#1071 – 指定的密钥太长; 最大密钥长度是767字节

当我执行下面的命令:

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 

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

你还必须知道,如果你在utf8mb4编码的big char或varchar字段上设置索引,你必须将最大索引前缀长度767字节(或3072字节)除以4得到191.这是因为一个utf8mb4字符的最大长度是四个字节。 对于utf8字符,它将是三个字节,导致最大索引前缀长度为254。

你有一个select是只放在你的VARCHAR领域的下限。

另一个select(根据对这个问题的回答 )是获得列的子集而不是全部数量,即:

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

在需要获取密钥时进行调整,但是我想知道是否值得查看关于此实体的数据模型,以查看是否有改进,使您可以实现预期的业务规则,而不受MySQL限制。

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

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

  • INNODB utf8 VARCHAR(255)
  • INNODB utf8mb4 VARCHAR(191)

MySQL假定string中每个字符的字节数最差。 对于MySQL'utf8'编码,每个字符3字节,因为该编码不允许U+FFFF以外的字符。 对于MySQL的“utf8mb4”编码,每个字符4个字节,因为这就是MySQL调用实际的UTF-8。

所以假设你使用的是'utf8',你的第一列将占用60个字节的索引,而你的第二个列将是1500。

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

在查询之前运行此查询:

 SET @@global.innodb_large_prefix = 1; 

这会将限制增加到3072 bytes

你可以添加一列长列的MD5

 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字节。

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

UTF8每个字符需要3个字节来存储string,所以在你的情况下,20 + 500个字符= 20 * 3 + 500 * 3 = 1560个字节, 超过允许的767个字节。

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


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

  1. 使用“便宜”编码(每个字符需要较less字节的编码)
    在我的情况下,我需要添加唯一索引列包含SEOstring的文章,因为我只使用[A-z0-9\-]字符的SEO,我使用latin1_general_ci只使用一个字节每个字符,所以列可以有长度为767个字节。
  2. 从你的列创build哈希,并使用唯一的索引
    另一个select是为了创build另一个列将存储search引擎优化的哈希,这个列将有UNIQUE关键,以确保SEO值是唯一的。 我也会将KEY索引加到原来的SEO列中以加快查找速度。

尝试使用utf8mb4将UNIQUE索引添加到VARCHAR(255)字段时遇到此问题。 虽然这个问题已经很好地概述了,但是我想为我们解决这个问题提供一些实际的build议。

使用utf8mb4时,字符数为4个字节,而在utf8下则为3个字节。 InnoDB数据库有索引只能包含767个字节的限制。 所以使用utf8时,可以存储255个字符(767/3 = 255),但是使用utf8mb4只能存放191个字符(767/4 = 191)。

您绝对可以使用utf8mb4为VARCHAR(255)字段添加常规索引,但会发生什么情况是索引大小会自动截断为191个字符,如unique_key

Sequel Pro屏幕截图显示索引被截断为191个字符

这很好,因为常规索引只是用来帮助MySQL更快速地search数据。 整个领域不需要被索引。

那么,为什么MySQL会自动为常规索引截断索引,但在尝试为唯一索引执行时会抛出一个明确的错误? 那么,为了让MySQL能够确定被插入或更新的值是否已经存在,它需要实际为整个值进行索引,而不仅仅是其中的一部分。

在一天结束的时候,如果你想在一个字段上有一个唯一的索引,那么这个字段的全部内容都必须符合索引。 对于utf8mb4,这意味着将VARCHAR字段长度减less到191个字符或更less。 如果你不需要这个表或字段的utf8mb4,你可以把它放回到utf8,并能够保持你的255长度的字段。

关于为什么你得到错误信息的答案已经被许多用户在这里回答。 我的答案是关于如何修复和使用它。

请参阅此链接 。

  1. 打开MySQL客户端(或MariaDB客户端)。 这是一个命令行工具。
  2. 它会问你的密码,input正确的密码。
  3. 使用此命令select您的数据库use my_database_name;

数据库改变了

  1. set global innodb_large_prefix=on;

查询OK,0行受影响(0.00秒)

  1. set global innodb_file_format=Barracuda;

查询OK,0行受影响(0.02秒)

  1. 转到您的数据库在phpMyAdmin或类似的东西,方便pipe理。 >select数据库>查看表结构 >转到操作选项卡。 >将ROW_FORMAT更改为DYNAMIC并保存更改。
  2. 转到表格结构选项卡>单击唯一button。
  3. 完成。 现在应该没有错误。

这种修复的问题是,如果你将数据库导出到另一台服务器上(例如从本地主机到真实主机),并且你不能在该服务器上使用MySQL命令行。 你不能在那里工作。

按照Lravel 5.4。* 文档 ; 你必须在里面设置默认的string长度

的方法

应用程序/提供者/ AppServiceProvider.php

文件如下:

 use Illuminate\Support\Facades\Schema; public function boot() { Schema::defaultStringLength(191); } 

改变你的整理。 你可以使用支持几乎所有的utf8_general_ci

我对这个话题做了一些search,终于得到了一些自定义的改变

对于MySQL工作台6.3.7版本graphics交互阶段是可用的

  1. 开始工作台并select连接。
  2. 转到pipe理或实例并select选项文件。
  3. 如果Workbench要求您有权读取configuration文件,然后通过按两次确定来允许它。
  4. 在中心的地方pipe理员选项文件窗口来。
  5. 转到InnoDB选项卡并检查innodb_large_prefix是否在常规部分中未选中。
  6. 将innodb_default_row_format选项值设置为DYNAMIC。

对于低于6.3.7的版本,直接选项不可用,所以需要使用命令提示符

  1. 以pipe理员身份启动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. 我们必须设置一些全局设置,依次input下面的命令设置全局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. 完成

根据下面给出的列,这两个variablesstring列使用utf8_general_ci归类(隐含utf8字符集)。

在MySQL中, utf8字符集对每个字符最多使用3个字节 。 因此,它需要分配500 * 3 = 1500字节,这比MySQL允许的767字节大得多。 这就是为什么你得到这个1071错误。

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

这也是众所周知的MySQL

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

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

请检查sql_mode是否像 – sql_mode = NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

如果是,则更改sql_mode = NO_ENGINE_SUBSTITUTION

要么

重新启动你的服务器,改变你的my.cnf文件(放入以下内容) –

innodb_large_prefix =上

如果你正在创造类似的东西:

 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

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

这是我原来的答案:

我只是放弃数据库并重新创build,错误消失了:

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

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

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