# 1071 - вказаний ключ був занадто довгим; Максимальна довжина ключа - 1000 байт


102

Я знаю, що на запитання з цією назвою відповідали і раніше, але читайте далі. Я прочитав ретельно всі інші питання / відповіді на цю помилку перед публікацією.

Я отримую вищезгадану помилку для наступного запиту:

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;

Хтось має ідею, чому і як це виправити? Улов полягає в тому, що цей самий запит відмінно працює на моїй локальній машині, а також працює на моєму попередньому хості. Btw.it зі зрілого проекту - phpdevshell - тому я б здогадався, що хлопці знають, що вони роблять, хоча ти ніколи не знаєш.

Будь-яка підказка цінується.

Я використовую phpMyAdmin.

Відповіді:


170

Як говорить @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

Дійсно, дуже корисний запит для з’ясування довжини, до якої слід встановити індекс. Використовували це кілька разів, щоб визначити найкращу довжину для індексу. Дякую, що поділились!
Нірай Кумар

1
У вашому зручному запиті є непомітна помилка, щоб виміряти, наскільки насправді є рядки: ви припускаєте, що рядки всі присутні; тобто, що вони всі там. Якщо деякі з них недійсні, це скине ваші обчислення та занижене коротке число. Ви хочете використовувати count ([ім'я_поля]) замість 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 charset. Чи можна якось подолати це обмеження (у мене повний доступ до сервера), чи є це з поважної причини?
CodeVirtuoso

4
Це жахлива порада. Будь ласка, дізнайтеся, що таке символи та як це спричинить серйозні проблеми в майбутньому. Змішана база даних шаблонів не тільки спричиняє проблеми при використанні приєднань або підборів, вона ставить ваші дані в ненормований формат і може бути майже неможливою для виправлення пізніше.
Джеффрі

16

У мене виникло це питання, і було вирішено наступне:

Причина

Існує відома помилка з MySQL, пов’язана з MyISAM, набором символів UTF8 та індексами, які ви можете перевірити тут.

Дозвіл

  • Переконайтеся, що MySQL налаштований із механізмом зберігання InnoDB.

  • Змініть механізм зберігання даних, який використовується за замовчуванням, щоб нові таблиці завжди створювалися належним чином:

    set GLOBAL storage_engine='InnoDb';

  • Для MySQL 5.6 та новіших версій використовуйте наступне:

    SET GLOBAL default_storage_engine = 'InnoDB';

  • І нарешті переконайтеся, що ви дотримуєтесь інструкцій, наведених у міграції на MySQL .

Довідково


У більшості випадків ми забуваємо налаштувати двигун зберігання даних як "InnoDB". Наступна відповідь є найбільш простою і, ймовірно, вирішить проблему для більшості користувачів тут. Дякую.
Фредеріко Сезар

10

запустіть цей запит перед створенням або зміною таблиці.

SET @@global.innodb_large_prefix = 1;

це встановить максимальну довжину ключа до 3072 байтів


3

Цей обмеження розміру індексу здається більшим для 64-бітних збірок MySQL.

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


2

Я щойно зробив обхід цієї помилки, просто змінивши значення "довжини" у вихідній базі даних на загальну суму близько "1000", змінивши її структуру, а потім експортував ту саму, на сервер. :)


0

Я зіткнувся з тією ж проблемою, яку використовували під запитом нижче, щоб вирішити її.

Під час створення БД ви можете використовувати кодування utf-8

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

EDIT: (З огляду на пропозиції з коментарів) utf8_bin змінено на utf8mb4


2
Це вказувало мені в правильному напрямку. Для мене мені довелося змінити свою відповідність на: utf8_general_ci
Ian Newland

2
Це НЕ правильний напрямок, не робіть цього! MySQL utf8НЕ utf8, це помилковий фірмовий формат, який ніколи не мав збуватися. utf8mb4вірно utf8і рекомендовано за замовчуванням для належної utf8підтримки.
Джеффрі

utf8mb4правильне кодування використовувати на mysql, а неutf8
alok

Це був лише приклад - все одно я оновив відповідь.
Sudhir Dhumal
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.