2013-03-01 4 views
21

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

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 показывает префикс, который был в индексируются (в байтах), как описано в this page.

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

+----+-------------+-------+------+----------------------------+----------------------------+---------+---------+--------+----------------+ 
| 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) 

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

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

Explain, дает:

+----+-------------+-------+-------+----------------------------+----------------------------+---------+----------------------------------------------+------+-------+ 
| 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 |  | 
+----+-------------+-------+-------+----------------------------+----------------------------+---------+----------------------------------------------+------+-------+ 

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

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

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

До MySQL 5.0.3 максимальная длина поля VARCHAR составляет 255, а 65535 в MySQL 5.0.3 и более поздних версиях. – Cyclonecode

+0

извините - должно было сказать, я использую 5.5.27-log – l0st3d

ответ

43

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

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

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

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

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

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

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

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


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

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

Как уже упоминалось в this MySQL documentation,

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

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

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

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

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


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

EDIT:

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

0

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

Предупреждение

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

Предупреждение

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

максимумы и минимумы

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

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

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

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

Ссылка: InnoDB Restrictions

Смежные вопросы