2016-07-21 6 views
-1

나는 다음과 같은 자체 조인 쿼리를 가지고 :MySQL을 - 자동 가입하기 - 전체 테이블 스캔 (인덱스를 스캔 할 수 없습니다)

SELECT A.id 
FROM mytbl  AS A 
LEFT JOIN mytbl AS B 
ON (A.lft BETWEEN B.lft AND B.rgt) 

쿼리는 매우 느리다 및 실행 계획보고 후 원인으로 나타납니다 JOIN에서 전체 테이블을 검사합니다. 이 테이블은 단지 500 개의 행을 가지고 있으며 이것이 이것이 최적화 도구의 선택과 다른 점이 있는지를보기 위해 100,000 개의 행으로 증가 시켰습니다. 100KB 행으로 전체 테이블 스캔을 계속하고있었습니다.

내 다음 단계는 다음과 같은 쿼리를 시도하고 힘 인덱스했다,하지만 같은 상황은 전체 테이블 스캔을 발생 :

SELECT A.id 
FROM categories_nested_set  AS A 
LEFT JOIN categories_nested_set AS B 
FORCE INDEX (idx_lft, idx_rgt) 
ON (A.lft BETWEEN B.lft AND B.rgt) 

Execution plan for full table scan query :/

모든 열 (ID, LFT, RGT) 정수이고, 모두 색인이 생성됩니다.

왜 MySql이 전체 ​​테이블 스캔을 수행합니까?

전체 테이블 검색 대신 색인을 사용하도록 쿼리를 변경하려면 어떻게해야합니까?

CREATE TABLE mytbl (lft int(11) NOT NULL DEFAULT '0', 
rgt int(11) DEFAULT NULL, 
id int(11) DEFAULT NULL, 
category varchar(128) DEFAULT NULL, 
    PRIMARY KEY (lft), 
    UNIQUE KEY id (id), 
    UNIQUE KEY rgt (rgt), 
    KEY idx_lft (lft), 
    KEY idx_rgt (rgt)) ENGINE=InnoDB DEFAULT CHARSET=utf8 

덕분에

+0

주 쇼'의 결과가 각 관련 XYZ 아래 – Drew

+0

결과 테이블 xyz'를 만들 : 쿼리의 고정 및 고정되지 않은 버전 사이에 기능을 EXPLAIN의 비교 '표 mytbl을 만듭니다 ( LFT의 INT (11) NOT NULL DEFAULT '0' RGT의 INT (11) 초기 NULL, 아이디 INT (11) 초기 NULL, 카테고리 VARCHAR (128) 초기 NULL, PRIMARY KEY (LFT) UNIQUE KEY ID (id), UNIQUE KEY rgt (rgt), KEY idx_l KEY idx_rgt (rgt) ) ENGINE = InnoDB DEFAULT CHARSET = utf8' – mils

+0

'PRIMARY KEY'는'UNIQUE' 키가'KEY'입니다. 따라서 두 개의 KEY는 중복되어 제거되어야합니다. –

답변

-1

다음 SO 질문 인접리스트 및 인덱스의 조합에 대한 약간의 정보가있는 한,이 솔루션에 중요하다 : 기본적인 비교 조건을 추가하는 것은의 사용을 유발한다는

MySQL & nested set: slow JOIN (not using index)

그것은 나타납니다

가 다음과 같은 색인 :

SELECT A.id 
FROM mytbl  AS A 
LEFT JOIN mytbl AS B ON (A.lft BETWEEN B.lft AND B.rgt) 
-- THE FOLLOWING DUMMY CONDITIONS TRIGGER INDEX 
WHERE A.lft > 0 
AND B.lft > 0 
AND B.rgt > 0 

그리고 더 이상 테이블 스캔을하지 마십시오.

편집 : EXPLAIN function results, top is fixed, bottom is not

+0

'수정'이 포함되거나 포함되지 않은 상태에서 다음을 테스트하십시오. 'FLUSH STATUS; 고르다 ...; SHOW SESSION STATUS LIKE 'Handler %'; '숫자가 같으면'전체 검사 '가되지만 테이블 대신 색인에 표시됩니다. –

+0

감사 릭 아래 번호 (영 숫자 제외) FIX WITH 'Handler_commit', '1' 'Handler_external_lock', '4' 'Handler_read_first', '2'는 'Handler_read_key', '2' 'Handler_read_next', '646'FIX 'Handler_commit'WITHOUT , '1' 'Handler_external_lock', '4' 'Handler_read_first', '72' 'Handler_read_key', '72' 'Handler_read_rnd_next', ' 37941 ' – mils

+0

그게 그 수정이 도움이되었다고 확신합니다. –

2

당신은 많은의 인덱스, 그들 중 일부는 중복이있다. 그들 중 일부를 정리하자. 인덱스가 너무 많으면 삽입 및 업데이트 속도가 느려집니다. 이미 LFT에 정의 된 기본 키를 가지고 있기 때문에

PRIMARY KEY (lft), 
KEY idx_lft (lft), 

, 그래서 무엇을 적 LFT의 다른 인덱스에 대한 필요가 없습니다. 마찬가지로 rgt의 고유 색인과 마찬가지로 아래 나열된 두 번째 색인은 필요하지 않습니다.

UNIQUE KEY rgt (rgt), 
KEY idx_rgt (rgt) 

이제 검색어를 살펴 보겠습니다.

SELECT A.id 
FROM mytbl  AS A 
LEFT JOIN mytbl AS B 
ON (A.lft BETWEEN B.lft AND B.rgt) 

야생에서 발생하는 쿼리는 거의 발생하지 않습니다. 500 개의 행을 사용하면이 쿼리가 5000 개의 행을 생성 할 수 있습니까? 한 번에 생성 된 전체 키가 정말로 필요합니까? 이 쿼리가 느린 이유는 mysql이 상수에 대해서만 optimize range comparisions 일 수 있기 때문입니다. 실제로 쿼리가 다음과 같이 표시 될 가능성이 큽니다.

SELECT B.* 
FROM mytbl  AS A 
LEFT JOIN mytbl AS B 
ON (A.lft BETWEEN B.lft AND B.rgt) 
WHERE a.id = N; 

여기서 특정 ID에 대한 노드를 만듭니다. 이것은 색인을 사용하고 정말 빨라질 것입니다. 쿼리를 최적화 할 때 중요한 점은 무엇이든 많이 사용하지 않을 것입니다.

+0

응답 해 주셔서 감사 드리며, 몇 가지 추가 정보로 내 질문을 업데이트했습니다. 기본적으로 더 큰 JOIN의 일부이므로 WHERE 절을 사용하여 수행 할 수 없습니다. 나는이 질문을 단순화하기 위해 철저히 분석했다. 그리고 JOIN을 사용한 실제 유스 케이스에서는 인덱스를 사용하지 않습니다. JOIN이 더 큰 시나리오에서 범위 비교를 위해 상수가 상수입니까? 아니면 사용자 정의 상수 여야합니까? 이 시나리오에서 테이블 스캔을 피하는 방법은 무엇입니까? 감사합니다. – mils

+0

목표 지점을 옮기고 장거리로 이동합니다. – e4c5

+0

성능 테스트를 한 결과,이 mytbl의 크기가 내 시스템에 데이터를로드하는 데 가장 큰 영향을줍니다. 500 대신 10k 행을 사용하면 성능이 6000 % 저하됩니다. 지금은 4 시간이 걸리며 점점 악화 될 수 있습니다. 그래서 MySql이 범위 쿼리를위한 인덱스를 사용하는 방법을 아는 데 정말 감사 할 것입니다. – mils