Как избежать полного сканирования таблицы с помощью этого основного внутреннего соединения? - PullRequest
6 голосов
/ 23 декабря 2009

У меня есть таблица с внешним ключом для таблицы, в которой хранятся некоторые данные BLOB-объектов. Когда я выполняю внутреннее объединение для таблиц с условием для главной таблицы, тип соединения изменяется от 'index' до 'ALL'. Я хотел бы избежать этого, так как моя таблица BLOB-объектов имеет порядок десятков гигабайт. Как я могу избежать этого?

Вот основное внутреннее соединение:

EXPLAIN SELECT m.id, b.id, b.data 
        FROM metadata m, blobstore b 
        WHERE m.fkBlob = b.id;

1, 'SIMPLE', 'm', 'index', 'fk_blob', 'fk_blob', '4', '', 1, 'Using index'
1, 'SIMPLE', 'b', 'eq_ref', 'PRIMARY', 'PRIMARY', '4', 'blob_index.m.fkBlob', 1, ''

Здесь я добавляю условие на главную таблицу:

EXPLAIN SELECT m.id, b.id, b.data 
        FROM metadata m, blobstore b 
        WHERE m.fkBlob = b.id AND m.start < '2009-01-01';
1, 'SIMPLE', 'b', 'ALL', 'PRIMARY', '', '', '', 1, ''
1, 'SIMPLE', 'm', 'ref', 'fk_blob,index_start', 'fk_blob', '4', 'blob_index.b.id', 1, 'Using where'

Обратите внимание, что порядок, в котором перечислены таблицы, изменился. Сейчас выполняется полное сканирование таблицы BLOB-таблиц из-за условия, которое я добавил для основной таблицы.

Вот схема:

 DROP TABLE IF EXISTS `blob_index`.`metadata`;
    CREATE TABLE  `blob_index`.`metadata` (
      `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
      `fkBlob` int(10) unsigned NOT NULL,
      `start` datetime NOT NULL,
      PRIMARY KEY (`id`),
      KEY `fk_blob` (`fkBlob`),
      KEY `index_start` (`start`),
      CONSTRAINT `fk_blob` FOREIGN KEY (`fkBlob`) REFERENCES `blobstore` (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1;


    DROP TABLE IF EXISTS `blob_index`.`blobstore`;
    CREATE TABLE  `blob_index`.`blobstore` (
      `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
      `data` mediumblob NOT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Ответы [ 5 ]

3 голосов
/ 23 декабря 2009

Оптимизатор считает, что ваш запрос выиграет от смены порядка таблиц (что, скорее всего, означает, что статистика не актуальна).

Вы можете попробовать добавить индекс на metadata (start, fkBlob):

CREATE INDEX ix_metadata_start_blob ON metadata (start, fkBlob)

и запустите ANALYZE TABLE для обеих таблиц.

Таким образом, индекс на start будет использоваться для фильтрации на metadata, который станет ведущим.

Вы также можете явно указать порядок объединения:

SELECT  *
FROM    metadata m
STRAIGHT_JOIN
        blobstore b
ON      b.id = m.fkBlob
WHERE   m.start <= '2009-01-01'

, хотя обычно это не рекомендуется.

3 голосов
/ 23 декабря 2009

Полагаю, вы пытаетесь это сделать с пустой таблицей (поскольку MySQL считает, что для полного сканирования таблицы необходимо пройти одну строку), что может повлиять на результаты планировщика. Когда вы сделаете это на реальном столе, результаты EXPLAIN могут отличаться (и на самом деле они отличались в моем тесте).

0 голосов
/ 29 декабря 2009

В первом примере MySQL использовал индекс метаданных fk_blob, потому что это был индекс покрытия - каждый столбец, который вы использовали в запросе, присутствовал в индексе. (Это то, что означает «использование индекса».) Этот запрос все еще выполнял полное сканирование, но сканировал каждую строку через вторичный индекс, а не первичный. Как только вы использовали start, вы потеряли индекс покрытия, и MySQL рассчитал, что быстрее использовать blobstore в качестве движущего индекса. (Основной индекс InnoDB интегрирован с хранилищем строк.)

Если вы хотите, чтобы MySQL продолжал использовать индекс метаданных в качестве движущего индекса, убедитесь, что в нем есть один индекс, который будет полезен для запроса. Индекс on (start, fkBlob) лучше всего подходит для второго запроса, но это может оказаться бесполезным для других запросов. Следующий лучший индекс - заменить (fkBlob) на (fkBlob, start). Вам придется сбалансировать наличие слишком большого количества индексов (которые дорого обходятся) по сравнению с эффективными планами запросов. Тестируйте, тестируйте, тестируйте - и никогда не верьте слепо объяснениям из вашей базы данных разработчиков.

0 голосов
/ 29 декабря 2009
if the index doesnot take it right use HINTS

select /* INDEX <index_name> */
blah blah blah
from ........
0 голосов
/ 23 декабря 2009

Если я читаю то, что вы написали правильно, оно меняется от index до ref и eq_ref до all.

CREATE INDEX idx_metadata USING BTREE ON `metadata` (fkBlob,start);

Должен забрать его обратно.

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