MySQL错误#1071 -指定的键太长;最大密钥长度为767字节

执行以下命令时:

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

我得到了这个错误信息:

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

columnn1和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
855819 次浏览

您使用的是什么字符编码?有些字符集(如UTF-16等等)每个字符使用一个以上的字节。

767字节的MySQL版本5.6(和以前的版本),是InnoDB表的声明的前缀限制。MyISAM表有1000字节长。这个限制已经增加到3072字节MySQL 5.7版本(或更高)。

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

一个选择是在VARCHAR字段上设置下限。

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

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

根据需要进行调整以获得要应用的键,但我想知道是否值得检查关于这个实体的数据模型,看看是否有可能进行改进,从而允许您在不触及MySQL限制的情况下实现预期的业务规则。

您可以添加长列的md5列

如果有人对InnoDB和utf8字符集有问题,试图将UNIQUE索引放在VARCHAR(256)字段上,请将其切换到VARCHAR(255)。255似乎是限制。

修改抱怨索引字段的CHARSET为"latin1"
例如:ALTER TABLE tbl CHANGE myfield myfield varchar(600) CHARACTER SET latin1 DEFAULT NULL Latin1一个字符占用一个字节,而不是四个

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

因此,假设您使用'utf8',那么您的第一列将占用索引的60字节,第二列将占用索引的1500字节。

在查询之前运行此查询:

SET @@global.innodb_large_prefix = 1;

这将增加限制到3072 bytes

请检查sql_mode是否像

sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

如果是,则更改为

sql_mode=NO_ENGINE_SUBSTITUTION

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

innodb_large_prefix=on

如果你要创建如下内容:

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 # eyz0 # eyz1
  • Innodb # eyz0 # eyz1

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

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

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

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

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

众所周知,MySQL

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

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

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

关于为什么你会得到错误消息的答案已经被很多用户回答了。我的答案是如何修复和使用它。

参考这个链接

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

数据库修改

  1. # EYZ0

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

  1. # EYZ0

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

  1. 去phpMyAdmin或类似的数据库,以便于管理。>选择数据库>查看表结构 >执行“操作”页签。将ROW_FORMAT更改为动态并保存更改。
  2. 去表的结构标签>点击独特的按钮。
  3. 完成了。现在应该没有错误了。

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

我在这个话题上做了一些搜索,最后得到了一些自定义更改

MySQL工作台6.3.7版本有图形界面

  1. 启动Workbench并选择连接。
  2. 转到管理或实例并选择选项文件。
  3. 如果Workbench要求您允许读取配置文件,然后按OK两次。
  4. 在中心位置的管理员选项文件窗口出现。
  5. 进入InnoDB选项卡,如果在General部分没有检查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. .
  4. 现在执行命令 mysql -u userName -p databasescheema 现在它要求输入相应用户的密码。 提供密码并进入mysql提示符
  5. 我们必须设置一些全局设置,依次输入以下命令 设置全局innodb_large_prefix=on; 设置全局innodb_file_format=barracuda; 设置全局innodb_file_per_table=true
  6. 现在在最后,我们必须改变所需表的ROW_FORMAT默认为COMPACT,我们必须将其设置为DYNAMIC。
  7. 使用以下命令 alter table table_name ROW_FORMAT=DYNAMIC
  8. 完成

我们在尝试使用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:

Sequel Pro截图显示索引截断在191个字符

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

那么,为什么MySQL为常规索引自动截断索引,但抛出一个显式错误时,试图这样做的唯一索引?好吧,为了让MySQL能够判断插入或更新的值是否已经存在,它需要索引整个值,而不仅仅是它的一部分。

最后,如果您想在一个字段上拥有唯一的索引,那么该字段的整个内容必须适合该索引。对于utf8mb4,这意味着将VARCHAR字段长度减少到191个字符或更少。如果这个表或字段不需要utf8mb4,那么可以将其删除回utf8,并能够保留255长度的字段。

我认为varchar(20)只需要21个字节,而varchar(500)只需要 需要501字节。所以总字节数是522,小于767。那么,为什么 我收到错误信息了吗?< / p >

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

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


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

    <李> # EYZ0 < br > 在我的情况下,我需要在列上添加唯一索引,包含文章的SEO字符串,因为我只使用[A-z0-9\-]字符进行SEO,我使用了latin1_general_ci,每个字符只使用一个字节,所以列可以有767字节的长度。李< / > <李> # EYZ0 < br > 我的另一个选择是创建另一个列,存储SEO的散列,这个列将有UNIQUE键,以确保SEO值是唯一的。我还会在原始SEO列中添加KEY索引,以加快查找速度

以下是我最初的回答:

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

drop database if exists rhodes;创建数据库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-character-set-to-utf8mb4

更改排序规则。您可以使用支持几乎所有功能的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')

我解决了这个问题:

varchar(200)

取而代之的是

varchar(191)

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

在我的例子中,我在使用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字节。

对于laravel 5.79.0

应遵循的步骤

  1. 转到App\Providers\AppServiceProvider.php
  2. 将此添加到顶部的提供者use Illuminate\Support\Facades\Schema;
  3. 在Boot函数中添加Schema::defaultStringLength(191);

就这些,好好享受吧。

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

5解决方法:

在5.7.7中提高了限制(MariaDB 10.2.2?)。并且可以通过5.6(10.1)中的一些工作来增加它。

如果你因为试图使用字符集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)

——# EYZ0

由于前缀限制,将发生此错误。在5.7之前的MySQL版本中,对于InnoDB表的前缀限制是767字节。MyISAM表有1000字节长。在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;

索引长度&安培;MySQL / MariaDB


Laravel默认使用utf8mb4字符设置,其中包括支持将“emojis”存储在数据库中。如果您运行的MySQL版本比5.7.7版本更老,或者MariaDB版本比10.2.2版本更老,您可能需要手动配置由迁移生成的默认字符串长度,以便MySQL为它们创建索引。您可以通过调用AppServiceProvider:中的模式:defaultStringLength方法来配置它

use Illuminate\Support\Facades\Schema;


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

或者,你可以为你的数据库启用innodb_large_prefix选项。参考您的数据库文档,以获得关于如何正确启用此选项的说明。

# EYZ0 # EYZ1

# EYZ0 # EYZ1

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

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

否则sequelize将创建表为utf8mb4

为了解决这个问题,这个方法对我很有效。

ALTER DATABASE dbname CHARACTER SET utf8 COLLATE utf8_general_ci;

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

enter image description here

但请注意,utf8字符集已弃用,它不支持所有的Unicode字符,例如表情符号,所以如果这样做,您将失去对Unicode的完全支持。

这解决了我的问题

ALTER DATABASE dbname CHARACTER SET utf8 COLLATE

我从varchar变成了nvarchar,对我有用。

这个问题

MySQL中有最大键长度限制。

  • InnoDB -最大密钥长度是1536字节(8kb页面大小)和768字节(4kb页面大小)(来源:Dev.MySQL.com)。
  • MyISAM -最大密钥长度是1000字节(源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;

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

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

# EYZ0: # EYZ1

步骤大致基于本指南这一个:

  1. # EYZ0
  2. # EYZ0
  3. # EYZ0
  4. 根据官方指南
    配置MariaDB存储库 请确保您符合Plesk的最低版本要求,详细编号在这里
  5. # EYZ0
  6. <李> # EYZ0
    在我的情况下,服务器无法启动这里的错误:"不能启动服务器:绑定在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. # EYZ0
  8. # EYZ0
  9. # EYZ0
  10. # EYZ0

好吧,在我的情况下,我必须将数据库文件从mySQL 5.7恢复到mySQL 5.6,我遇到了这个问题。

根本原因是版本不兼容,并且索引的某些列长于191(默认值为255)。

所以解决方案很简单:使所有“索引列”的长度;小于191的数字(如180)

enter image description here