Подтвердить что ты не робот

Длина индекса varchar MySQL

У меня есть таблица вроде этого:

CREATE TABLE `products` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(512) NOT NULL,
  `description` text,
  PRIMARY KEY (`id`),
) ENGINE=InnoDB AUTO_INCREMENT=38 DEFAULT CHARSET=utf8;

и один такой:

CREATE TABLE `product_variants` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `product_id` int(11) unsigned NOT NULL,
  `product_code` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `product_code` (`product_code`),
  KEY `product_variant_product_fk` (`product_id`),
  CONSTRAINT `product_variant_product_fk` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1037 DEFAULT CHARSET=utf8;

и оператор SQL, подобный этому

SELECT p.id AS id, p.name AS name, p.description AS description, pv.id AS product_variant_id, pv.product_code AS product_code
FROM products p
INNER JOIN product_variants pv ON pv.product_id = p.id
ORDER BY p.name ASC
LIMIT 300 OFFSET 0;

который, если я объясню, дает мне следующее:

+----+-------------+-------+------+----------------------------+----------------------------+---------+---------+--------+----------------+
| id | select_type | table | type | possible_keys              | key                        | key_len | ref     | rows   | Extra          |
+----+-------------+-------+------+----------------------------+----------------------------+---------+---------+--------+----------------+
|  1 | SIMPLE      | p     | ALL  | PRIMARY                    | NULL                       | NULL    | NULL    | 993658 | Using filesort |
|  1 | SIMPLE      | pv    | ref  | product_variant_product_fk | product_variant_product_fk | 4       | db.p.id |      1 |                |
+----+-------------+-------+------+----------------------------+----------------------------+---------+---------+--------+----------------+
2 rows in set (0.00 sec)

Для миллиона строк это довольно медленно. Я попытался добавить индекс на products.name с:

ALTER TABLE products ADD INDEX `product_name_idx` (name(512));

который дает следующее:

mysql> show indexes from products;
+----------+------------+------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table    | Non_unique | Key_name         | Seq_in_index | Column_name     | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+----------+------------+------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| products |          0 | PRIMARY          |            1 | id              | A         |      993658 |     NULL | NULL   |      | BTREE      |         |               |
| products |          1 | product_manf_fk  |            1 | manufacturer_id | A         |          18 |     NULL | NULL   | YES  | BTREE      |         |               |
| products |          1 | product_name_idx |            1 | name            | A         |         201 |      255 | NULL   |      | BTREE      |         |               |
+----------+------------+------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
3 rows in set (0.00 sec)

Я думаю, что столбец Sub_part показывает префикс, который был в (в байтах), как описано на этой странице.

Когда я повторно объясню запрос, я получаю:

+----+-------------+-------+------+----------------------------+----------------------------+---------+---------+--------+----------------+
| id | select_type | table | type | possible_keys              | key                        | key_len | ref     | rows   | Extra          |
+----+-------------+-------+------+----------------------------+----------------------------+---------+---------+--------+----------------+
|  1 | SIMPLE      | p     | ALL  | PRIMARY                    | NULL                       | NULL    | NULL    | 993658 | Using filesort |
|  1 | SIMPLE      | pv    | ref  | product_variant_product_fk | product_variant_product_fk | 4       | db.p.id |      1 |                |
+----+-------------+-------+------+----------------------------+----------------------------+---------+---------+--------+----------------+
2 rows in set (0.00 sec)

который выглядит как новый индекс не используется. Как описано в эта страница, индексы не будут использоваться для сортировки, если они префиксные индексы. Фактически, если я усекаю данные с помощью:

alter table products modify `name`  varchar(255) not null;

Объяснение дает:

+----+-------------+-------+-------+----------------------------+----------------------------+---------+----------------------------------------------+------+-------+
| id | select_type | table | type  | possible_keys              | key                        | key_len | ref                                          | rows | Extra |
+----+-------------+-------+-------+----------------------------+----------------------------+---------+----------------------------------------------+------+-------+
|  1 | SIMPLE      | p     | index | PRIMARY                    | product_name_idx           | 767     | NULL                                         |  300 |       |
|  1 | SIMPLE      | pv    | ref   | product_variant_product_fk | product_variant_product_fk | 4       | oh_2c98c233_69fe_4f06_ad0d_fe6f85a5beac.p.id |    1 |       |
+----+-------------+-------+-------+----------------------------+----------------------------+---------+----------------------------------------------+------+-------+

который, я думаю, поддерживает это. Тем не менее, он говорит на этой странице, что Таблицы InnoDB могут иметь до 767 байт индекса. Если длина в байтов, почему он отказывается иметь более 255? Если он символы, как он определяет длину каждого символа UTF-8? Является он просто предполагает 3?

Кроме того, я использую эту версию MySQL:

mysql> select version();
+------------+
| version()  |
+------------+
| 5.5.27-log |
+------------+
1 row in set (0.00 sec)
4b9b3361

Ответ 1

Я должен пересмотреть свой ответ из-за моих исследований. Я изначально разместил это (цитируя себя):

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

И я не уверен, но это все еще может быть правильным, но не так, как я думал.

Вот правильный ответ:

MySQL принимает 3 байта на символ utf8. 255 символов - максимальный размер индекса, который вы можете указать для каждого столбца, потому что 256x3 = 768, что нарушает ограничение на 767 байтов.

Если вы не укажете размер индекса, MySQL выберет максимальный размер (т.е. 255 за столбец). Ограничение UNIQUE не может быть помещено в столбец utf8, длина которого больше 255, поскольку уникальный индекс должен содержать все значение ячейки. Но можно использовать обычный индекс - он будет просто индексировать первые 255 символов (или первые 767 байт?). И вот где еще есть какая-то тайна для меня.

МИСТЕР: Я могу понять, почему MySQL предполагает 3 байта на символ для безопасности, поскольку в противном случае ограничение UNIQUE может быть нарушено. Но документы, похоже, предполагают, что индекс фактически имеет размер в байтах, а не в символах. Итак, предположим, что вы разместили индекс 25 5 char (765 байт) на столбце varchar (25 6). Если хранятся все символы ASCII, 1-байтовые символы, такие как A-Z, a-z, 0-9, тогда вы можете поместить весь столбец в индекс 767 байтов. И похоже, что это действительно произойдет.

Ниже приведена более подробная информация из моего первоначального ответа о символах, байтах и ​​т.д.


Согласно wikipedia, символ UTF-8 может быть длиной 1,2, 3 или 4 байта. Но, согласно эта документация mysql, максимальный размер символа составляет 3 байта, поэтому индекс индекса столбца более 255 символов может попасть в этот байт-лимит, Но, как я понимаю, это не так. Если большинство ваших символов находятся в диапазоне ASCII, тогда ваш средний размер символа будет ближе к 1 байту. Если ваш средний размер символа составляет, например, 1,3 байта (в основном 1 байт, но значительное количество 2-3 байтовых символов), то вы можете указать индекс 767/1.3

Итак, если вы сохраняете в основном 1-байтные символы, ваш фактический лимит символов будет больше похож: 767/1.3 = 590. Но оказывается, что это не так, как это работает. 255 символов - это предел.

Как упоминалось в этой документации MySQL,

Пределы префикса измеряются в байтах, тогда как длина префикса в Операторы CREATE INDEX интерпретируются как количество символов для неважные типы данных (CHAR, VARCHAR, TEXT). Учитывайте это при указании длины префикса для столбца, который использует многобайтовый набор символов.

Кажется, что MySQL советует людям делать расчет/гадание, как я это делал, чтобы определить ваш размер ключа для столбца varchar. Но на самом деле вы не можете указать индекс, превышающий 255 для столбцов utf8.

Наконец, если вы снова вернетесь к моей второй ссылке, это также:

Если параметр конфигурации innodb_large_prefix включен, это ограничение длины увеличивается до 3072 байт, для таблиц InnoDB, которые используют DYNAMIC и COMPRESSED.

Итак, кажется, что вы можете получить гораздо большие индексы, если хотите, с небольшим количеством настроек. Просто убедитесь, что форматы строк DYNAMIC или COMPRESSED. В этом случае вы, вероятно, можете указать индекс 1023 или 1024 символа.


Кстати, оказывается, что вы можете хранить 4-байтовые символы, используя набор символов utf8mb4. Набор символов utf8, по-видимому, хранит только "plane 0" characters.

EDIT:

Я просто попытался создать составной индекс в столбце varchar (511) с столбцом tinyint (1) и получил сообщение об ошибке, указывающее, что максимальный размер индекса составляет 767 байт. Это заставляет меня полагать, что MySQL предполагает, что столбцы набора символов utf8 будут содержать 3 байта на символ (максимум) и позволяют использовать максимум 255 символов. Но, возможно, это только с составными индексами. Я уточню свой ответ, когда узнаю больше. Но сейчас я оставляю это как редактирование.

Ответ 2

Ограничения на таблицах InnoDB

Внимание

Не конвертируйте системные таблицы MySQL в базу данных mysql из таблиц MyISAM в InnoDB. Это неподдерживаемая операция. Если вы это сделаете, MySQL не будет перезагружен, пока вы не восстановите старые системные таблицы из резервной копии или не сгенерируете их с помощью программы mysql_install_db.

Внимание

Не рекомендуется настраивать InnoDB для использования файлов данных или файлов журналов на томах NFS. В противном случае файлы могут быть заблокированы другими процессами и станут недоступными для использования MySQL.

Максимальные и минимальные значения

  • Таблица может содержать не более 1000 столбцов.
  • Таблица может содержать не более 64 вторичных индексов.
  • По умолчанию индексный индекс для индекса с одним столбцом может содержать до 767 байт. Такой же предел длины применяется к любому префиксу ключа индекса. Например, вы можете использовать этот предел с индексом префикса столбца более 255 символов в столбце TEXT или VARCHAR, предполагая набор символов UTF-8 и максимум 3 байта для каждого символа. Когда параметр конфигурации innodb_large_prefix включен, этот предел длины увеличивается до 3072 байт, для таблиц InnoDB, которые используют форматы строк DYNAMIC и COMPRESSED.
  • Если вы укажете длину префикса индекса, которая больше допустимого максимального значения, длина будет постепенно уменьшена до максимальной длины. В MySQL 5.6 и более поздних версиях, указав длину префикса индекса, превышающую максимальную длину, возникает ошибка.

Когда включено innodb_large_prefix, попытка создания префикса индекса с длиной ключа больше 3072 для таблицы REDUNDANT или COMPACT вызывает ошибку ER_INDEX_COLUMN_TOO_LONG.

Внутренняя максимальная длина ключа InnoDB составляет 3500 байт, но сама MySQL ограничивает это до 3072 байт. Этот предел применяется к длине объединенного индексного ключа в индексе с несколькими столбцами.

Максимальная длина строки, за исключением столбцов переменной длины (VARBINARY, VARCHAR, BLOB и TEXT), немного меньше половины страницы базы данных. То есть максимальная длина строки составляет около 8000 байт. Столбцы LONGBLOB и LONGTEXT должны быть меньше 4 ГБ, а общая длина строки, включая столбцы BLOB и TEXT, должна быть меньше 4 ГБ.

Ссылка: Ограничения InnoDB