错误:指定的密钥太长;最大密钥长度为1000字节

时间:2012-08-07 14:08:48

标签: mysql

错误:

  

1071 - 指定的密钥太长;最大密钥长度为1000字节

CREATE TABLE `phppos_modules_actions` (
  `action_id` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL ,
  `module_id` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL ,
  `action_name_key` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL ,
  `sort` INT NOT NULL ,
  PRIMARY KEY ( `action_id` , `module_id` )
) ENGINE = INNODB CHARACTER SET utf8 COLLATE utf8_unicode_ci;

我知道错误的发生是因为255x2x3(每个字符3个字节)

所有安装都不会发生这种情况。我可以更改什么设置?

9 个答案:

答案 0 :(得分:11)

NO_ENGINE_SUBSTITUTION在INNODB未激活,组合错误

时被禁用
  • 直到MySql 5.5,sqlmode默认为空字符串,表示默认情况下未设置sqlmode NO_ENGINE_SUBSTITUTION

根据MySql文档(参见https://dev.mysql.com/doc/refman/5.6/en/sql-mode.html#sqlmode_no_engine_substitution),这是sqlmode NO_ENGINE_SUBSTITUTION的含义:

  

当a时控制默认存储引擎的自动替换   诸如CREATE TABLE或ALTER TABLE之类的语句指定存储   已禁用或未编译的引擎。

     

因为存储引擎在运行时可以插入,所以不可用   引擎的处理方式相同:

     

禁用NO_ENGINE_SUBSTITUTION后,对于CREATE TABLE,默认值为   使用发动机,如果需要发动机,则发出警告   不可用。对于ALTER TABLE,会发生警告而表格不会发生   改变。

     

启用NO_ENGINE_SUBSTITUTION后,会发生错误,表格为   如果所需的引擎不可用,则不会创建或更改。

  • 所以:如果NO_ENGINE_SUBSTITUTION被禁用且INNODB关闭,如果在CREATE TABLE语句中指定INNODB,MySql也将切换到MYISAM。
  • 如果您正在为MYISAM创建的表格正常,则只会收到警告并创建表格。这不是你的情况,你的创建语句包含一个超出MYISAM 1000字节限制的索引,然后创建失败,错误1071报告MYISAM的错误。那是因为工作引擎是MYISAM,而不是INNODB。

PROOF

MySql版本5.1.56社区

案例1:

Options in my.cnf  
sql-mode=""  
default-storage-engine=MYISAM  
skip-innodb uncommented (without#) 

Return on execution of your create statement:
Error Code: 1071. Specified key was too long; max key length is 1000 bytes

Explanation: INNODB is not active, the engine is automatically switched to MYISAM  
that returns this error as they key is longer than MYISAM 1000 bytes limit.  
The key length is: 
2 fields x 255 char x 3 bytes utf8 encoding + 2 x 1 length byte = 1532 bytes   

案例2:

Options in my.cnf
sql-mode="NO_ENGINE_SUBSTITUTION"
default-storage-engine=MYISAM
skip-innodb uncommented (without#)

Return on execution of your create statement:
Error Code: 1286. Unknown table engine 'INNODB'

Explanation: INNODB is not active but the engine substitution is not permitted
by sql mode therefore the DB returns an error about the attempt of using a disabled engine. 

案例3:

Options in my.cnf
sql-mode="NO_ENGINE_SUBSTITUTION"
default-storage-engine=MYISAM
skip-innodb commented (with#)

Return on execution of your create statement:
Table creation OK!

Explanation: INNODB is active (skip-innodb commented) and it is used also if      
the default engine is MYISAM.

要重现测试,请在my.cnf中每次更改后重新启动MySql。

由于默认情况下MySql 5.6版sqlmode不再为空且包含NO_ENGINE_SUBSTITUTION,而且INNODB是默认引擎,因此很难满足错误。

其他测试

没有其他方法可以重现错误:

Error Code: 1071. Specified key was too long; max key length is 1000 bytes 

在尝试创建INNODB表时已找到。

在INNODB中,您有两种ERROR 1071:

Error Code: 1071. Specified key was too long; max key length is 767 bytes

这与innodb_large_prefix ON或OFF无关,但仅与用作索引的单个VARCHAR列的大小有关。

Mysql存储varchar utf8,包含3个字节加1个字节,长度为255个字符,之后为2个字符(请参阅:http://dev.mysql.com/doc/refman/5.7/en/storage-requirements.html),因此如果您尝试使用VARCHAR(256)utf8设置密钥,则会得到:< / p>

256 x 3 + 2 = 770 bytes

并且您得到上一个错误,因为InnoDB表的单个列的最大密钥长度为767个字节。 VARCHAR(255)没问题,因为:

255 x 3 + 1 = 766 bytes

我在四个安装的Mysql,版本5.1.56,5.5.33,5.6和5.7上进行了测试,并且已经确认。您的查询与VARCHAR(255)没有问题,VARCHAR(256)问题:

Error Code: 1071. Specified key was too long; max key length is 767 bytes

正如您所看到的消息不同,因为它是INNODB消息而不是MYISAM消息!

INNODB表的另一种ERROR 1071是:

Error Code: 1071. Specified key was too long; max key length is 3072 bytes

这与具有多列的键有关。要启用这些键,您需要将innodb_large_prefix设置为on。 无论如何,如果你试图运行这样的东西:

CREATE TABLE `phppos_modules_actions` (
  `action_id` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL ,
  `module_id` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL ,
  `action_name_key` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL ,
  `action_name_key1` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL ,
  `action_name_key2` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL ,
  `sort` INT NOT NULL ,
  PRIMARY KEY ( `action_id` , `module_id`, `action_name_key`, `action_name_key1`, `action_name_key2` )
) ENGINE = INNODB CHARACTER SET utf8 COLLATE utf8_unicode_ci;

使用5个VARCHAR(255)utf8列的密钥,即3830字节,您将遇到:

Error Code: 1071. Specified key was too long; max key length is 3072 bytes

外来假设

在寻找原因的过程中,我制定并测试了不同且非常奇怪的假设:

行格式

经过测试的REDUNDANT,COMPACT,COMPRESS,DYNAMIC:对您的陈述创建表格没有影响。

FILE格式

经过测试的Antelope和Barracuda:使用您的陈述对表创建没有影响。

MySql内置

经过测试的32位和64位MySql:使用您的语句对表创建没有影响。

其他类似的失败

在这里,您可以在相同的情况下找到相同的错误:

https://www.drupal.org/node/2466287

我在PROOF中列出的3个测试情况中测试了该声明,并且它再现了与您完全相同的行为,因此我可以说问题是相同的。在这种情况下,他们切换到其他数据库,但问题是设置的混合,而不是数据库版本。

参考

这里给出了一篇非常好的与INNODB建立索引的文章:

http://mechanics.flite.com/blog/2014/07/29/using-innodb-large-prefix-to-avoid-error-1071/

警告:在创建索引超过1000的INNODB表后,通过在my.cnf中取消注释skip-innodb来禁用INNODB将不允许启动MySql服务

此致

答案 1 :(得分:3)

我现在没有多大的灵活性,但这里有一些选择:

  • 将MySQL版本升级到最新版本(我测试了代码 MySQL 5.5.25并没有错误。)

  • 从utf8更改为latin1。

  • 减小构成主键的字段的大小。       创建单独的auto_increment主键字段以替换现有键。在第一个字段action_id(但不是第二个字段)

  • 上创建单独的索引

除了这些选项之外,你几乎陷入困境,因为在MySQL中没有可以更改的设置可以使索引键大于1000字节。

答案 2 :(得分:3)

在MySQL 5.6.3+中有一些限制(需要ROW_FORMAT=DYNAMICinnodb_file_format=BARRACUDAinnodb_file_per_table=true),您可以启用innodb_large_prefix以获得3072字节的密钥长度限制。

答案 3 :(得分:2)

你(意外?)InnoDB关闭了。它将表创建为MyISAM,整个键超过1000个字节。

检查是否有InnoDB的变量,以及是否有&#34; no_engine_substitution&#34; (如果存在于5.1.56中),请检查mysqld.err以确定它在启动时是否存在一些相关错误。

答案 4 :(得分:1)

它似乎与存储引擎有关。我的最终用户正在使用MyISAM来处理数据存储,导致错误。

答案 5 :(得分:1)

如上所述,索引的总长度太长。

简短的回答是,你不应该为这样长的VARCHAR列编制索引,因为索引会非常庞大​​且效率低下。

最佳做法是使用前缀索引,这样您只需索引数据的左子字符串。无论如何,你的大多数数据都会短于255个字符。

767字节是InnoDB表的规定前缀限制 - MyISAM表的长度为1,000字节。

根据对此问题的回复,您可以通过指定列的子集而不是整个数量来获取要应用的密钥。意味着您可以在定义索引时声明每列的前缀长度。

例如:

KEY `key_name` (`action_id`(50),`module_id`(50))

以下是更清楚地解释它的示例:我创建了一个表并在其中插入了一些数据。

CREATE TABLE `phppos_modules_actions` (
  `action_id` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL ,
  `module_id` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL ,
  `action_name_key` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL ,
  `sort` INT NOT NULL ,
  PRIMARY KEY ( `action_id`(50) , `module_id`(50) )
) ENGINE = INNODB CHARACTER SET utf8 COLLATE utf8_unicode_ci;

但是给定列的最佳前缀长度是多少?这是一个找出方法:

SELECT
 ROUND(SUM(LENGTH(`action_id`)<10)*100/COUNT(*),2) AS pct_length_10,
 ROUND(SUM(LENGTH(`action_id`)<20)*100/COUNT(*),2) AS pct_length_20,
 ROUND(SUM(LENGTH(`action_id`)<50)*100/COUNT(*),2) AS pct_length_50,
 ROUND(SUM(LENGTH(`action_id`)<100)*100/COUNT(*),2) AS pct_length_100
FROM `phppos_modules_actions`;


+---------------+---------------+---------------+----------------+
| pct_length_10 | pct_length_20 | pct_length_50 | pct_length_100 |
+---------------+---------------+---------------+----------------+
|         42.86 |         80.20 |        100    |         100    |
+---------------+---------------+---------------+----------------+

这告诉您80%的字符串少于20个字符,并且所有字符串都少于50个字符。因此,不需要索引超过50的前缀长度,当然也不需要索引255个字符的全长。

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

关于innodb中设置的一些替代方法可以在http://mechanics.flite.com/blog/2014/07/29/using-innodb-large-prefix-to-avoid-error-1071/

找到

答案 6 :(得分:0)

官方文件5.6

限制 InnoDB

  

InnoDB内部最大密钥长度为3500字节,但MySQL本身   将此限制为3072字节。此限制适用于   多列索引中的组合索引键。

http://dev.mysql.com/doc/refman/5.7/en/innodb-restrictions.html

MyISAM 存储引擎

  

最大密钥长度为1000个字节。这也可以改变   更改源和重新编译。对于密钥长于的情况   250字节,一个比默认值1024字节更大的密钥块大小   使用

http://dev.mysql.com/doc/refman/5.7/en/myisam-storage-engine.html

但是你的表被声明为InnoDB。所以我不知道该怎么想

这个old bug

结尾处还有一条线索
  

如果你需要这个,你应该看看MySQL 5.5以及从5.5.14(2011年7月)开始提供的innodb_large_prefix选项,因为它可能正在寻找你想要的东西:

     

“启用此选项以允许索引键前缀长于767字节   (最多3072字节),用于使用DYNAMIC和的InnoDB表   压缩行格式。 (创建这样的表也需要   选项值innodb_file_format = barracuda和   innodb_file_per_table = true。)请参见第13.3.15节“InnoDB的限制   “表”用于与索引键前缀关联的相关最大值   在各种环境下。

答案 7 :(得分:0)

最大密钥长度为1000个字节。这也可以通过更改源和重新编译来更改。对于长度大于250字节的密钥的情况,使用比默认值1024字节更大的密钥块大小。

答案 8 :(得分:0)

就我而言,MySQL是在没有InnoDB支持的情况下启动的。在数据库备份导入期间 - 尝试创建MyISAM表。

同时,MySQL重启期间控制台中没有显示任何错误 只有在检查MySQL日志文件时 - 才能找到它。

BTW错误与innodb_log_file_size = 4G有关,只有在将其更改为innodb_log_file_size = 1G时才会启用 - 启用了InnoDB支持。