Возможный INDEX для поля VARCHAR в MySql

40

Я работаю в базе данных MySql с такой таблицей:

+--------------+
|  table_name  |
+--------------+
|    myField   |
+--------------+

... и мне нужно сделать много запросов, как это (с 5-10 строк в списке) :

SELECT myField FROM table_name
WHERE myField IN ('something', 'other stuff', 'some other a bit longer'...)

Там будет около 24.000.000 уникальных строк

1) Должен ли я использовать FULLTEXTили или INDEXключ для моего VARCHAR(150)?
2) Если я увеличу число символов со 150 до 220 или 250 ... будет ли это иметь большое значение? (Есть ли способ , чтобы вычислить его?)
3) Как я уже сказал, они собираются быть уникальным, так MyField должно быть PRIMARY KEY . Не редкость ли добавить ПЕРВИЧНЫЙ КЛЮЧ в поле, которое уже является ИНДЕКСОМ VARCHAR / FULLTEXT?

Башня Марка
источник
Вам не нужно использовать PRIMARY для уникальности. Для этого уже есть УНИКАЛЬНЫЕ.
kommradHomer

Ответы:

62

ПРЕДЛОЖЕНИЕ № 1: Стандартная индексация

CREATE TABLE mytable
(
    id int not null auto_increment,
    myfield varchar(255) not null,
    primary key (id),
    key (myfield)
);

Если вы индексируете, как это, вы можете искать всю строку или выполнять лево-ориентированный поиск LIKE

ПРЕДЛОЖЕНИЕ № 2: Индексирование FULLTEXT

CREATE TABLE mytable
(
    id int not null auto_increment,
    myfield varchar(255) not null,
    primary key (id),
    fulltext (myfield)
);

Вы можете эффективно использовать поиск по отдельным ключевым словам, а также целые фразы. Вам нужно будет определить пользовательский список стоп-слов, потому что MySQL не будет индексировать 543 слова .

Вот мои другие посты за последние два года по индексам FULLTEXT

ПРЕДЛОЖЕНИЕ № 3: Индексирование хэша

CREATE TABLE mytable
(
    id int not null auto_increment,
    myfield varchar(255) not null,
    hashmyfield char(32) not null,
    primary key (id),
    key (hashmyfield)
);

Если вы ищете одно конкретное значение, и эти значения могут иметь длину, значительно превышающую 32 символа, вы можете сохранить значение хеш-функции:

INSERT INTO mytable (myfield,hashmyfield)
VALUES ('whatever',MD5('whatever'));

Таким образом, вы просто ищете хеш-значения для получения результатов

SELECT * FROM mytable WHERE hashmyfield = MD5('whatever');

Попробуйте!

RolandoMySQLDBA
источник
У меня недостаточно репутации, чтобы проголосовать за ваш ответ, но я должен сказать, что это было ОТЛИЧНО. Спасибо за объяснение и примеры. Я думаю, что хеширование является лучшим для моего случая, это отличное решение. Но все же один вопрос: как вы думаете, какой будет предел строк для быстрого поиска в таблице? [используя в качестве КЛЮЧА VARCHAR (32) для поисков]
Башня Марка
2
Параметр хеширования по-прежнему представляет собой текст и 32 байта, что в действительности составляет 16 байтов. Вы можете использовать поле bigint с conv (слева (md5 («что угодно»), 16), 16, -10). Там нет 16-байтового числа, но вы можете найти половину md5 достаточным, и тогда в индексе будет только 8 байт
atxdba
1
Неправильно использовать MD5 или SHA1 для создания строк, которые будут проиндексированы. Распределение строк, создаваемых хеш-функциями, такими как MD5 или SHA1, является случайным в большом пространстве, что снижает эффективность вашего индекса, что может замедлять операторы INSERT и SELECT. Вот пост, объясняющий это: code-epicenter.com/…
Mr.M
Я извиняюсь, потому что это старая ветка, но мой вопрос напрямую связан с этим, но я не могу получить четкий ответ для своих нужд, прочитав вышеизложенное и другие подобные статьи. Мой сценарий: я разрабатываю очень элементарную систему запасов, которая на данный момент состоит только из одной таблицы. Доступ к нему осуществляется через API, поэтому вся конфигурация хранится в другом месте - вот почему нам нужна только одна таблица. Два столбца, о которых я думаю об индексировании, будут содержать примерно 200 уникальных записей длиной менее 20 символов. Стоит ли добавлять индексы?
Майк
Это левый поиск like 'a%'?
Бухгалтер م
18

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

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

+-----+-----------+
| id  | value     |
+-----+-----------+
| 1   | abc       |
| 2   | abd       |
| 3   | adg       |
+-----+-----------+

Если мы будем индексировать только первый символ (N = 1), тогда таблица индекса будет выглядеть следующим образом:

+---------------+-----------+
| indexedValue  | rows      |
+---------------+-----------+
| a             | 1,2,3     |
+---------------+-----------+

В этом случае индекс селективности равен IS = 1/3 = 0,33.

Давайте теперь посмотрим, что произойдет, если мы увеличим количество проиндексированных символов до двух (N = 2).

+---------------+-----------+
| indexedValue  | rows      |
+---------------+-----------+
| ab             | 1,2      |
| ad             | 3        |
+---------------+-----------+

В этом сценарии IS = 2/3 = 0,66, что означает, что мы увеличили селективность индекса, но мы также увеличили размер индекса. Хитрость заключается в том, чтобы найти минимальное число N, которое приведет к максимальной селективности индекса .

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

Допустим, мы хотим добавить столбец last_name из таблицы employee в индекс, и мы хотим определить наименьшее число N, которое обеспечит наилучшую селективность индекса.

Сначала давайте определим наиболее частые фамилии:

select count(*) as cnt, last_name from employees group by employees.last_name order by cnt

+-----+-------------+
| cnt | last_name   |
+-----+-------------+
| 226 | Baba        |
| 223 | Coorg       |
| 223 | Gelosh      |
| 222 | Farris      |
| 222 | Sudbeck     |
| 221 | Adachi      |
| 220 | Osgood      |
| 218 | Neiman      |
| 218 | Mandell     |
| 218 | Masada      |
| 217 | Boudaillier |
| 217 | Wendorf     |
| 216 | Pettis      |
| 216 | Solares     |
| 216 | Mahnke      |
+-----+-------------+
15 rows in set (0.64 sec)

Как видите, фамилия Баба - самая частая. Теперь мы собираемся найти наиболее часто встречающиеся префиксы last_name , начиная с пятибуквенных префиксов.

+-----+--------+
| cnt | prefix |
+-----+--------+
| 794 | Schaa  |
| 758 | Mande  |
| 711 | Schwa  |
| 562 | Angel  |
| 561 | Gecse  |
| 555 | Delgr  |
| 550 | Berna  |
| 547 | Peter  |
| 543 | Cappe  |
| 539 | Stran  |
| 534 | Canna  |
| 485 | Georg  |
| 417 | Neima  |
| 398 | Petti  |
| 398 | Duclo  |
+-----+--------+
15 rows in set (0.55 sec)

Каждый префикс встречается гораздо чаще, что означает, что мы должны увеличивать число N, пока значения не станут почти такими же, как в предыдущем примере.

Вот результаты для N = 9

select count(*) as cnt, left(last_name,9) as prefix from employees group by prefix order by cnt desc limit 0,15;

+-----+-----------+
| cnt | prefix    |
+-----+-----------+
| 336 | Schwartzb |
| 226 | Baba      |
| 223 | Coorg     |
| 223 | Gelosh    |
| 222 | Sudbeck   |
| 222 | Farris    |
| 221 | Adachi    |
| 220 | Osgood    |
| 218 | Mandell   |
| 218 | Neiman    |
| 218 | Masada    |
| 217 | Wendorf   |
| 217 | Boudailli |
| 216 | Cummings  |
| 216 | Pettis    |
+-----+-----------+

Вот результаты для N = 10.

+-----+------------+
| cnt | prefix     |
+-----+------------+
| 226 | Baba       |
| 223 | Coorg      |
| 223 | Gelosh     |
| 222 | Sudbeck    |
| 222 | Farris     |
| 221 | Adachi     |
| 220 | Osgood     |
| 218 | Mandell    |
| 218 | Neiman     |
| 218 | Masada     |
| 217 | Wendorf    |
| 217 | Boudaillie |
| 216 | Cummings   |
| 216 | Pettis     |
| 216 | Solares    |
+-----+------------+
15 rows in set (0.56 sec)

Это очень хорошие результаты. Это означает, что мы можем сделать индекс по столбцу last_name с индексированием только первых 10 символов. В столбце определения таблицы last_name определяется как VARCHAR(16), и это означает, что мы сохранили 6 байтов (или больше, если в фамилии есть символы UTF8) для каждой записи. В этой таблице 1637 различных значений, умноженных на 6 байтов, составляют около 9 КБ, и представьте, как это число будет расти, если наша таблица будет содержать миллион строк.

Вы можете прочитать другие способы вычисления числа N в моем посте Префиксные индексы в MySQL .

Использование функций MD5 и SHA1 для генерации значений, которые должны быть проиндексированы, также не является хорошим подходом . Зачем? Прочтите это в посте Как правильно выбрать тип данных для первичного ключа в базе данных MySQL

Mr.M
источник
Это очень подробный ответ на другой вопрос.
Мустаччо
1
Ты шутишь, что ли?
Мистер М
Можете ли вы объяснить, что не так, или что нельзя применить к вопросу?
Мистер М
2
Привет, мистер. Мне действительно нравится ваш ответ. Зачем ? В моем старом ответа, я сказал в внушении # 1: If you index like this, you can either look for the whole string or do left-oriented LIKE searches. Я также сказал в ПРЕДЛОЖЕНИИ № 3 If you are looking for one specific value and those values could be lengths well beyond 32 characters, you could store the hash value:. Ваш ответ адекватно демонстрирует, почему не следует использовать огромные ключи и указывать на крайнем левом символе, что может повлиять на производительность. Ваш ответ принадлежит здесь. +1 за ваш ответ и добро пожаловать в DBA StackExchange.
RolandoMySQLDBA