# 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.

CodeVirtuoso
источник

Ответы:

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 байт.

Devart
источник
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
аллок
Это был просто пример - в любом случае я обновил ответ.
Судхир Дхумал,