625

当我执行以下命令时:

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

我收到此错误消息:

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

关于 column1 和 column2 的信息:

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
4

36 回答 36

573

MySQL 5.6 版(和之前的版本)中的767 字节是 InnoDB 表的规定前缀限制。MyISAM 表的长度为 1,000 字节。在 MySQL 5.7 版(及更高版本)中,此限制已增加到3072 字节。

您还必须注意,如果您在编码的大字符或varchar字段上设置索引,则utf8mb4必须将 767 字节(或 3072 字节)的最大索引前缀长度除以 4,得到191。这是因为一个utf8mb4字符的最大长度是四个字节。对于一个utf8字符,它将是三个字节,导致最大索引前缀长度为255(或减去空终止符,254 个字符)。

您可以选择的一种选择是对您的VARCHAR字段设置下限。

另一种选择(根据对这个问题的回应)是获取列的子集而不是整个数量,即:

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

根据需要进行调整以获取应用密钥,但我想知道是否值得审查有关此实体的数据模型以查看是否有可能进行改进,这将允许您在不达到 MySQL 限制的情况下实施预期的业务规则.

于 2009-11-29T03:52:38.547 回答
426

如果有人在尝试将UNIQUE索引放在VARCHAR(256)字段上时遇到 INNODB / Utf-8 问题,请将其切换到VARCHAR(255). 似乎255是限制。

于 2013-05-29T17:19:13.613 回答
421

当你达到极限时。设置以下。

  • INNODButf8 VARCHAR(255)
  • INNODButf8mb4 VARCHAR(191)
于 2015-07-17T11:12:49.573 回答
148

MySQL 假设字符串中每个字符的字节数是最坏的情况。对于 MySQL 'utf8' 编码,每个字符 3 个字节,因为该编码不允许超过U+FFFF. 对于 MySQL 'utf8mb4' 编码,它是每个字符 4 个字节,因为这就是 MySQL 所说的实际 UTF-8。

因此,假设您使用的是“utf8”,您的第一列将占用 60 个字节的索引,而第二列将占用 1500 个字节。

于 2014-03-19T19:15:27.983 回答
57

在查询之前运行此查询:

SET @@global.innodb_large_prefix = 1;

这会将限制增加到3072 bytes.

于 2014-04-04T21:05:47.987 回答
55

Laravel 框架解决方案

根据Laravel 5.4.* 文档boot您必须在文件的方法中设置默认字符串长度app/Providers/AppServiceProvider.php,如下所示:

use Illuminate\Support\Facades\Schema;

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

Laravel 5.4.* 文档给出了这个修复的解释:

Laravelutf8mb4默认使用字符集,其中包括支持在数据库中存储“表情符号”。如果您运行的 MySQL 版本早于 5.7.7 版本或 MariaDB 版本早于 10.2.2 版本,您可能需要手动配置迁移生成的默认字符串长度,以便 MySQL 为其创建索引。您可以通过Schema::defaultStringLengthAppServiceProvider.

或者,您可以innodb_large_prefix为您的数据库启用该选项。有关如何正确启用此选项的说明,请参阅数据库的文档。

于 2017-07-23T20:35:51.193 回答
40

你使用什么字符编码?某些字符集(如 UTF-16 等)每个字符使用多个字节。

于 2009-11-29T03:21:27.350 回答
31

我认为 varchar(20) 只需要 21 个字节,而 varchar(500) 只需要 501 个字节。所以总字节数为 522,小于 767。那为什么我会收到错误消息?

UTF8 需要每个字符 3 个字节来存储字符串,因此在您的情况下 20 + 500 个字符 = 20*3+500*3 = 1560个字节,这超过了允许的767个字节。

UTF8 的限制是 767/3 = 255 个字符,对于每个字符使用 4 个字节的 UTF8mb4,它是 767/4 = 191 个字符。


如果您需要使用比限制更长的列,有两种解决方案可以解决此问题:

  1. 使用“更便宜”的编码(每个字符需要更少字节的编码)
    在我的情况下,我需要在包含文章 SEO 字符串的列上添加唯一索引,因为我只使用[A-z0-9\-]字符进行 SEO,我使用latin1_general_ci的每个字符只使用一个字节所以列可以有 767 个字节的长度。
  2. 从您的列创建哈希并仅在该列上使用唯一索引
    对我来说,另一个选择是创建另一个存储 SEO 哈希的列,该列将具有UNIQUE确保 SEO 值唯一的键。我还将KEY向原始 SEO 列添加索引以加快查找速度。
于 2016-12-23T09:03:00.207 回答
30

在您的导入文件中替换utf8mb4为。utf8

在此处输入图像描述

于 2019-07-17T23:56:47.097 回答
28

此处的许多用户已经回答了有关您收到错误消息的原因的答案。我的回答是关于如何修复和使用它。

从此链接参考。

  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 导出到另一台服务器(例如从 localhost 到真实主机)并且您不能在该服务器中使用 MySQL 命令行。你不能让它在那里工作。

于 2016-07-30T16:07:12.070 回答
22
Specified key was too long; max key length is 767 bytes

latin-1您收到该消息是因为仅当您使用字符集时 1 个字节才等于 1 个字符。如果使用utf8,则在定义键列时每个字符将被视为 3 个字节。如果使用utf8mb4,则在定义键列时每个字符将被视为 4 个字节。因此,您需要将关键字段的字符限制乘以 1、3 或 4(在我的示例中)以确定关键字段尝试允许的字节数。如果您使用的是 uft8mb4,则只能为原生 InnoDB 主键字段定义 191 个字符。只是不要违反 767 字节。

于 2015-10-13T12:49:10.020 回答
17

您可以添加一列 md5 的长列

于 2011-02-25T04:18:34.310 回答
17

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#767_limit_in_innodb_indexes

于 2018-10-10T22:23:04.590 回答
15

我解决了这个问题:

varchar(200) 

替换为

varchar(191)

所有超过 200 个的唯一或主 varchar 键将它们替换为 191 或将它们设置为文本。

于 2018-04-09T06:48:43.637 回答
14

我们在尝试使用 utf8mb4 向 VARCHAR(255) 字段添加 UNIQUE 索引时遇到了这个问题。虽然这里已经很好地概述了这个问题,但我想为我们如何解决这个问题添加一些实用的建议。

使用 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 个长度的字段。

于 2016-12-15T23:59:56.720 回答
14

这是我的原始答案:

我只是删除数据库并像这样重新创建,错误就消失了:

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

但是,它并不适用于所有情况。

这实际上是在具有字符集utf8(或 utf8mb4)的 VARCHAR 列上使用索引的问题,其中 VARCHAR 列具有超过一定长度的字符。在 的情况下utf8mb4,该特定长度为 191。

有关如何在 MySQL 数据库中使用长索引的更多信息,请参阅本文中的长索引部分:http: //hanoian.com/content/index.php/24-automate-the-converting-a-mysql-database-字符集到 utf8mb4

于 2017-02-15T04:50:25.457 回答
14

对于 laravel 5.79.0

要遵循的步骤

  1. App\Providers\AppServiceProvider.php
  2. 将此添加到 use Illuminate\Support\Facades\Schema;顶部的提供程序。
  3. 在 Boot 函数里面添加这个Schema::defaultStringLength(191);

这一切,享受。

于 2018-06-26T09:09:21.880 回答
9

为了解决这个问题,这对我来说就像一个魅力。

ALTER DATABASE dbname CHARACTER SET utf8 COLLATE utf8_general_ci;
于 2019-04-12T12:48:20.660 回答
8

我对此主题进行了一些搜索,最后得到了一些自定义更改

对于 MySQL 工作台 6.3.7 版本图形界面可用

  1. 启动 Workbench 并选择连接。
  2. 转到管理或实例并选择选项文件。
  3. 如果 Workbench 询问您读取配置文件的权限,然后按两次 OK 允许它。
  4. 在中心位置管理员选项文件窗口来了。
  5. 转到 InnoDB 选项卡并检查 innodb_large_prefix 如果它未在 General 部分中选中。
  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. 完毕
于 2016-09-29T10:18:35.217 回答
6

更改您的排序规则。你可以使用支持几乎所有的utf8_general_ci

于 2017-05-29T09:15:20.027 回答
6

索引长度和 MySQL / MariaDB


Laravel 默认使用utf8mb4 字符集,其中包括支持在数据库中存储“表情符号”。如果您运行的 MySQL 版本早于 5.7.7 版本或 MariaDB 版本早于 10.2.2 版本,您可能需要手动配置迁移生成的默认字符串长度,以便 MySQL 为其创建索引。您可以通过在AppServiceProvider中调用Schema::defaultStringLength方法来配置它:

use Illuminate\Support\Facades\Schema;

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

或者,您可以为您的数据库启用 innodb_large_prefix 选项。有关如何正确启用此选项的说明,请参阅数据库的文档。

来自博客的参考: https ://www.scratchcode.io/specified-key-too-long-error-in-laravel/

来自官方 laravel 文档的参考: https ://laravel.com/docs/5.7/migrations

于 2019-02-21T09:32:13.493 回答
5

只是在创建表时更改utf8mb4为解决了我的问题。utf8例如:CREATE TABLE ... DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;CREATE TABLE ... DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

于 2018-07-21T15:31:14.630 回答
5

这解决了我的问题

ALTER DATABASE dbname CHARACTER SET utf8 COLLATE utf8_general_ci;

于 2020-07-07T16:16:21.893 回答
4

根据下面给出的列,这 2 个变量字符串列正在使用utf8_general_ci排序规则(utf8隐含 charset)。

在 MySQL 中,utf8charset 每个字符最多使用3 个字节。因此,它需要分配 500*3=1500 字节,这比 MySQL 允许的 767 字节大得多。这就是您收到此 1071 错误的原因。

换句话说,您需要根据字符集的字节表示来计算字符数,因为并非每个字符集都是单字节表示(如您所假设的那样)utf8。MySQL 中的 IE 每个字符最多使用 3 个字节,767/3≈255字符,对于utf8mb4,最多 4 字节表示,767/4≈191 个字符。

众所周知,MySQL

column1 varchar(20) utf8_general_ci
column2  varchar(500) utf8_general_ci
于 2015-12-02T18:20:03.937 回答
3

就我而言,当我使用 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 字节”简单消失了。

于 2018-05-14T18:28:44.603 回答
2

请检查是否sql_mode

sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

如果是,请更改为

sql_mode=NO_ENGINE_SUBSTITUTION

或者

重新启动您的服务器,更改您的 my.cnf 文件(如下所示)

innodb_large_prefix=on
于 2014-06-04T16:25:55.863 回答
2

我发现此查询可用于检测哪些列的索引违反了最大长度:

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')
于 2017-11-16T10:21:38.397 回答
2

由于前缀限制,将发生此错误。767 字节是 5.7 之前的 MySQL 版本中 InnoDB 表的声明前缀限制。MyISAM 表的长度为 1,000 字节。在 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;
于 2019-01-18T15:58:10.440 回答
2

问题

MySQL 中有最大密钥长度限制。

  • InnoDB — 最大密钥长度为 1,536 字节(对于 8kb 页面大小)和 768 字节(对于 4kb 页面大小)(来源:Dev.MySQL.com)。
  • MyISAM — 最大密钥长度为 1,000 字节(来源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,如果您使用其他模式之一,它将进行部分词匹配。在此处查看有关全文选项的更多信息: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;
于 2021-07-19T15:09:59.320 回答
1

我有从 varchar 到 nvarchar 的变化,对我有用。

于 2020-10-22T17:01:01.420 回答
1

对于这个问题,我自己的解决方案比降低表的 VARCHAR 大小更简单,危险性更小。

情况:运行 Plesk Obsidian 18.0.37 和 MariaDB 5.5 的 CentOS 7 服务器。我试图从运行 MariaDB 10.1 的服务器导入 MySQL 转储。

解决方案从 MariaDB 5.5 升级到 10.6。

这些步骤大致基于本指南这个

  1. mysqldump -u admin -p`cat /etc/psa/.psa.shadow` --all-databases --routines --triggers > /root/all-databases.sql
  2. systemctl stop mariadb
  3. cp -a /var/lib/mysql/ /var/lib/mysql_backup
  4. 根据官方指南
    配置 MariaDB 存储库 确保满足此处详述的 Plesk 最低版本要求
  5. yum install MariaDB-client MariaDB-server MariaDB-compat MariaDB-shared
  6. systemctl start mariadb
    在我的情况下,服务器无法在此处启动并出现错误:“无法启动服务器:绑定 TCP/IP 端口。出现错误:22:无效参数”。
    修复方法是替换bind-address如下/etc/my.cnf并重新运行命令:
    [mysqld]
    # OLD (broken)
    #bind-address = ::ffff:127.0.0.1
    # NEW
    bind-address = 127.0.0.1
    
  7. MYSQL_PWD=`cat /etc/psa/.psa.shadow` mysql_upgrade -uadmin
  8. plesk sbin packagemng -sdf
  9. rm -f /etc/init.d/mysql
  10. systemctl daemon-reload
于 2021-09-29T04:08:11.627 回答
0

如果您正在创建类似的东西:

CREATE TABLE IF NOT EXISTS your_table (
  id int(7) UNSIGNED NOT NULL AUTO_INCREMENT,
  name varchar(256) COLLATE utf8mb4_bin NOT NULL,
  PRIMARY KEY (id),
  UNIQUE KEY name (name)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1 ROW_FORMAT=FIXED;

它应该是这样的

CREATE TABLE IF NOT EXISTS your_table (
      id int(7) UNSIGNED NOT NULL AUTO_INCREMENT,
      name varchar(256) COLLATE utf8mb4_bin NOT NULL,
      PRIMARY KEY (id)
    ) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1 ROW_FORMAT=FIXED;

但是您需要从代码中检查该列的唯一性或添加一个新列作为 varchar 列的 MD5 或 SHA1

于 2014-09-20T12:06:58.657 回答
-1

对我来说,通过将列大小限制为 200 来更改主键/唯一键组合后,“#1071 - 指定键太长;最大键长度为 767 字节”的问题得到了解决。

ALTER TABLE `mytable` ADD UNIQUE (
`column1` (200) ,
`column2` (200)
);
于 2016-03-18T16:36:48.317 回答
-2

如果您innodb_log_file_size最近进行了更改,请尝试恢复以前有效的值。

于 2015-08-05T11:30:57.423 回答
-2

我对这个问题的解决方法是添加一个选项作为第三个参数:charset

queryInterface.createTable(
  tableName,
  { /*... columns*/ },
  { charset: 'utf8' } 
)

否则 sequelize 会将表创建为utf8mb4

于 2019-03-02T20:05:37.630 回答
-4

将抱怨索引字段的 CHARSET 更改为“latin1”,
即 ALTER TABLE tbl CHANGE myfield myfield varchar(600) CHARACTER SET latin1 DEFAULT NULL;
latin1 为一个字符占用一个字节而不是四个

于 2013-12-27T13:14:25.627 回答