MySQL не использует индекс с JOIN, WHERE и ORDER - PullRequest
7 голосов
/ 04 августа 2009

У нас есть две таблицы, напоминающие простую структуру записи тегов, как показано ниже (на самом деле она намного сложнее, но в этом суть проблемы):

tag (A.a) | recordId (A.b)
1         | 1
2         | 1
2         | 2
3         | 2
....

и

recordId (B.b) | recordData (B.c)
1              | 123
2              | 666
3              | 1246

Проблема в извлечении упорядоченных записей с определенным тегом. Очевидный способ сделать это с помощью простого объединения и индексов для (PK) (A.a, A.b), (A.b), (PK) (B.b), (B.b, B.c) как таковых:

select A.a, A.b, B.c from A join B on A.b = B.b where a = 44 order by c;

Однако, это дает неприятный результат сортировки файлов:

+----+-------------+-------+------+---------------+---------+---------+-----------+------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key     | key_len | ref       | rows | Extra                                        |
+----+-------------+-------+------+---------------+---------+---------+-----------+------+----------------------------------------------+
|  1 | SIMPLE      | A     | ref  | PRIMARY,b     | PRIMARY | 4       | const     |   94 | Using index; Using temporary; Using filesort | 
|  1 | SIMPLE      | B     | ref  | PRIMARY,b     | b       | 4       | booli.A.b |    1 | Using index                                  | 
+----+-------------+-------+------+---------------+---------+---------+-----------+------+----------------------------------------------+

Используя огромное и чрезвычайно избыточное «материализованное представление», мы можем получить довольно приличную производительность, но это за счет усложнения бизнес-логики, чего мы хотели бы избежать, тем более что таблицы A и B уже являются MV: s (и необходимы для других запросов, и влияют на те же запросы с помощью UNION).

create temporary table C engine=innodb as (select A.a, A.b, B.c from A join B on A.b = B.b);
explain select a, b, c from C where a = 44 order by c;

Еще более усложняет ситуацию тот факт, что у нас есть B-таблицы, такие как фильтры диапазона.

select A.a, A.b, B.c from A join B on A.b = B.b where a = 44 AND B.c > 678 order by c;

Но мы уверены, что справимся с этим, если проблема с сортировкой файлов исчезнет.

Кто-нибудь знает, почему простое объединение в кодовом блоке 3 выше не будет использовать индекс для сортировки, и если мы сможем каким-то образом обойти проблему без создания нового MV?

Ниже приведен полный список SQL, который мы используем для тестирования.

DROP TABLE IF EXISTS A;
DROP TABLE IF EXISTS B;
DROP TABLE IF EXISTS C;
CREATE TEMPORARY TABLE A (a INT NOT NULL, b INT NOT NULL, PRIMARY KEY(a, b), INDEX idx_A_b (b)) ENGINE=INNODB;
CREATE TEMPORARY TABLE B (b INT NOT NULL, c INT NOT NULL, d VARCHAR(5000) NOT NULL DEFAULT '', PRIMARY KEY(b), INDEX idx_B_c (c), INDEX idx_B_b (b, c)) ENGINE=INNODB;

DELIMITER $$
CREATE PROCEDURE prc_filler(cnt INT)
BEGIN
        DECLARE _cnt INT;
        SET _cnt = 1;
        WHILE _cnt <= cnt DO
                INSERT IGNORE INTO A SELECT RAND()*100, RAND()*10000;
                INSERT IGNORE INTO B SELECT RAND()*10000, RAND()*1000, '';
                SET _cnt = _cnt + 1;
        END WHILE;
END
$$
DELIMITER ;

START TRANSACTION;
CALL prc_filler(100000);
COMMIT;
DROP PROCEDURE prc_filler;

CREATE TEMPORARY TABLE C ENGINE=INNODB AS (SELECT A.a, A.b, B.c FROM A JOIN B ON A.b = B.b);
ALTER TABLE C ADD (PRIMARY KEY(a, b), INDEX idx_C_a_c (a, c));

EXPLAIN EXTENDED SELECT A.a, A.b, B.c FROM A JOIN B ON A.b = B.b WHERE A.a = 44;
EXPLAIN EXTENDED SELECT A.a, A.b, B.c FROM A JOIN B ON A.b = B.b WHERE 1 ORDER BY B.c;
EXPLAIN EXTENDED SELECT A.a, A.b, B.c FROM A JOIN B ON A.b = B.b where A.a = 44 ORDER BY B.c;
EXPLAIN EXTENDED SELECT a, b, c FROM C WHERE a = 44 ORDER BY c;
-- Added after Quassnois comments
EXPLAIN EXTENDED SELECT A.a, A.b, B.c FROM  B FORCE INDEX (idx_B_c) JOIN A ON A.b = B.b WHERE A.a = 44 ORDER BY B.c;
EXPLAIN EXTENDED SELECT A.a, A.b, B.c FROM A JOIN B ON A.b = B.b WHERE A.a = 44 ORDER BY B.c LIMIT 10;
EXPLAIN EXTENDED SELECT A.a, A.b, B.c FROM  B FORCE INDEX (idx_B_c) JOIN A ON A.b = B.b WHERE A.a = 44 ORDER BY B.c LIMIT 10;

Ответы [ 2 ]

9 голосов
/ 04 августа 2009

Когда я пытаюсь воспроизвести этот запрос, используя ваши сценарии:

SELECT  A.a, A.b, B.c
FROM    A
JOIN    B
ON      A.b = B.b
WHERE   a = 44
ORDER BY
        c

, завершается в 0.0043 seconds (мгновенно), возвращает 930 строк и дает план:

1, 'SIMPLE', 'A', 'ref', 'PRIMARY', 'PRIMARY', '4', 'const', 1610, 'Using index; Using temporary; Using filesort'
1, 'SIMPLE', 'B', 'eq_ref', 'PRIMARY', 'PRIMARY', '4', 'test.A.b', 1, ''

Это довольно эффективно для такого запроса.

Для такого запроса нельзя использовать один индекс как для фильтрации, так и для сортировки.

См. Эту статью в моем блоге для более подробных объяснений:

Если вы ожидаете, что ваш запрос будет возвращать несколько записей, вы должны использовать индекс для A для фильтрации, а затем сортировать с использованием файловой сортировки (как в приведенном выше запросе).

Если вы ожидаете, что он вернет много записей (и LIMIT их), вам нужно использовать индекс для сортировки, а затем отфильтровать:

CREATE INDEX ix_a_b ON a (b);
CREATE INDEX ix_b_c ON b (c)

SELECT  *
FROM    B FORCE INDEX (ix_b_c)
JOIN    A
ON      A.b = B.b
ORDER BY
        b.c
LIMIT 10;

1, 'SIMPLE', 'B', 'index', '', 'ix_b_c', '4', '', 2, 'Using index'
1, 'SIMPLE', 'A', 'ref', 'ix_a_b', 'ix_a_b', '4', 'test.B.b', 4, 'Using index'
1 голос
/ 04 августа 2009

select A.a, A.b, B.c from A join B on A.b = B.b where a = 44 order by c;

Если вы псевдоним столбцов, это поможет? Пример:

 SELECT 
 T1.a AS colA, 
 T2.b AS colB, 
 T2.c AS colC 
 FROM A AS T1 
 JOIN B AS T2 
 ON (T1.b = T2.b) 
 WHERE 
 T1.a = 44 
 ORDER BY colC;

Единственные изменения, которые я сделал:

  • Я поставил условия соединения в скобках
  • Условия объединения и условия, основанные на столбцах таблицы
  • Условие ORDER BY основано на результирующем столбце таблицы
  • Я связал столбцы таблицы результатов и запрашиваемые таблицы, чтобы (надеюсь) сделать их более понятными при использовании одного или другого (и более понятными для сервера. запрос).

Я знаю, что ваши реальные данные более сложны, но я предполагаю, что вы предоставили простую версию запроса, потому что проблема на этом простом уровне.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...