# 1071 - Указанный ключ был слишком длинным; максимальная длина ключа составляет 1000 байт


105

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

Я получаю указанную выше ошибку по следующему запросу:

CREATE TABLE IF NOT EXISTS `pds_core_menu_items` (
  `menu_id` varchar(32) NOT NULL,
  `parent_menu_id` int(32) unsigned DEFAULT NULL,
  `menu_name` varchar(255) DEFAULT NULL,
  `menu_link` varchar(255) DEFAULT NULL,
  `plugin` varchar(255) DEFAULT NULL,
  `menu_type` int(1) DEFAULT NULL,
  `extend` varchar(255) DEFAULT NULL,
  `new_window` int(1) DEFAULT NULL,
  `rank` int(100) DEFAULT NULL,
  `hide` int(1) DEFAULT NULL,
  `template_id` int(32) unsigned DEFAULT NULL,
  `alias` varchar(255) DEFAULT NULL,
  `layout` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`menu_id`),
  KEY `index` (`parent_menu_id`,`menu_link`,`plugin`,`alias`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Кто-нибудь знает, почему и как это исправить? Загвоздка в том, что этот же запрос отлично работает на моем локальном компьютере и работал на моем предыдущем хосте. Кстати, это из зрелого проекта - phpdevshell - так что я предполагаю, что эти ребята знают, что делают, хотя вы никогда не знаете.

Любая подсказка приветствуется.

Я использую phpMyAdmin.

Ответы:


175

Как говорит @Devart, общая длина вашего индекса слишком велика.

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

Лучше всего использовать префиксные индексы, чтобы вы индексировали только левую подстроку данных. В любом случае большая часть ваших данных будет намного короче 255 символов.

Вы можете объявить длину префикса для каждого столбца при определении индекса. Например:

...
KEY `index` (`parent_menu_id`,`menu_link`(50),`plugin`(50),`alias`(50))
...

Но какова наилучшая длина префикса для данного столбца? Вот способ узнать:

SELECT
 ROUND(SUM(LENGTH(`menu_link`)<10)*100/COUNT(`menu_link`),2) AS pct_length_10,
 ROUND(SUM(LENGTH(`menu_link`)<20)*100/COUNT(`menu_link`),2) AS pct_length_20,
 ROUND(SUM(LENGTH(`menu_link`)<50)*100/COUNT(`menu_link`),2) AS pct_length_50,
 ROUND(SUM(LENGTH(`menu_link`)<100)*100/COUNT(`menu_link`),2) AS pct_length_100
FROM `pds_core_menu_items`;

Он сообщает вам долю строк, длина которых в menu_linkстолбце не превышает заданной длины . Вы можете увидеть такой результат:

+---------------+---------------+---------------+----------------+
| pct_length_10 | pct_length_20 | pct_length_50 | pct_length_100 |
+---------------+---------------+---------------+----------------+
|         21.78 |         80.20 |        100.00 |         100.00 |
+---------------+---------------+---------------+----------------+

Это говорит о том, что 80% ваших строк содержат менее 20 символов, а все ваши строки - менее 50 символов. Таким образом, нет необходимости индексировать длину префикса, превышающую 50, и, конечно же, нет необходимости индексировать полную длину 255 символов.

PS: Типы данных INT(1)и INT(32)указывают на еще одно недопонимание MySQL. Числовой аргумент не влияет на хранение или диапазон значений, разрешенных для столбца. INTвсегда 4 байта, и он всегда допускает значения от -2147483648 до 2147483647. Числовой аргумент касается значений заполнения во время отображения, который не действует, если вы не используете этот ZEROFILLпараметр.


17
Большое спасибо за подробное объяснение. Помимо решения проблемы, я также узнал кое-что ценное.
— CodeVirtuoso 05

Действительно, очень полезный запрос для определения длины, на которую должен быть установлен индекс. Использовал это несколько раз, чтобы определить лучшую длину индекса. Спасибо, что поделились!
— Нирадж Кумар

1
В вашем удобном запросе есть небольшая ошибка для измерения реальной длины строк: вы предполагаете, что все строки присутствуют; то есть, что они все есть. Если некоторые из них равны нулю, это отбросит ваши вычисления и приведет к занижению коротких строк. Вы хотите использовать count ([field_name]) вместо count (*).
— D Mac

Он не будет использовать больше, чем первый "префиксный" индекс.
— Рик Джеймс

28

Эта ошибка означает, что длина индекса indexпревышает 1000 байт. MySQL и механизмы хранения могут иметь это ограничение. У меня аналогичная ошибка в MySQL 5.5 - «Указанный ключ слишком длинный; максимальная длина ключа составляет 3072 байта при запуске этого скрипта:

CREATE TABLE IF NOT EXISTS test_table1 (
  column1 varchar(500) NOT NULL,
  column2 varchar(500) NOT NULL,
  column3 varchar(500) NOT NULL,
  column4 varchar(500) NOT NULL,
  column5 varchar(500) NOT NULL,
  column6 varchar(500) NOT NULL,
  KEY `index` (column1, column2, column3, column4, column5, column6)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

UTF8 многобайтовый, длина ключа рассчитывается таким образом - 500 * 3 * 6 = 9000 байт.

Но обратите внимание, следующий запрос работает!

CREATE TABLE IF NOT EXISTS test_table1 (
  column1 varchar(500) NOT NULL,
  column2 varchar(500) NOT NULL,
  column3 varchar(500) NOT NULL,
  column4 varchar(500) NOT NULL,
  column5 varchar(500) NOT NULL,
  column6 varchar(500) NOT NULL,
  KEY `index` (column1, column2, column3, column4, column5, column6)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

... поскольку я использовал CHARSET = latin1, в этом случае длина ключа составляет 500 * 6 = 3000 байт.


7
Спасибо за ответ, это работает, но за счет отказа от кодировки utf8. Можно ли как-то обойти это ограничение (у меня полный доступ к серверу), есть ли на то веская причина?
— CodeVirtuoso 05

5
Это ужасный совет, пожалуйста, узнайте, что такое кодировки и как это вызовет серьезные проблемы в будущем. База данных со смешанной кодировкой не только вызывает проблемы при использовании объединений или подвыборов, но и переводит ваши данные в ненормализованный формат, и впоследствии исправить их практически невозможно.
— Джеффри

17

У меня была эта проблема, и я решил ее следующим образом:

Причина

Существует известная ошибка MySQL, связанная с MyISAM, набором символов UTF8 и индексами, которую вы можете проверить здесь.

разрешение

  • Убедитесь, что MySQL настроен с механизмом хранения InnoDB.

  • Измените механизм хранения, используемый по умолчанию, чтобы новые таблицы всегда создавались соответствующим образом:

    set GLOBAL storage_engine='InnoDb';

  • Для MySQL 5.6 и новее используйте следующее:

    SET GLOBAL default_storage_engine = 'InnoDB';

  • И, наконец, убедитесь, что вы следуете инструкциям, приведенным в разделе « Переход на MySQL» .

Ссылка


В большинстве случаев мы забываем настроить механизм хранения как InnoDB. Следующий ответ является наиболее простым и, вероятно, решит проблему для большинства пользователей. Спасибо.
— Frederiko Cesar

10

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

SET @@global.innodb_large_prefix = 1;

это установит максимальную длину ключа 3072 байта


3

Этот предел размера индекса кажется больше в 64-битных сборках MySQL.

Я столкнулся с этим ограничением, пытаясь сбросить нашу базу данных разработчиков и загрузить ее на локальную виртуальную машину VMWare. Наконец я понял, что удаленный сервер разработчика был 64-битным, и я создал 32-битный virt. Я только что создал 64-битный virt и смог загрузить базу данных локально.


2

Я только что обошел эту ошибку, просто изменив значения «длины» в исходной базе данных до суммы около «1000», изменив ее структуру и затем экспортировав ее на сервер. :)


0

Я столкнулся с той же проблемой, которая использовалась ниже для ее решения.

При создании БД вы можете использовать кодировку utf-8

например. create database my_db character set utf8 collate utf8mb4;

РЕДАКТИРОВАТЬ: (Учитывая предложения из комментариев) Изменен utf8_bin на utf8mb4


2
Это указывало мне в правильном направлении. Мне пришлось изменить параметры сортировки на: utf8_general_ci
— Ян Ньюленд,

2
Это НЕ правильное направление, не делайте этого! MySQL - utf8это НЕ utf8, это проприетарный формат с ошибками, который никогда не должен был реализовываться. utf8mb4истинно utf8и рекомендуется по умолчанию для надлежащей utf8поддержки.
— Джеффри

utf8mb4- правильная кодировка для использования в mysql, а не для использованияutf8
— аллок

Это был просто пример - в любом случае я обновил ответ.
— Судхир Дхумал,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.