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


102

我知道有关此标题的问题已经回答过,但是请继续阅读。发布前,我已彻底阅读了关于此错误的所有其他问题/答案。

我收到以下查询的上述错误:

CREATE TABLE IF NOT EXISTS `pds_core_menu_items` (
  `menu_id` varchar(32) NOT NULL,
  `parent_menu_id` int(32) unsigned DEFAULT NULL,
  `menu_name` varchar(255) DEFAULT NULL,
  `menu_link` varchar(255) DEFAULT NULL,
  `plugin` varchar(255) DEFAULT NULL,
  `menu_type` int(1) DEFAULT NULL,
  `extend` varchar(255) DEFAULT NULL,
  `new_window` int(1) DEFAULT NULL,
  `rank` int(100) DEFAULT NULL,
  `hide` int(1) DEFAULT NULL,
  `template_id` int(32) unsigned DEFAULT NULL,
  `alias` varchar(255) DEFAULT NULL,
  `layout` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`menu_id`),
  KEY `index` (`parent_menu_id`,`menu_link`,`plugin`,`alias`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

有谁知道为什么以及如何解决它?问题是-相同的查询在我的本地计算机上运行完美,在我以前的主机上也运行良好。顺便说一句,它来自一个成熟的项目-phpdevshell-所以我猜这些家伙知道他们在做什么,尽管你永远都不知道。

任何线索表示赞赏。

我正在使用phpMyAdmin。

Answers:


170

正如@Devart所说,索引的总长度太长。

简短的答案是,无论如何您都不应该为这么长的VARCHAR列建立索引,因为索引将非常庞大且效率低下。

最佳实践是使用前缀索引,因此您仅索引数据的左子字符串。无论如何,您的大多数数据都会少于255个字符。

您可以在定义索引时为每列声明前缀长度。例如:

...
KEY `index` (`parent_menu_id`,`menu_link`(50),`plugin`(50),`alias`(50))
...

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

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

它告诉您列中不超过给定字符串长度的行的比例menu_link。您可能会看到如下输出:

+---------------+---------------+---------------+----------------+
| pct_length_10 | pct_length_20 | pct_length_50 | pct_length_100 |
+---------------+---------------+---------------+----------------+
|         21.78 |         80.20 |        100.00 |         100.00 |
+---------------+---------------+---------------+----------------+

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

PS:INT(1)INT(32)数据类型表示对MySQL的另一种误解。数字参数与存储或该列允许的值范围无关。 INT始终为4个字节,并且始终允许从-2147483648到2147483647之间的值。数字参数与显示期间的填充值有关,除非您使用该ZEROFILL选项,否则该参数无效。


17
非常感谢您的详细解释。除了解决问题之外,我还学到了一些有价值的东西。
CodeVirtuoso 2012年

确实,这对于查找应该设置索引长度的查询非常有用。多次使用它来确定索引的最佳长度。感谢你的分享!
Niraj Kumar

1
您的便捷查询中存在一个细微的错误,可以测量字符串的实际长度:您假设字符串全部存在;也就是说,他们都在那里。如果一些为空,它将使您的计算中断,并漏报短字符串。您要使用count([field_name])而不是count(*)。
D Mac

它只会使用第一个“前缀”索引。
瑞克·詹姆斯

28

此错误意味着索引长度index超过1000个字节。MySQL和存储引擎可能有此限制。我在MySQL 5.5上也遇到了类似的错误-'指定的密钥太长;运行此脚本时,最大密钥长度为3072字节:

CREATE TABLE IF NOT EXISTS test_table1 (
  column1 varchar(500) NOT NULL,
  column2 varchar(500) NOT NULL,
  column3 varchar(500) NOT NULL,
  column4 varchar(500) NOT NULL,
  column5 varchar(500) NOT NULL,
  column6 varchar(500) NOT NULL,
  KEY `index` (column1, column2, column3, column4, column5, column6)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

UTF8是多字节,并且密钥长度是以这种方式计算的-500 * 3 * 6 = 9000字节。

但是请注意,下一个查询有效!

CREATE TABLE IF NOT EXISTS test_table1 (
  column1 varchar(500) NOT NULL,
  column2 varchar(500) NOT NULL,
  column3 varchar(500) NOT NULL,
  column4 varchar(500) NOT NULL,
  column5 varchar(500) NOT NULL,
  column6 varchar(500) NOT NULL,
  KEY `index` (column1, column2, column3, column4, column5, column6)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

...因为我使用了CHARSET = latin1,所以在这种情况下,密钥长度为500 * 6 = 3000字节。


7
感谢您的答复,此方法有效,但是以放弃utf8字符集为代价。是否可以以某种方式克服此限制(我具有完全的服务器访问权限),是否有充分的理由呢?
CodeVirtuoso 2012年

4
这是一个糟糕的建议,请了解什么是字符集以及将来如何导致重大问题。混合字符集数据库不仅会在使用联接或子选择时引起问题,还会将您的数据设置为非规范化格式,并且以后几乎无法纠正。
杰弗里

16

我遇到了这个问题,并通过以下方法解决了:

原因

MySQL中存在一个与MyISAM,UTF8字符集和索引有关的已知错误,您可以在此处检查。

解析度

  • 确保使用InnoDB存储引擎配置了MySQL。

  • 更改默认情况下使用的存储引擎,以便始终可以正确创建新表:

    set GLOBAL storage_engine='InnoDb';

  • 对于MySQL 5.6和更高版本,请使用以下命令:

    SET GLOBAL default_storage_engine = 'InnoDB';

  • 最后,确保您遵循迁移到MySQL中提供的说明。

参考


在大多数情况下,我们忘记将存储引擎配置为“ InnoDB”。以下答案是最简单的答案,可能会为这里的大多数用户解决问题。谢谢。
弗雷德里科·塞萨尔


3

在MySQL的64位版本上,此索引大小限制似乎更大。

我遇到了这个限制,试图转储我们的开发数据库并将其加载到本地VMWare virt中。最终,我意识到远程开发服务器是64位的,并且创建了32位的virt。我刚刚创建了64位virt,并且能够在本地加载数据库。


2

我只是通过更改原始数据库的结构,然后将其导出到服务器,将原始数据库中的“长度”值更改为总计“ 1000”,从而绕过了该错误。:)


0

我遇到了相同的问题,可在下面的查询中使用它来解决。

创建数据库时,您可以使用utf-8编码

例如。 create database my_db character set utf8 collate utf8mb4;

编辑:(考虑来自注释的建议)将utf8_bin更改为utf8mb4


2
这为我指明了正确的方向。对我来说,我必须将排序规则更改为:utf8_general_ci
伊恩·纽兰德

2
这不是正确的方向,请勿这样做!MySQL utf8不是utf8,它是一种错误的专有格式,永远都不可能实现。utf8mb4是true utf8,是为获得适当utf8支持而推荐的默认设置。
杰弗里

utf8mb4是在mysql而不是mysql上使用的正确编码utf8
alok

这只是一个例子-无论如何我已经更新了答案。
Sudhir Dhumal
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.