ChatGPT解决这个技术问题 Extra ChatGPT

MySQL 错误 #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
因为它不是 520 字节,而是 2080 字节,远远超过 767 字节,所以可以执行 column1 varchar(20) 和 column2 varchar(170)。如果您想要一个字符/字节等值,请使用 latin1
我认为您的计算在这里有点错误。 mysql 使用 1 或 2 个额外字节来记录值长度:如果列的最大长度为 255 字节或更短,则为 1 字节,如果大于 255 字节,则为 2。 utf8_general_ci 编码每个字符需要 3 个字节,因此 varchar(20) 使用 61 个字节,varchar(500) 使用 1502 个字节,总共 1563 个字节
mysql> select maxlen, character_set_name from information_schema.character_sets where character_set_name in('latin1', 'utf8', 'utf8mb4');麦克斯 | character_set_name ------ | ------------------- 1 | latin1 ------ | ------------------- 3 | utf8 ------ | ------------------- 4 | utf8mb4
'如果你想要一个字符/字节等价物,请使用 latin1' 请不要这样做。 Latin1 真的,真的很烂。你会后悔的。

T
Top-Master

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

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

您可以选择的一种方法是在 VARCHAR 字段上设置下限。

另一种选择(根据 response to this issue)是获取列的子集而不是整个数量,即:

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

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


通过指定列的子集而不是整个数量来应用。一个很好的解决方案。
这并不能解释为什么远低于长度限制的字段超过了长度限制......
我已经尝试在上面缺少@Cerin 的信息中进行编辑,这显然也被其他人认为是缺失的,但由于更适合作为评论而被拒绝。对于那些试图理解为什么 500 + 20 > 767 的人,请参阅 Stefan Endrullis 对 Julien 回答的评论。
这可能是个问题。例如:我有字段名称 (255) 并在名称 (191) 处添加唯一的字段,因为我使用的是 utf8mb4。如果我让我的用户用“IJUE3ump5fiUuCi16jSofYS234MLschW4wsIktKiBrTPOTKBK6Vteh5pNuz1tKjy...aO500mlJs”添加他们的名字,而另一个用户用这个“IJUE3ump5fiUuCi16jSofYS234MLschW4wsIktKiBrTPOTKBK6Vteh50pmlJs”添加他们的名字......它应该通过验证,而不是卡在重复条目上。
索引限制为 767 字节,而不是字符。而且由于 Mysql 的 utf8mb4 字符集(世界其他地方称之为 utf8)需要(最多)4 bytes per character,因此您最多只能索引 VARCHAR(191)。 Mysql 的 utf8 字符集(世界其他地方称之为损坏)每个字符最多需要 3 个字节,所以如果你使用它(你 shouldn't),你最多可以索引 VARCHAR(255)
A
Aley

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

INNODB utf8 VARCHAR(255)

INNODB utf8mb4 VARCHAR(191)


这是最好的答案。简单、直截了当,还包括 utf8mb4 限制(这是新数据库最常用的编码,因为它接受表情符号/等)。
因为 767 / 4 ~= 191 和 767 / 3 ~= 255
在哪里以及如何设置它?
是的,在 CREATE TABLE 语句的末尾指定 ENGINE=InnoDB DEFAULT CHARSET=utf8 我能够拥有一个 VARCHAR(255) 主键。谢谢。
有人给他+1以超过限制191 :)
P
PinkTurtle

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


允许的字符数仅取决于您的字符集。 UTF8 每个字符最多可以使用 3 个字节,utf8mb4 最多可以使用 4 个字节,而 latin1 只能使用 1 个字节。因此,对于 utf8,您的密钥长度限制为 255 个字符,因为 3*255 = 765 < 767
这为我节省了很多挫折-谢谢。考虑到用户声称他们达到了 767b 限制来使用 InnoDB,这应该是 IMO 接受的答案。
正如 Stefan Endrullis 所说,这取决于字符集。如果您使用使用 3 个字节的 UTF8:255x3=765 低于 767 限制,而 256x3=768 更高。但是如果你使用 UTF8mb4 它的 255*4=1020,所以它不是一个真正的解决方案
这个答案是正确的。 但是如果 255 确实对您有用,则意味着您正在使用 Mysql utf8,不幸的是,它已损坏。它只能对基本多语言平面中的字符进行编码。您会遇到超出此范围的角色的问题。例如,我认为他们添加的那些表情符号字符不在其中。因此,不要切换到 VARCHAR(255),而是切换到 VARCHAR(191) 将编码切换到 utf8mb4(实际上只是 utf8,但 MySql 想保留。兼容)。
对我的多列唯一约束没有帮助。它也不适用于 OP 的多列唯一约束。 (总大小为 825 字节)
m
morganwahl

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

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


— 这大概意味着在使用 utf8mb4 时,我需要将它们设置为(最多)191,因为 191*4 = 764 < 767。
@Isaac 是的,确切地说,
我认为这可能是正确的答案,但您能否详细说明需要做些什么来纠正这样的问题?至少对于像我这样的 MySQL 菜鸟?
没有一种方法可以绕过这个索引限制。这里的问题是关于独特的约束;对于那些,您可以拥有一列无限长度的文本,另一列存储该文本的哈希(如 MD5),并将哈希列用于您的唯一约束。您必须确保您的程序在更改文本时保持哈希值是最新的,但是有多种方法可以轻松处理该问题。坦率地说,MySQL 应该自己实现这样的东西,所以你不必这样做;如果 MariaDB 内置了类似的东西,我不会感到惊讶。
非常长的 varchar 列上的唯一索引很少见。想想你为什么需要它,因为它可能是一个设计问题。如果您只是想要一个索引进行搜索,请考虑一些适合 191 个字符的“关键字”字段,或将文本拆分为简短描述和长/完整文本等。或者如果您确实需要全文搜索,请考虑使用专门的软件它,例如 Apache Lucene。
F
Fábio Batista

在查询之前运行此查询:

SET @@global.innodb_large_prefix = 1;

这会将限制增加到 3072 bytes


全局更改为 innodb_large_prefix 有什么缺点吗?那个数据库是“全球性的”还是所有数据库完全是全球性的?
仅在使用非标准行格式时适用。请参阅dev.mysql.com/doc/refman/5.5/en/…。具体来说,“对于使用 DYNAMIC 和 COMPRESSED 行格式的 InnoDB 表,启用此选项以允许索引键前缀长度超过 767 个字节(最多 3072 个字节)。”默认行格式不受影响。
这对我来说效果很好 - 可以在此处找到更多详细信息和指南:mechanics.flite.com/blog/2014/07/29/…
之后我们需要重新启动mysql服务器吗?
此答案中缺少重要的详细信息。 innodb_file_format 必须是 BARRACUDA 并且在表级别您必须使用 ROW_FORMAT=DYNAMICROW_FORMAT=COMPRESSED。请参阅上面的@cwd 评论和指南。
D
Daniel Böhmer

Laravel 框架解决方案

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

use Illuminate\Support\Facades\Schema;

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

此修复的说明,由 Laravel 5.4.* documentation 提供:

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


@BojanPetkovic 好吧,我刚遇到 Laravel 问题,这个答案实际上解决了我的问题。
这个答案很棒。但是有谁知道,为什么这确实有效?
@Bobby Laravel 文档在标题“索引长度和 MySQL / MariaDB”laravel.com/docs/5.4/migrations#creating-indexes 中给出了解释
@Bobby我已经更新了答案。我已经为此修复添加了 laravel 文档给出的解释。
我做了一些研究,发现了不同的观点。由于在用户表的电子邮件字段中使用了“唯一”索引,因此会出现此问题。所以简单地增加一个250的极限值就可以解决问题。这个值被添加到文件迁移中,它在代码“unique ()”的行上创建用户表,所以它看起来像这样: $table->string('email', 250)->unique ();
A
Amber

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


如果是UTF8,一个字符最多可以使用4个字节,所以20个字符列是20 * 4 + 1个字节,500个字符列是500 * 4 + 2个字节
对于它的价值,我只是遇到了同样的问题,从 utf8_general_ci 切换到 utf8_unicode_ci 为我解决了这个问题。我不知道为什么:(
对于具有 UNIQUE 索引的 VARCHAR(256) 列,更改排序规则对我没有影响,就像对 @Andresch 一样。但是,将长度从 256 减少到 255 确实解决了这个问题。我不明白为什么,因为每个字符 767 / max 4 bytes 最多会产生 191?
255*3 = 765; 256*3 = 768。看来您的服务器假设每个字符 3 个字节,@Arjan
@Greg:您是对的,但这应该详细说明:UTF-8 本身每个代码点使用 1-4 个字节。 MySQL 的“字符集”(真正的编码)有一个称为“utf8”的字符集,它能够对部分 UTF-8 进行编码,每个代码点使用 1-3 个字节,并且无法对 BMP 之外的代码点进行编码。它还包括另一个称为“utf8mb4”的字符集,每个代码点使用 1-4 个字节,并且能够编码所有 Unicode 代码点。 (utf8mb4 是 UTF-8,utf8 是 UTF-8 的奇怪版本。)
B
Buksy

我认为 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 个字符。

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

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


这成功了。我有 varchar(256),我不得不将其更改为 varchar(250)。
B
Bryan

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

https://i.stack.imgur.com/W1AoE.png


为什么要这样做呢?此外,如果这有任何缺点(我假设),你应该提到这一点
这意味着您的列将无法存储一些 Unicode 字符;最著名的表情符号。
如果您使用的是 laravel ...此解决方案将节省您的时间!
@AhmedOsama 这也适用于我。我也在使用 laravel (v9)。
T
Tim Kretschmer

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

请参阅 this link

打开 MySQL 客户端(或 MariaDB 客户端)。它是一个命令行工具。它将询问您的密码,输入正确的密码。使用此命令选择您的数据库 use my_database_name;

数据库已更改

设置全局 innodb_large_prefix=on;

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

设置全局 innodb_file_format=梭子鱼;

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

转到 phpMyAdmin 上的数据库或类似的东西以便于管理。 > 选择数据库 > 查看表结构 > 转到操作选项卡。 > 将 ROW_FORMAT 更改为 DYNAMIC 并保存更改。转到表的结构选项卡 > 单击唯一按钮。完毕。现在它应该没有错误了。

此修复的问题是,如果您将 db 导出到另一台服务器(例如从 localhost 到真实主机)并且您不能在该服务器中使用 MySQL 命令行。你不能让它在那里工作。


如果输入上述查询后仍然出错,请尝试转到“phpmyadmin”>将“排序规则”设置为您的偏好(对我来说,我使用“utf8_general_ci”)>单击应用(即使它已经是 utf8)
我没有使用与您的说明配合使用的工具,但我仍然投票支持它试图帮助人们实际解决问题。关于导致问题的原因有无穷无尽的解释,但关于如何实际解决问题的解释却非常少。
A
Anthony Rutledge
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 字节。


R
Rick James

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


R
Rick James

您可以添加一列 md5 的长列


请注意,这将不允许您对这些列进行范围扫描。 VARCHAR 上的前缀长度将允许您保持此特征,同时在索引中导致可能的虚假匹配(以及扫描和行查找以消除它们)(请参阅接受的答案)。 (这本质上是一个手动实现的哈希索引,遗憾的是 MySQL 不支持 InnoDB 表。)
我不能使用前缀索引,因为我需要保持与 H2 的兼容性以进行测试,并且发现使用哈希列效果很好。我强烈建议使用 SHA1 之类的抗碰撞功能而不是 MD5 来防止恶意用户创建碰撞。如果您的查询之一只检查哈希值,而不是完整的列值,则可能会利用索引冲突来泄漏数据。
A
Ammar Hussein

我解决了这个问题:

varchar(200) 

替换为

varchar(191)

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


这对我也有用。我有一个 varchar(250),但数据从来没有那么长。将其更改为 varchar(100)。感谢您的想法:)
M
ManojKiran Appathurai

对于 laravel 5.7 到 9.0

要遵循的步骤

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

这一切,享受。


也适用于 Laravel 5.8。
这是一个糟糕的解决方案。原因:索引并不意味着无限长。当您将唯一索引应用于某事物时,您希望该索引在大多数情况下都是固定的。这意味着您不应该使 email 之类的内容独一无二,但您应该对电子邮件进行哈希处理并使其独一无二。与原始字符串数据不同,散列是固定宽度的,可以被索引并使其唯一而不会出现问题。您没有了解问题,而是在传播无法扩展的糟糕做法。
m
maknz

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

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

您绝对可以使用 utf8mb4 为 VARCHAR(255) 字段添加常规索引,但会发生索引大小自动截断为 191 个字符的情况 - 就像这里的 unique_key

https://i.stack.imgur.com/3hGHE.png

这很好,因为常规索引只是用来帮助 MySQL 更快地搜索您的数据。整个字段不需要被索引。

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

归根结底,如果您想在某个字段上拥有唯一索引,则该字段的全部内容必须适合该索引。对于 utf8mb4,这意味着将 VARCHAR 字段长度减少到 191 个字符或更少。如果该表或字段不需要 utf8mb4,则可以将其放回 utf8 并能够保留 255 个长度的字段。


C
Châu Hồng Lĩnh

这是我的原始答案:

我只是删除数据库并像这样重新创建,错误消失了:drop database if exists rhodes;创建数据库 rhodes 默认 CHARACTER 设置 utf8 默认 COLLATE utf8_general_ci;

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

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

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


解决了 openmeetings 设置的问题(顺便说一句,你拯救了我的夜晚 :-)
N
Nick Pridorozhko

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

ALTER DATABASE dbname CHARACTER SET utf8 COLLATE utf8_general_ci;

我确认我的问题也是数据库排序规则。我对此没有任何解释。我使用 mysql v5.6.32,数据库排序规则是 utf8mb4_unicode_ci。
尝试使用 phpmyadmin 添加解释,请参阅 i.imgur.com/fJzm4oE.png
A
Adrian Cid Almaguer

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

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

启动 Workbench 并选择连接。转到管理或实例并选择选项文件。如果 Workbench 询问您读取配置文件的权限,然后按两次 OK 允许它。在中心位置管理员选项文件窗口来了。转到 InnoDB 选项卡并检查 innodb_large_prefix 如果它未在 General 部分中选中。将 innodb_default_row_format 选项值设置为 DYNAMIC。

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

以管理员身份启动 CMD。转到安装 mysql 服务器的目录大多数情况下,它位于“C:\Program Files\MySQL\MySQL Server 5.7\bin”,因此命令是“cd \”“cd Program Files\MySQL\MySQL Server 5.7\bin”。现在运行命令 mysql -u userName -p databasescheema 现在它要求输入各个用户的密码。提供密码并进入 mysql 提示符。我们要设置一些全局设置,一一输入下面的命令 set global innodb_large_prefix=on;设置全局 innodb_file_format=barracuda;设置全局 innodb_file_per_table=true;现在最后我们必须更改所需表的 ROW_FORMAT,默认情况下它的 COMPACT 我们必须将其设置为 DYNAMIC。使用以下命令 alter table table_name ROW_FORMAT=DYNAMIC;完毕


我找不到这个: 6. 将 innodb_default_row_format 选项值设置为 DYNAMIC。
如果我使用 set global innodb_default_row_format = DYNAMIC;,我会看到这条消息:错误 1193 (HY000): Unknown system variable 'innodb_default_row_format'
你是如何从工作台到 cmd 的?我是从工作台上做的,它直接有选项。
我从 CMD 执行此操作,因为我在 Workbench 中看不到该选项
我看到这个 var 是在 v5.7.9 中引入的问题,我有 v5.6.33 版本,谢谢
N
Nids Barthwal

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


“几乎”是一个很好的暗示,表明这不是一个长期的解决方案
M
Mayank Dudakiya

索引长度和 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


M
MajidJafari

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


S
Shamal Sabah

这解决了我的问题

ALTER DATABASE dbname CHARACTER SET utf8 COLLATE utf8_general_ci;


D
Devy

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

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

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

众所周知,MySQL

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

C
Cassio Seffrin

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


A
Andrew

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

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')

Y
YakovL

请检查 sql_mode 是否像

sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

如果是,请更改为

sql_mode=NO_ENGINE_SUBSTITUTION

或者

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

innodb_large_prefix=on

R
Rivers

由于前缀限制,将发生此错误。 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;

H
HoldOffHunger

问题

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

InnoDB — 最大密钥长度为 1,536 字节(对于 8kb 页面大小)和 768 字节(对于 4kb 页面大小)(来源:Dev.MySQL.com)。

MyISAM — 最大密钥长度为 1,000 字节(来源 Dev.MySQL.com)。

这些以字节为单位!因此,一个 UTF-8 字符可能需要超过一个字节才能存储到密钥中。

因此,您只有两个直接的解决方案:

仅索引文本类型的前 n 个字符。

创建全文搜索——文本中的所有内容都可以搜索,类似于 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 keyword,则不需要在字段名称周围使用反引号 (```)。

创建全文搜索

全文搜索将允许您搜索 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;

K
Khushi

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