2014-11-03 3 views
2

У меня есть база данных MySql с 4 таблицами (намного больше, чем на самом деле, но только эти 4 имеют отношение к вопросу), позволяет называть их A, B, C и D. Вот схема :wierd MySql join behavior

CREATE TABLE A 
(
    pKey INT NOT NULL AUTO_INCREMENT, 
    name NVARCHAR(50), 
    PRIMARY KEY(pKey), 
    UNIQUE INDEX(name) 
); 

CREATE TABLE C 
(
    pKey INT NOT NULL AUTO_INCREMENT, 
    PRIMARY KEY(pKey) 
); 

CREATE TABLE B 
(
    pKey INT NOT NULL AUTO_INCREMENT, 
    aKey INT NOT NULL, 
    cKey INT NOT NULL, 
    PRIMARY KEY(pKey), 
    UNIQUE INDEX UniqueKey (aKey, cKey), 
    FOREIGN KEY(aKey) REFERENCES A(pKey), 
    FOREIGN KEY(cKey) REFERENCES C(pKey) 
); 

CREATE TABLE D 
(
    pKey INT NOT NULL AUTO_INCREMENT, 
    cKey INT NOT NULL, 
    PRIMARY KEY(pKey), 
    INDEX(cKey), 
    FOREIGN KEY(cKey) REFERENCES C(pKey) 
); 

Я бегу следующий запрос:

SELECT 
    --stuff... 
FROM A 
INNER JOIN B 
    ON A.pKey=B.aKey 
INNER JOIN C 
    ON B.cKey=C.pKey 
INNER JOIN D 
    ON D.cKey=C.pKey 
WHERE 
    A.name=parameter_1; 

беда в том, что это большая база данных работает на одном сервере, и большинство таблиц имеют 100К + записи, и его не редкость для таблицу, чтобы сломать 10 миллионов записей. Одна таблица имеет более 200 миллионов записей.

Оставив в стороне любые проблемы с MySql и архитектурой (я застрял с обоими), я получаю некоторое нечетное поведение с вышеуказанным запросом, когда я использую объяснение по этому запросу. Из-за этого поведения у меня есть несколько вопросов. Сначала я покажу странное поведение.

Если я просто ОБЪЯВЛЯЮТ указанный выше запрос в MySql, то получаю ссылки, которые я ожидал бы в столбце ref вывода EXPLAIN. Тем не менее, мне нужно запустить этот запрос в качестве подзапроса большего запроса. EXPLAINning большего запроса дает мне что-то подобное для вышеупомянутого запроса (это просто строки большего запроса, которые соответствуют таблицам в запросе):

+----+-------------+-------+-------+---------------------+---------+---------+-----------------+-------+--------------------------+ 
| id | select_type | table | type | possible_keys  | key  | key_len | ref    | rows | Extra     | 
+----+-------------+-------+-------+---------------------+---------+---------+-----------------+-------+--------------------------+ 
| 1 | SIMPLE  | A  | const | PRIMARY,key1,key2 | key1 | 38  |     |  1 |       | 
| 1 | SIMPLE  | D  | index | key3    | key3 | 12  | NULL   | 73868 | Using index    | 
| 1 | SIMPLE  | C  | index | PRIMARY,key3  | PRIMARY | 8  | NULL   |  1 |       | 
| 1 | SIMPLE  | B  | ref | key4,UniqueKey,key6 | key4 | 12  | const,DB.D.key3 |  1 | Using where; Using index | 
+----+-------------+-------+-------+---------------------+---------+---------+-----------------+-------+--------------------------+ 

MySql делает два индекс сканирования, и один исй тип join. Я могу немного улучшить это, если использовать указательные подсказки, но только немного. Ранее я сказал, что этот запрос выполняется как подзапрос. Вот формат этого другого запроса:

SELECT 
    --stuff 
FROM 
(
    --sub-query1 
) a 
INNER JOIN 
(
    --the query I have a question about 
) b 
ON a.c1=b.c2 

Оптимизатор полностью игнорирует таблицу C в пользу делать присоединиться на двух иностранных ключевых столбцов, B.cKey = D.cKey. Итак, вот вопрос 1) Почему оптимизатор игнорирует таблицу C, как это?

Далее, даже если я использую подсказки индекса, и он игнорирует таблицу C, он по-прежнему выполняет сканирование индекса, чтобы присоединиться к B и D, несмотря на соответствующие индексы. Почему?

В приведенном выше объяснении это показывает, что в таблице D. имеется 73 868 строк. На данный момент существует 73 568 строк. Одна из других таблиц, запрашиваемых (не показана в этом вопросе), содержит около 100 миллионов строк, поэтому оптимизация этого довольно важна. Для полного запроса результат столбца строк равен 2.37E42. И да, я уже рассмотрел способы уменьшить количество таблиц в запросе; информация, которую мне нужно получить, требует, чтобы каждая таблица, к которой я обращался, и я не могу изменить архитектуру базы данных.

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

Спасибо!

EDIT: Я исправил форматирование для супер запроса.

ответ

0

Вы можете добавить индекс в эту схему?

Если это так, я предлагаю вам добавить составной индекс (name, pKey) в таблицу A. Не утруждайте себя уникальным ограничением; вы уже справлялись с другими вашими индексами.Это соединение позволит вашему критерию выбора A.name=parameter_1, и ваше соединение будет удовлетворено одним сканированием индекса.

Ваше использование таблицы с одним столбцом C - это просто исключить строки результатов, которые не находятся в этой таблице. Я бы не стал беспокоиться о том, что он пропал без вести с EXPLAIN, если у вас нет проблем с производительностью с вашим запросом.

В общем, при работе с этими многопользовательскими соединениями вы должны попытаться использовать составные индексы покрытия, чтобы помочь вашей работе. Вы можете прочитать об этом индексе.

+0

Я добавил указатель, который вы предложили, и не было никакой разницы. В таблице C фактически больше столбцов, чем один первичный ключ, и я выбираю значения из некоторых из этих столбцов. Поскольку я не думал, что эти дополнительные столбцы имеют отношение к операции JOIN, я не включил их. – wizard07KSU