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

Когда я выполнил следующую команду:

ALTER TABLE `mytable` ADD UNIQUE (
`column1` ,
`column2`
);

Я получил это сообщение об ошибке:

#1071 - Specified key was too long; max key length is 767 bytes

Информация о столбце 1 и столбце 2:

column1 varchar(20) utf8_general_ci
column2  varchar(500) utf8_general_ci

Я думаю varchar(20) требуется только 21 байт varchar(500) требуется только 501 байт. Таким образом, общее количество байтов составляет 522, а не 767. Так почему я получил сообщение об ошибке?

#1071 - Specified key was too long; max key length is 767 bytes

38 ответов

Решение

767 байт - это заявленное ограничение префикса для таблиц InnoDB в MySQL версии 5.6 (и предыдущих версиях). Это длина 1000 байтов для таблиц MyISAM. В версии MySQL 5.7 и выше этот предел был увеличен до 3072 байт.

Вы также должны знать, что если вы устанавливаете индекс для большого поля типа char или varchar, кодируемого utf8mb4, вы должны разделить максимальную длину префикса индекса 767 байт (или 3072 байт) на 4, что приводит к 191. Это потому, что максимальная длина символа utf8mb4 составляет четыре байта. Для символа utf8 это будет три байта, что приведет к максимальной длине префикса индекса 254.

Один из вариантов, который у вас есть, это просто установить нижний предел для ваших полей VARCHAR.

Другой вариант (согласно ответу на эту проблему) состоит в том, чтобы получить подмножество столбца, а не всю сумму, то есть:

ALTER TABLE `mytable` ADD UNIQUE ( column1(15), column2(200) );

Настройте его так, чтобы получить ключ для применения, но мне интересно, стоит ли пересматривать вашу модель данных относительно этой сущности, чтобы увидеть, есть ли улучшения, которые позволят вам реализовать намеченные бизнес-правила, не нарушая ограничения MySQL.

Когда вы достигнете предела. Установите следующее.

  • INNODB utf8VARCHAR(255)
  • INNODB utf8mb4VARCHAR(191)

Если у кого-то возникают проблемы с INNODB / Utf-8, попробуйте поставить UNIQUE индекс на VARCHAR(256) поле, переключите его на VARCHAR(255), Кажется, 255 является ограничением.

MySQL предполагает наихудший случай для количества байтов на символ в строке. Для кодировки MySQL UTF8 это 3 байта на символ, так как эта кодировка не допускает символы за пределами U+FFFF, Для кодировки MySQL 'utf8mb4' это 4 байта на символ, так как именно это MySQL называет фактическим UTF-8.

Итак, если вы используете utf8, ваш первый столбец займет 60 байтов индекса, а второй - 1500.

Решение для Laravel Framework

Согласно документации Laravel 5.4.*; Вы должны установить длину строки по умолчанию внутри boot метод app/Providers/AppServiceProvider.php файл следующим образом:

use Illuminate\Support\Facades\Schema;

public function boot() 
{
    Schema::defaultStringLength(191); 
}

Объяснение этого исправления, приведенное в документации Laravel 5.4.*:

Laravel использует utf8mb4 набор символов по умолчанию, который включает в себя поддержку хранения "эмодзи" в базе данных. Если вы используете версию MySQL, более раннюю, чем версия 5.7.7, или MariaDB старше, чем версия 10.2.2, вам может потребоваться вручную настроить длину строки по умолчанию, создаваемую миграциями, чтобы MySQL создавал для них индексы. Вы можете настроить это, позвонив Schema::defaultStringLength метод в вашем AppServiceProvider,

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

Запустите этот запрос перед вашим запросом:

SET @@global.innodb_large_prefix = 1;

это увеличит лимит до 3072 bytes,

Какую кодировку символов вы используете? Некоторые наборы символов (например, UTF-16 и т. Д.) Используют более одного байта на символ.

Заменить utf8mb4 с участием utf8 в вашем файле импорта.

Я думаю, что varchar(20) требует только 21 байт, тогда как varchar(500) требует только 501 байт. Таким образом, общее количество байтов составляет 522, а не 767. Так почему я получил сообщение об ошибке?

UTF8 требует 3 байта на символ для хранения строки, поэтому в вашем случае 20 + 500 символов = 20 * 3 + 500 * 3 = 1560 байтов, что превышает допустимые 767 байтов.

Предел для UTF8 составляет 767/3 = 255 символов, для UTF8mb4, который использует 4 байта на символ, это 767/4 = 191 символ.


Есть два решения этой проблемы, если вам нужно использовать более длинный столбец, чем предел:

  1. Используйте "более дешевую" кодировку (ту, которая требует меньше байтов на символ)
    В моем случае мне нужно было добавить уникальный индекс на столбец, содержащий SEO строку статьи, так как я использую только [A-z0-9\-] персонажи для SEO я использовал latin1_general_ci который использует только один байт на символ, поэтому столбец может иметь длину 767 байтов.
  2. Создайте хеш из вашего столбца и используйте уникальный индекс только для этого
    Другой вариант для меня был создать еще один столбец, который будет хранить хэш SEO, этот столбец будет иметь UNIQUE ключ к тому, чтобы значения SEO были уникальными. Я бы тоже добавил KEY индекс до оригинальной колонки SEO, чтобы ускорить поиск.

Ответ о том, почему вы получаете сообщение об ошибке, уже был дан ответ многим пользователям здесь. Мой ответ о том, как исправить и использовать его как есть.

Ссылка по этой ссылке.

  1. Откройте клиент MySQL (или клиент MariaDB). Это инструмент командной строки.
  2. Он спросит ваш пароль, введите ваш правильный пароль.
  3. Выберите вашу базу данных с помощью этой команды use my_database_name;

База данных изменена

  1. set global innodb_large_prefix=on;

Запрос в порядке, затронуто 0 строк (0, 00 с)

  1. set global innodb_file_format=Barracuda;

Запрос в порядке, затронуто 0 строк (0, 02 с)

  1. Перейдите в свою базу данных на phpMyAdmin или что-то подобное для простоты управления. > Выберите базу данных> Просмотр структуры таблицы> Перейдите на вкладку Операции. > Измените ROW_FORMAT на DYNAMIC и сохраните изменения.
  2. Перейдите на вкладку структуры таблицы> Нажмите на уникальную кнопку.
  3. Готово. Теперь оно не должно иметь ошибок.

Проблема этого исправления заключается в том, что вы экспортируете db на другой сервер (например, с локального хоста на реальный хост) и не можете использовать командную строку MySQL на этом сервере. Вы не можете заставить это работать там.

Specified key was too long; max key length is 767 bytes

Вы получили это сообщение, потому что 1 байт равен 1 символу, только если вы используете latin-1 набор символов. Если вы используете utf8каждый символ будет рассматриваться как 3 байта при определении ключевого столбца. Если вы используете utf8mb4каждый символ будет считаться 4 байтом при определении ключевого столбца. Таким образом, вам нужно умножить ограничение на количество символов в вашем ключевом поле на 1, 3 или 4 (в моем примере), чтобы определить количество байтов, которое поле ключа пытается разрешить. Если вы используете uft8mb4, вы можете определить только 191 символ для поля первичного ключа InnoDB. Только не нарушайте 767 байт.

5 обходной путь:

Лимит был повышен в 5.7.7 (MariaDB 10.2.2?). И это может быть увеличено с некоторой работой в 5.6 (10.1).

Если вы превышаете лимит из-за попытки использовать CHARACTER SET utf8mb4. Затем выполните одно из следующих действий (у каждого есть недостаток), чтобы избежать ошибки:

⚈  Upgrade to 5.7.7 for 3072 byte limit -- your cloud may not provide this;
⚈  Change 255 to 191 on the VARCHAR -- you lose any values longer than 191 characters (unlikely?);
⚈  ALTER .. CONVERT TO utf8 -- you lose Emoji and some of Chinese;
⚈  Use a "prefix" index -- you lose some of the performance benefits.
⚈  Or... Stay with older version but perform 4 steps to raise the limit to 3072 bytes:

SET GLOBAL innodb_file_format=Barracuda;
SET GLOBAL innodb_file_per_table=1;
SET GLOBAL innodb_large_prefix=1;
logout & login (to get the global values);
ALTER TABLE tbl ROW_FORMAT=DYNAMIC;  -- (or COMPRESSED)

- http://mysql.rjweb.org/doc.php/limits

Вы можете добавить столбец md5 длинных столбцов

Для Laravel 5,6

Шаги к следованию

  1. Идти к App\Providers\AppServiceProvider.php,
  2. Добавить это к провайдеру use Illuminate\Support\Facades\Schema; в топе.
  3. В функции загрузки Добавить это Schema::defaultStringLength(191);

это все, наслаждайтесь.

Я исправил эту проблему с:

varchar(200) 

заменено на

varchar(191)

все varchar, у которых есть больше чем 200, заменяют их 191 или устанавливают их текст.

Мы столкнулись с этой проблемой при попытке добавить индекс UNIQUE в поле VARCHAR(255) с помощью utf8mb4. Несмотря на то, что проблема уже хорошо изложена, я хотел бы добавить несколько практических советов о том, как мы это выяснили и решили.

При использовании utf8mb4 символы считаются как 4 байта, тогда как в utf8 они могут быть как 3 байта. Базы данных InnoDB имеют ограничение, что индексы могут содержать только 767 байтов. Таким образом, при использовании utf8 вы можете хранить 255 символов (767/3 = 255), но при использовании utf8mb4 вы можете хранить только 191 символ (767/4 = 191).

Вы абсолютно можете добавить обычные индексы для VARCHAR(255) поля, использующие utf8mb4, но происходит то, что размер индекса автоматически усекается до 191 символа - как unique_key Вот:

Скриншот Sequel Pro, показывающий, что индекс урезан до 191 символа

Это хорошо, потому что обычные индексы просто используются, чтобы помочь MySQL быстрее искать ваши данные. Целое поле не нужно индексировать.

Итак, почему MySQL усекает индекс автоматически для обычных индексов, но выдает явную ошибку при попытке сделать это для уникальных индексов? Что ж, для того, чтобы MySQL мог выяснить, существует ли уже добавляемое или обновляемое значение, ему нужно на самом деле индексировать все значение, а не только его часть.

В конце дня, если вы хотите иметь уникальный индекс для поля, все содержимое поля должно вписываться в индекс. Для utf8mb4 это означает уменьшение длины поля VARCHAR до 191 символа или менее. Если вам не нужен utf8mb4 для этой таблицы или поля, вы можете вернуть его обратно к utf8 и сохранить свои поля длиной 255.

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

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

drop database if exists rhodes; create database rhodes default CHARACTER set utf8 default COLLATE utf8_general_ci;

Тем не менее, это не работает для всех случаев.

На самом деле это проблема использования индексов для столбцов VARCHAR с набором символов utf8 (или же utf8mb4), со столбцами VARCHAR, которые имеют более определенной длины символов. В случае utf8mb4, что определенная длина составляет 191.

Пожалуйста, обратитесь к разделу Длинный индекс в этой статье для получения дополнительной информации о том, как использовать длинные индексы в базе данных MySQL: http://hanoian.com/content/index.php/24-automate-the-converting-a-mysql-database-character-set-to-utf8mb4

Чтобы исправить это, это работает для меня как шарм.

ALTER DATABASE dbname CHARACTER SET utf8 COLLATE utf8_general_ci;

Я сделал некоторые поиски по этой теме, наконец, получил некоторые пользовательские изменения

Для MySQL верстак 6.3.7 Версия Доступна графическая межфаза

  1. Запустите Workbench и выберите соединение.
  2. Перейдите в управление или Экземпляр и выберите Файл параметров.
  3. Если Workbench попросит у вас разрешения на чтение файла конфигурации, а затем разрешите его, дважды нажав OK.
  4. В центре места появляется окно файла настроек администратора.
  5. Перейдите на вкладку InnoDB и проверьте innodb_large_prefix, если он не отмечен в разделе "Общие".
  6. установите значение параметра innodb_default_row_format равным DYNAMIC.

Для версий ниже 6.3.7 прямые опции недоступны, поэтому нужно использовать командную строку

  1. Запустите CMD от имени администратора.
  2. Перейти к директору, где установлен сервер mysql. В большинстве случаев это по адресу "C:\Program Files\MySQL\MySQL Server 5.7\bin", поэтому команда "cd \" "cd Program Files\MySQL\MySQL Server 5.7\bin".
  3. Теперь запустите команду mysql -u userName -p databasescheema Теперь он запрашивает пароль соответствующего пользователя. Введите пароль и введите приглашение MySQL.
  4. Мы должны установить некоторые глобальные настройки, вводить команды ниже по одному set global innodb_large_prefix=on; установить глобальный innodb_file_format=barracuda; установить глобальный innodb_file_per_table=true;
  5. Теперь, наконец, мы должны изменить ROW_FORMAT требуемой таблицы, по умолчанию ее COMPACT, мы должны установить ее в DYNAMIC.
  6. используйте следующую команду alter table table_name ROW_FORMAT=DYNAMIC;
  7. Готово

Длина индекса и MySQL / MariaDB


Laravel по умолчанию использует набор символов utf8mb4, который включает поддержку для хранения "emojis" в базе данных. Если вы используете версию MySQL, более раннюю, чем версия 5.7.7, или MariaDB, более раннюю, чем версия 10.2.2, вам может потребоваться вручную настроить длину строки по умолчанию, создаваемую миграциями, чтобы MySQL создавал для них индексы. Вы можете настроить это, вызвав метод Schema::defaultStringLength в вашем AppServiceProvider:

use Illuminate\Support\Facades\Schema;

/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
    Schema::defaultStringLength(191);
}

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

Ссылка из Официальной документации Laravel: https://laravel.com/docs/5.7/migrations

Это решило мою проблему

ALTER DATABASE dbname CHARACTER SET utf8 COLLATE utf8_general_ci;

Измените свою сортировку. Вы можете использовать utf8_general_ci, который поддерживает почти все

Просто меняется utf8mb4 в utf8 При создании таблиц решена моя проблема. Например: CREATE TABLE ... DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; в CREATE TABLE ... DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;,

Исходя из приведенного ниже столбца, эти два столбца переменной строки используют utf8_general_ci сопоставление (utf8 подразумевается charset).

В MySQL utf8 charset использует максимум 3 байта для каждого символа. Таким образом, потребуется выделить 500*3=1500 байт, что намного больше, чем 767 байт, которые позволяет MySQL. Вот почему вы получаете эту ошибку 1071.

Другими словами, вам нужно вычислить количество символов на основе байтового представления кодировки, так как не каждая кодировка является однобайтовым представлением (как вы предполагали). IE utf8 в MySQL используется максимум 3 байта на символ, 767/3≈255 символов, а для utf8mb4, самое большее 4-байтовое представление, 767/4≈191 символов.

Также известно, что MySQL

column1 varchar(20) utf8_general_ci
column2  varchar(500) utf8_general_ci

Я нашел этот запрос полезным для определения того, какие столбцы имеют индекс, нарушающий максимальную длину:

SELECT
  c.TABLE_NAME As TableName,
  c.COLUMN_NAME AS ColumnName,
  c.DATA_TYPE AS DataType,
  c.CHARACTER_MAXIMUM_LENGTH AS ColumnLength,
  s.INDEX_NAME AS IndexName
FROM information_schema.COLUMNS AS c
INNER JOIN information_schema.statistics AS s
  ON s.table_name = c.TABLE_NAME
 AND s.COLUMN_NAME = c.COLUMN_NAME 
WHERE c.TABLE_SCHEMA = DATABASE()
  AND c.CHARACTER_MAXIMUM_LENGTH > 191 
  AND c.DATA_TYPE IN ('char', 'varchar', 'text')

В моем случае у меня была эта проблема, когда я делал резервные копии базы данных, используя символы ввода / вывода перенаправления linux. Поэтому я меняю синтаксис, как описано ниже. PS: используя терминал Linux или Mac.

Резервное копирование (без перенаправления>)

# mysqldump -u root -p databasename -r bkp.sql

Восстановить (без <перенаправления)

# mysql -u root -p --default-character-set=utf8 databasename
mysql> SET names 'utf8'
mysql> SOURCE bkp.sql

Ошибка "Указанный ключ слишком длинный; максимальная длина ключа 767 байт" просто исчезла.

Эта проблема

В MySQL есть ограничения на максимальную длину ключа.

  • InnoDB - максимальная длина ключа составляет 1536 байт (для размера страницы 8 КБ) и 768 (для размера страницы 4 КБ) (Источник: Dev.MySQL.com).
  • MyISAM - максимальная длина ключа составляет 1000 байт (Source Dev.MySQL.com).

Они считаются байтами! Таким образом, для сохранения в ключе символа UTF-8 может потребоваться более одного байта.

Следовательно, у вас есть только два немедленных решения:

  • Индексируйте только первые n-е символы типа текста.
  • Создать FULL TEXT поиск - все будет доступно для поиска в тексте аналогично ElasticSearch

Индексирование первых N символов текстового типа

Если вы создаете таблицу, используйте следующий синтаксис для индексации первых 255 символов некоторого поля: KEY sometextkey (SomeText (255)). Вот так:

      CREATE TABLE `MyTable` (
    `id` int(11) NOT NULL auto_increment,
    `SomeText` TEXT NOT NULL,
    PRIMARY KEY  (`id`),
    KEY `sometextkey` (`SomeText`(255))
);

Если у вас уже есть таблица, вы можете добавить уникальный ключ в поле с помощью: ADD UNIQUE(ConfigValue (20));. Вот так:

      ALTER TABLE
MyTable
ADD UNIQUE(`ConfigValue`(20));

Если имя поля не является зарезервированным ключевым словом MySQL, то обратные кавычки (``) вокруг имени поля не обязательны.

Создание ПОЛНОГО ТЕКСТА поиска

Полнотекстовый поиск позволит вам искать всю ценность вашего TEXTполе. Он будет выполнять сопоставление всего слова, если вы используете NATURAL LANGUAGE MODE, или частичное сопоставление слов, если вы используете один из других режимов. Дополнительную информацию о параметрах FullText можно найти здесь: Dev.MySQL.com

Создайте свою таблицу с текстом и добавьте полнотекстовый индекс ...

      ALTER TABLE
        MyTable
ADD FULLTEXT INDEX
        `SomeTextKey` (`SomeTextField` DESC);

Затем выполните поиск в своей таблице так ...

      SELECT
        MyTable.id, MyTable.Title,
MATCH
        (MyTable.Text)
AGAINST
        ('foobar' IN NATURAL LANGUAGE MODE) AS score
FROM
        MyTable
HAVING
        score > 0
ORDER BY
        score DESC;

Из-за ограничений префикса эта ошибка произойдет. 767 байт - это заявленное ограничение префикса для таблиц InnoDB в версиях MySQL до 5.7 . Это длина 1000 байтов для таблиц MyISAM. В версии MySQL 5.7 и выше этот предел был увеличен до 3072 байт.

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

SET GLOBAL innodb_file_format=Barracuda;
SET GLOBAL innodb_file_per_table=on;
SET GLOBAL innodb_large_prefix=on;

Пожалуйста, проверьте, если sql_mode как

sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

если это так, измените на

sql_mode=NO_ENGINE_SUBSTITUTION

ИЛИ ЖЕ

перезагрузите сервер, изменив файл my.cnf (добавив следующее)

innodb_large_prefix=on

У меня есть изменения с varchar на nvarchar, у меня работает.

Другие вопросы по тегам