MySQL: создайте внешний ключ без индекса - PullRequest
0 голосов
/ 03 мая 2018

Возможно ли иметь внешний ключ без индекса в MySQL 5.6.34? Я хочу этого, потому что я создал обнуляемый столбец в 20M строках с внешним ключом для другой таблицы. Поскольку это новая функция, только новые строки МОГУТ заполнять этот столбец фактическим значением, и, как вы можете ожидать, мощность этого индекса становится ужасной . Поэтому в большинстве случаев использование этого индекса на самом деле плохая идея. Проблема: у меня есть тонны запросов, которые разделяют это ограничение:

[...] from large_table where tenant_id = ? and nullable_foreign_key_with_index is null and [...]

Вопрос? MySQL считает, что для разрешения запросов рекомендуется использовать стратегию index_merge / intersect . В этом случае MySQL будет выполнять 2 запроса параллельно: один с tenant_id (который использует действительный и хороший индекс), а другой с nullable_foreign_key_with_index, что плохо, почти «полное сканирование таблицы параллельно», учитывая, что мощность этот индекс <1000 </strong> в таблице с > 20M строками. Подробнее об этой «проблеме» можно узнать здесь

Итак, каковы возможные решения? Учитывая, что MySQL «заставляет» внешний ключ иметь присоединенный индекс:

  1. Удалите внешний ключ и индекс. Это плохо, потому что в случае ошибки в приложении мы можем нарушить ссылочную целостность.

  2. FOREIGN_KEY_CHECKS = 0; Падение индекса; FOREIGN_KEY_CHECKS = 1. Это плохо, потому что даже если внешний ключ все еще существует, MySQL больше не проверяет столбец, чтобы проверить, существует ли значение на самом деле. Это ошибка?

  3. Используйте подсказки запросов во всех существующих запросах, чтобы убедиться, что мы используем только старый и эффективный "tenant_id_index" . Это плохо, потому что мне приходится выслеживать все существующие запросы, а также не забывать использовать его снова при создании новостных запросов.

Итак, как я могу сказать: "MySQL, не беспокойтесь о создании индекса для этого внешнего ключа, но продолжайте проверять его содержимое в связанной таблице, которая в любом случае индексируется первичным ключом" . Я что-то пропустил? Лучшая идея на данный момент - удалить внешний ключ и просто поверить, что приложение работает, как и ожидалось, что, вероятно, так и есть, но это приведет к классическому обсуждению наличия ограничений в APP и DATABASE. Есть идеи?

Ответы [ 3 ]

0 голосов
/ 03 мая 2018

В: Как я могу сказать: "MySQL, не беспокойтесь о создании индекса для этого внешнего ключа, но продолжайте проверять его содержимое в связанной таблице, которая в любом случае индексируется первичным ключом"

A: Нет, не могу. InnoDB требует подходящего индекса для поддержки применения ограничения внешнего ключа.

Рассмотрим обратную сторону ... если мы собираемся УДАЛИТЬ строку в родительской таблице, тогда InnoDB необходимо проверить ограничение внешнего ключа.

Это означает, что InnoDB необходимо проверить содержимое дочерней таблицы, чтобы найти строки, имеющие определенное значение в столбце внешнего ключа. По существу эквивалентно

SELECT ... FROM child_table c WHERE c.foreign_key_col = ? 

И для этого InnoDB требует наличия индекса для child_table, в котором foreign_key_col является ведущим столбцом.

Опции, предложенные в вопросе (отключение или удаление внешнего ключа), будут работать, потому что тогда InnoDB не будет применять внешний ключ. Но, как отмечено в вопросе, это означает, что внешний ключ не применяется. Который побеждает цель внешнего ключа. Код приложения может быть ответственным за обеспечение ссылочной целостности, или мы могли бы написать несколько триггеров ug-gghhh-ly (нет, мы не хотим туда идти).


Как уже отметил Гордон в своем (как обычно, отличном) ответе ... проблема не в том, чтобы удалить индекс в столбце внешнего ключа. фактическая проблема - неэффективный план выполнения. И наиболее вероятное решение этой проблемы - убедиться, что доступен более подходящий индекс.

Составные индексы - это путь. Индекс как это:

... ON child_table (foreign_key_col,tenant_id,...)

будет соответствовать требованию внешнего ключа, индекса со столбцом внешнего ключа в качестве ведущего столбца. И удалите (теперь избыточный) индекс только на singleton foreign_key_col.

Этот индекс также можно использовать для удовлетворения запроса, использующего ужасный план доступа к слиянию индексов. (Проверьте с помощью EXPLAIN.)

Также рассмотрите возможность добавления столбцов (например, foreign_key_col) к индексу, в котором tenant_id является ведущим столбцом

... ON child_table (tenant_id,...,foreign_key_col,...)

и сбросьте избыточный индекс в столбце синглтона tenant_id.

0 голосов
/ 18 мая 2018

Резюме: почти всегда лучше иметь составной индекс, а не зависеть от "пересечения слияния индекса".

Если оба столбца проверены с = (или IS NULL), не имеет значения, в каком порядке находятся столбцы в определении индекса. То есть кардинальность не имеет значения.

0 голосов
/ 03 мая 2018

Для этого запроса:

from large_table
where tenant_id = ? and
      nullable_foreign_key_with_index is null and [...]

Просто добавьте индекс large_table(tenant_id, nullable_foreign_key_with_index).

MySQL должен использовать этот индекс для таблицы.

Я почти уверен, что вы можете сделать это в обратном направлении (я был бы на 100% уверен, если бы сравнение было с чем-то иным, чем NULL, но я почти уверен, что MySQL правильно делает и с NULL. )

large_table(nullable_foreign_key_with_index, tenant_id)

И MySQL распознает, что этот индекс работает для внешнего ключа, а не создает какой-либо другой индекс.

...