2016-07-14 1 views
2

저는 InnoDB 테이블에서 고객 간의 거리를 계산하는 알고리즘의 결과를 작성했습니다. 예를 들어 내 고객 A는, B, C와 D는, 데이터베이스의 테이블이 다른 컬럼 사이에, 다음과 같습니다 인 경우 :이 경우 MyISAM은 mysql의 InnoDB보다 훨씬 빠릅니다.

From | To | Distance 
    A  B  344 
    A  C  274 
    A  D  182 
    B  C  338 

등등 ...이 행의 많은 내가 생각입니다 5 천만 명에 달했다.

다른 열은 product_type 및 value입니다. 고객 B (customer_to 열)가 product_type을 얼마나 구매했는지 알 수 있습니다. 즉, 고객 B가 구매하는 product_type의 수에 따라 각 쌍이 여러 번 있음을 의미합니다.

각 고객을 이웃 사람들이 구매하는 제품과 값으로 그룹화하는 쿼리가 필요했습니다. 쿼리는 다음과 같습니다.

select customer_from, product_type, avg(value) as opportunity 
from customer_distances 
where distance < 500 
group by customer_from, product_type 
order by opportunity desc; 

innodb 테이블에서 해당 쿼리에 응답 할 수 없습니다. net_read_timeout을 28800로 변경했지만 쿼리 중에 mysql 연결이 끊어졌습니다.

거친 쿼리는 트랜잭션 처리를 위해 innodb 빌드와 관련이 있습니다. 그래서 엔진으로 MyIsam으로 새로운 테이블을 만들고 innodb 테이블의 모든 레코드를 insert-select했습니다.

예상대로 셀렉트는 매우 빠르며 (70 세그), 다른 모든 셀렉트는 count (distinct customer_from)와 같이 거의 즉각적으로 선택됩니다.

그냥 호기심 때문에 myisam 테이블에 거리를 삽입하는 과정을 계속했습니다. 프로그램이 INSERT를 위해 innodb 테이블에서 작업 할 때보 다 적어도 100 배 이상 빠르게 실행되기 시작했을 때 놀랐습니다!

각 고객에 대해 프로그램은 3000 행 (각 product_type의 각 이웃에 대해 하나씩, 고객 당 300 개의 이웃 및 10 개의 product_types와 같은)을 삽입합니다. 단일 테이블을 삽입하는 innodb 테이블을 사용하면 40 ~ 60 초 (aprox. 3000 행)의 데이터가 필요합니다. myisam 테이블을 사용하면 3 고객 (9000 행 aprox)을 삽입하는 데 1 초가 걸립니다.

일부 추가 정보 :

  • MySQL 데이터베이스 내 PC (로컬 호스트)입니다.
  • 프로그램이 java로 작성되었으며 내 PC에서 실행 중입니다.
  • 준비된 문을 사용하고 있으며 행과 다음 문자 사이의 데이터 만 변경합니다. 이이 질문은 이렇게 요약이 질문에 Why is myisam storage engine is faster than Innodb storage engine

관련이있다 : 왜의 MyISAM은 그렇게 빨리 삽입 문에? 당신은 어떻게 생각하십니까?

편집 1 : innodb와 myisam 두 테이블 모두에 create 문을 추가합니다. 편집 2 : 사용하기 어려운 정보를 삭제하고 여기 저기 조금씩 형식을 지정했습니다.

/* INNODB TABLE */ 
CREATE TABLE `customer_distances` (
    `customer_from` varchar(50) NOT NULL, 
    `customer_from_type` varchar(50) DEFAULT NULL, 
    `customer_from_segment` varchar(50) DEFAULT NULL, 
    `customer_from_district` int(11) DEFAULT NULL, 
    `customer_from_zone` int(11) DEFAULT NULL, 
    `customer_from_longitud` decimal(15,6) DEFAULT NULL, 
    `customer_from_latitud` decimal(15,6) DEFAULT NULL, 
    `customer_to` varchar(50) NOT NULL, 
    `customer_to_type` varchar(50) DEFAULT NULL, 
    `customer_to_segment` varchar(50) DEFAULT NULL, 
    `customer_to_district` int(11) DEFAULT NULL, 
    `customer_to_zone` int(11) DEFAULT NULL, 
    `customer_to_longitud` decimal(15,6) DEFAULT NULL, 
    `customer_to_latitud` decimal(15,6) DEFAULT NULL, 
    `distance` decimal(10,2) DEFAULT NULL, 
    `product_business_line` varchar(50) DEFAULT NULL, 
    `product_type` varchar(50) NOT NULL, 
    `customer_from_liters` decimal(10,2) DEFAULT NULL, 
    `customer_from_dollars` decimal(10,2) DEFAULT NULL, 
    `customer_from_units` decimal(10,2) DEFAULT NULL, 
    `customer_to_liters` decimal(10,2) DEFAULT NULL, 
    `customer_to_dollars` decimal(10,2) DEFAULT NULL, 
    `customer_to_units` decimal(10,2) DEFAULT NULL, 
    `liters_opportunity` decimal(10,2) DEFAULT NULL, 
    `dollars_opportunity` decimal(10,2) DEFAULT NULL, 
    `units_oportunity` decimal(10,2) DEFAULT NULL, 
    PRIMARY KEY (`cliente_desde`,`cliente_hasta`,`grupo`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

/* MYISAM TABLE */ 
CREATE TABLE `customer_distances` (
    `customer_from` varchar(50) NOT NULL, 
    `customer_from_type` varchar(50) DEFAULT NULL, 
    `customer_from_segment` varchar(50) DEFAULT NULL, 
    `customer_from_district` int(11) DEFAULT NULL, 
    `customer_from_zone` int(11) DEFAULT NULL, 
    `customer_from_longitud` decimal(15,6) DEFAULT NULL, 
    `customer_from_latitud` decimal(15,6) DEFAULT NULL, 
    `customer_to` varchar(50) NOT NULL, 
    `customer_to_type` varchar(50) DEFAULT NULL, 
    `customer_to_segment` varchar(50) DEFAULT NULL, 
    `customer_to_district` int(11) DEFAULT NULL, 
    `customer_to_zone` int(11) DEFAULT NULL, 
    `customer_to_longitud` decimal(15,6) DEFAULT NULL, 
    `customer_to_latitud` decimal(15,6) DEFAULT NULL, 
    `distance` decimal(10,2) DEFAULT NULL, 
    `product_business_line` varchar(50) DEFAULT NULL, 
    `product_type` varchar(50) NOT NULL, 
    `customer_from_liters` decimal(10,2) DEFAULT NULL, 
    `customer_from_dollars` decimal(10,2) DEFAULT NULL, 
    `customer_from_units` decimal(10,2) DEFAULT NULL, 
    `customer_to_liters` decimal(10,2) DEFAULT NULL, 
    `customer_to_dollars` decimal(10,2) DEFAULT NULL, 
    `customer_to_units` decimal(10,2) DEFAULT NULL, 
    `liters_opportunity` decimal(10,2) DEFAULT NULL, 
    `dollars_opportunity` decimal(10,2) DEFAULT NULL, 
    `units_oportunity` decimal(10,2) DEFAULT NULL, 
    PRIMARY KEY (`cliente_desde`,`cliente_hasta`,`grupo`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8; 
+0

테이블이 다른 경우 (인덱스 등 ...)? 그리고 성능에 영향을 줄 수있는 엔진에 대한 별도의 서버 설정 (예 : 메모리 캐시 크기)이 있습니다. – Uueerdo

+0

제품 판매 데이터를 표준화해야합니다. 테이블은 현재 설계된 것처럼별로 의미가 없습니다. – EJP

+0

동일한 테이블, 동일한 서버. –

답변

3

삽입

    InnoDB하지만, 기본적으로 "커밋"즉시 각 INSERT
  • . 한 번에 100-1000 행 뭉침으로이를 해결할 수 있습니다.
  • 인서트를 일괄 처리하면 MyISAM과 InnoDB 모두 속도가 빨라진다.
  • autocommitBEGIN..COMMIT에 대해 자세히 알아보십시오.

은 선택

  • InnoDB는의 MyISAM보다 더 많은 디스크 공간을 사용 - 일반적으로 2 배 - 3 배; 이는 아마도 테이블 스캔에 영향을 미친다. 아마
  • 이 쿼리에 대해 (customer_from, product_type, distance)에 대한 복합 인덱스는 두 엔진에 모두 도움이 될 것이다.

튜닝

  • 단지의 MyISAM을 실행, RAM 및 innodb_buffer_pool_size=0의 20 % key_buffer_size을 설정합니다.
  • 단지 이노을 실행 만 10M의 RAM innodb_buffer_pool_size 70 %로 key_buffer_size을 설정합니다.

정규화 및 저장 공간

  • 작은 -> 더 캐시 -> 적은 I/O ->
  • DECIMAL(10,2) 가장 최고의 아니다 (엔진 중 하나에서) 빨리 사례. 돈이 없다면 FLOAT을 고려하십시오 (예 : distance). 적은 수의 숫자를 고려하십시오. 최대 99,999,999.99를 처리하며 5 바이트를 사용합니다.
  • customer_fromcustomer_to의 10 개 열과 같이 복제 된 열을 갖는 것이 일반적으로 좋지 않습니다. 양쪽에 Customers 테이블이 있어야합니다.
  • 위도와 경도는 각각 7 바이트이며 불필요한 해결 방법이 있습니다. latidud DECIMAL(6,4)longitud (7,4)을 제안하십시오. 이며 7 바이트입니다. (다음은 16m/52피트 해상도를 제공합니다.) 그 제안 후

    에서, 50M 행 테이블이 매우 작은, 그리고 매우 빠르게 모두 엔진에서 실행됩니다

결과. 그런 다음 비교를 다시 실행하십시오.

+0

답변 해 주셔서 감사합니다. 변경 사항을 수행하고 결과를 문서화합니다. –

+0

@DagoBorda - 아직 결과가 있습니까? –

관련 문제