MySQL: столбцы с низкой кардинальностью / селективностью = как индексировать? - PullRequest
39 голосов
/ 05 марта 2010

Мне нужно добавить индексы в мою таблицу (столбцы) и наткнуться на этот пост:

Сколько индексов базы данных слишком много?

Цитата: Сказав это, вы можете явно добавить в таблицу множество бессмысленных индексов, которые ничего не сделают. Добавление индексов B-Tree в столбец с двумя различными значениями будет бессмысленным, поскольку оно ничего не добавляет в плане поиска данных. Чем более уникальны значения в столбце, тем больше он будет использовать индекс. ”

Является ли индекс действительно бессмысленным, если есть только два различных значения? Для следующей таблицы (база данных MySQL, InnoDB)

Id (BIGINT)
fullname (VARCHAR)
address (VARCHAR)
status (VARCHAR)

Дополнительные условия:

  • База данных содержит 300 миллионов записей
  • Состояние может быть только «включено» и «отключено»
  • 150 миллионов записей имеют статус = включено, а 150 миллионов записей stauts = отключено

Насколько я понимаю, без индекса статуса выбор с where status=’enabled’ приведет к полному сканированию таблицы с 300 миллионами записей для обработки?

Насколько эффективен поиск, когда я использую индекс BTREE для статуса?

Должен ли я индексировать этот столбец или нет?

Какие альтернативы (возможно, любые другие индексы) предоставляет MySQL InnoDB для эффективного поиска записей с помощью предложения "where status =" enabled "в данном примере с очень низкой кардинальностью / селективностью значений?

Ответы [ 7 ]

37 голосов
/ 05 марта 2010

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

Причина этого связана с тем, как база данных обращается к таблице. Таблицы могут быть оценены либо полным сканированием таблицы, где каждый блок читается и обрабатывается по очереди. Или путем поиска rowid или ключа, где база данных имеет ключ / rowid и читает именно ту строку, которая ей требуется.

В случае, когда вы используете предложение where на основе первичного ключа или другого уникального индекса, например. where id = 1, база данных может использовать индекс, чтобы получить точную ссылку на место хранения данных строки. Это явно более эффективно, чем полное сканирование таблицы и обработка каждого блока.

Теперь вернемся к вашему примеру, у вас есть предложение where where status = 'enabled', индекс вернет 150 м строк, и базе данных придется читать каждую строку по очереди, используя отдельные небольшие операции чтения. Принимая во внимание, что доступ к таблице с полным сканированием таблицы позволяет базе данных использовать более эффективные большие чтения.

Есть момент, когда лучше всего выполнить полное сканирование таблицы, а не использовать индекс. С mysql вы можете использовать FORCE INDEX (idx_name) как часть вашего запроса, чтобы позволить сравнение между каждым методом доступа к таблице.

Справка: http://dev.mysql.com/doc/refman/5.5/en/how-to-avoid-table-scan.html

11 голосов
/ 05 марта 2010

Извините, что не согласен с Майком. Добавление индекса предназначено для ограничения количества полных поисков записей для MySQL, тем самым ограничивая количество операций ввода-вывода, которое обычно является узким местом.

Эта индексация не является бесплатной; вы платите за него при вставках / обновлениях, когда индекс должен обновляться, и за сам поиск, поскольку теперь ему нужно загрузить индексный файл (полнотекстовый индекс для 300M записей, вероятно, отсутствует в памяти). Так что вполне возможно, что вы получите дополнительные IO вместо того, чтобы ограничить его.

Я согласен с утверждением, что двоичная переменная лучше всего хранится как единица, bool или tinyint, так как это уменьшает длину строки и тем самым может ограничить дисковый ввод-вывод, а также сравнение чисел быстрее.

Если вам нужна скорость и вы редко используете отключенные записи, вы можете захотеть иметь 2 таблицы: одну для включенных и одну для отключенных записей и перемещать записи при изменении состояния. Поскольку это увеличивает сложность и риск, это будет мой последний выбор, конечно. Обязательно сделайте переход в 1 транзакцию, если вам довелось сделать это.

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

6 голосов
/ 26 июля 2012

Если данные распределены как 50:50, тогда запрос, например, status="enabled", позволит избежать половины сканирования таблицы.

Наличие индекса для таких таблиц полностью зависит от распределения данных, т. Е. Если записи с включенным статусом составляют 90%, а другие - 10%. и для запроса, где status="disabled" сканирует только 10% таблицы.

поэтому индекс для таких столбцов зависит от распределения данных.

4 голосов
/ 05 марта 2010

Вам вряд ли понадобятся все 150 млн записей одновременно, поэтому я думаю, что «статус» всегда будет использоваться вместе с другими столбцами. Возможно, было бы разумнее использовать составной индекс, такой как (status, fullname)

3 голосов
/ 05 марта 2010

Ян, вам обязательно нужно проиндексировать этот столбец. Я не уверен в контексте цитаты, но все, что вы сказали выше, правильно. Без индекса для этого столбца вы наверняка выполняете сканирование таблицы на 300M строк, что является худшим результатом для этих данных.

Ян, как спросили, где ваш запрос включает просто "где статус = включен" без какого-либо другого ограничивающего фактора, индекс в этом столбце, очевидно, не поможет (рад, что сообщество SO показало мне, что случилось). Однако, если есть ограничивающий фактор, такой как «предел 10», индекс может помочь. Кроме того, помните, что индексы также используются по группам и в порядке оптимизации. Если вы делаете «выбрать количество (*), статус из таблицы таблицы по статусу», индекс будет полезным.

Вам также следует рассмотреть возможность преобразования статуса в tinyint, где 0 означает отключение, а 1 - включение. Вы теряете тонны пространства, храня эту строку против крошечного, который требует только 1 байт на строку!

1 голос
/ 22 марта 2018

@ a'r ответ верен, однако необходимо указать, что полезность индекса определяется не только его количеством элементов, но также распределением данных и запросами, выполняемыми в базе данных.

В случае OP с 150M записями, имеющими status='enabled' и 150M, имеющими status='disabled', индекс не нужен и является пустой тратой ресурсов.

В случае 299M записей, имеющих status='enabled' и 1M, имеющих status='disabled', индекс полезен (и будет использоваться) в запросах типа SELECT ... where status='disabled'.
Запросы типа SELECT ... where status='enabled' будут по-прежнему выполняться с полным сканированием таблицы.

0 голосов
/ 08 апреля 2019

У меня есть аналогичный столбец в моей базе данных MySQL. Примерно 4 миллиона строк, с распределением 90% 1 и 10% 0.

Я только что обнаружил сегодня, что мои запросы (where column = 1) на самом деле выполняются значительно быстрее БЕЗ индекса.

Глупо я удалил индекс. Я говорю глупо, потому что теперь я подозреваю, что запросы (where column = 0), возможно, все еще выиграли от этого. Поэтому вместо этого я должен явно указать MySQL игнорировать индекс при поиске 1 и использовать его при поиске 0. Может быть.

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