#1071 - 指定密钥太长;最大密钥长度为767字节

时间:2009-11-29 03:18:17

标签: mysql byte varchar mysql-error-1071

当我执行以下命令时:

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

36 个答案:

答案 0 :(得分:396)

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

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

您可以选择在VARCHAR字段上设置下限。

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

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

调整,因为你需要获取密钥才能应用,但我想知道是否值得查看有关此实体的数据模型,看看是否有改进可以让你实现预期的业务规则而不需要MySQL限制。

答案 1 :(得分:383)

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

答案 2 :(得分:276)

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

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

答案 3 :(得分:145)

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

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

答案 4 :(得分:46)

在查询之前运行此查询:

SET @@global.innodb_large_prefix = 1;

这会将限制提高到3072 bytes

答案 5 :(得分:37)

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

答案 6 :(得分:31)

Laravel Framework解决方案

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

use Illuminate\Support\Facades\Schema;

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

此修补程序的说明,由Laravel 5.4.* documentation

提供
  

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

     

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

答案 7 :(得分:22)

  

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

UTF8需要每个字符3个字节来存储字符串,所以在你的情况下20 + 500个字符= 20 * 3 + 500 * 3 = 1560 字节是< strong> 允许 767 字节。

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

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

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

答案 8 :(得分:18)

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

请参阅this link

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

    数据库已更改

    1. set global innodb_large_prefix=on;
    2.   

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

      1. set global innodb_file_format=Barracuda;
      2.   

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

        1. 转到phpMyAdmin上的数据库或类似的东西,以便于管理。 &GT;选择数据库&gt;查看表结构&gt;转到操作标签。 &GT;将 ROW_FORMAT 更改为 DYNAMIC 并保存更改。
        2. 转到表格结构标签&gt;点击唯一按钮。
        3. 完成。现在它应该没有错误。
        4. 此修复的问题是如果将db导出到另一台服务器(例如从localhost导出到真实主机),并且您无法在该服务器中使用MySQL命令行。你无法在那里工作。

答案 9 :(得分:17)

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

答案 10 :(得分:15)

你可以添加长列md5的列

答案 11 :(得分:11)

尝试使用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 screenshot showing index truncated at 191 characters

这很好,因为常规索引仅用于帮助MySQL更快地搜索数据。整个字段不需要编入索引。

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

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

答案 12 :(得分:8)

这是我原来的答案:

  

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

     

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

答案 13 :(得分:6)

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

对于MySQL工作台6.3.7版本可以使用图形化阶段

  1. 启动Workbench并选择连接。
  2. 转到管理层或实例,然后选择选项文件。
  3. 如果Workbench要求您允许读取配置文件,然后按两次OK键允许它。
  4. 在中心位置管理员选项文件窗口出现。
  5. 转到InnoDB选项卡,如果未在“常规”部分中选中,请检查innodb_large_prefix。
  6. 将innodb_default_row_format选项值设置为DYNAMIC。
  7. 对于6.3.7以下的版本,直接选项不可用,因此需要使用命令提示符

    1. 以管理员身份启动CMD。
    2. 转到安装mysql服务器的导演大多数情况下它在 &#34; C:\ Program Files \ MySQL \ MySQL Server 5.7 \ bin&#34;所以命令是 &#34; cd \&#34; &#34; cd Program Files \ MySQL \ MySQL Server 5.7 \ bin&#34;。
    3. 现在运行命令 mysql -u userName -p databasescheema 现在它要求各个用户的密码。 提供密码并进入mysql提示符。
    4. 我们必须设置一些全局设置,逐个输入以下命令 set global innodb_large_prefix = on; set global innodb_file_format = barracuda; set global innodb_file_per_table = true;
    5. 现在最后我们必须改变所需表格的ROW_FORMAT默认情况下我们必须将它设置为DYNAMIC。
    6. 使用以下命令 alter table table_name ROW_FORMAT = DYNAMIC;
    7. 完成

答案 14 :(得分:6)

我用以下方法解决了这个问题:

varchar(200) 

替换为

varchar(191)

所有超过200的varchar用191替换它们或设置它们的文本。

答案 15 :(得分:5)

更改整理。您可以使用支持几乎所有

utf8_general_ci

答案 16 :(得分:5)

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

答案 17 :(得分:3)

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

答案 18 :(得分:2)

根据下面给出的列,这两个变量字符串列使用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

答案 19 :(得分:2)

这解决了我的问题

ALTER DATABASE dbname CHARACTER SET utf8 COLLATE utf8_general_ci;

答案 20 :(得分:2)

对于laravel 5.7或5.6

要遵循的步骤

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

所有,享受。

答案 21 :(得分:1)

就我而言,当我使用linux重定向输出/输入字符备份数据库时,我遇到了这个问题。因此,我按如下所述更改语法。 PS:使用linux或mac终端。

备份(没有&gt;重定向)

host $1 | cut -d' ' -f5

恢复(没有&lt;重定向)

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

错误&#34;指定密钥太长;最大密钥长度为767字节&#34;简单的消失了。

答案 22 :(得分:1)

索引长度和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选项。请参阅数据库的文档,以获取有关如何正确启用此选项的说明。

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

答案 23 :(得分:1)

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

答案 24 :(得分:1)

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

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

答案 25 :(得分:1)

请检查sql_mode是否类似

sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

如果是,请改为

sql_mode=NO_ENGINE_SUBSTITUTION

OR

重新启动服务器,更改my.cnf文件(放下)

innodb_large_prefix=on

答案 26 :(得分:0)

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

答案 27 :(得分: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

答案 28 :(得分:0)

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

enter image description here

答案 29 :(得分:0)

我从varchar更改为nvarchar,对我有用。

答案 30 :(得分:0)

问题

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 keyword,则字段名称周围不需要反引号 (```)。

创建全文搜索

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

答案 31 :(得分:-1)

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

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

答案 32 :(得分:-1)

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

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

否则,sequelize会将表创建为 utf8mb4

答案 33 :(得分:-1)

要解决此问题,这对我来说就像一种魅力。

ALTER DATABASE dbname CHARACTER SET utf8 COLLATE utf8_general_ci;

答案 34 :(得分:-1)

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

答案 35 :(得分:-3)

如果您运行Laravel(laravel现在默认为4字节Unicode导致这一点),您可以通过更改下一行来解决此问题 来自

的config / database.php
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',

 到

'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',