2012-01-05 161 views
47

我知道这个标题的问题已经回答过,但请继续阅读。在发布之前,我已详细阅读了有关此错误的所有其他问题/答案。#1071 - 指定的密钥太长;最大密钥长度为1000字节

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

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; 

没有人有想法,为什么,以及如何解决它? 问题在于 - 同样的查询在我的本地机器上完美工作,并且在我之前的主机上工作得很好。 Btw.it来自一个成熟的项目 - phpdevshell - 所以我猜这些人知道他们在做什么,尽管你永远不知道。

任何线索赞赏。

我正在使用phpMyAdmin。

回答

94

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

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

最佳做法是使用前缀索引因此,您只需索引数据的左侧子字符串。无论如何,你的大部分数据将比255个字符少很多。

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

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

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

SELECT 
ROUND(SUM(LENGTH(`menu_link`)<10)*100/COUNT(*),2) AS pct_length_10, 
ROUND(SUM(LENGTH(`menu_link`)<20)*100/COUNT(*),2) AS pct_length_20, 
ROUND(SUM(LENGTH(`menu_link`)<50)*100/COUNT(*),2) AS pct_length_50, 
ROUND(SUM(LENGTH(`menu_link`)<100)*100/COUNT(*),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选项,否则不起作用。

+6

非常感谢您的详细解释。除了解决问题之外,我还学到了一些有价值的东西。 – CodeVirtuoso 2012-01-05 18:53:09

+1

这是一个整洁的查询,感谢分享! – yekta 2017-02-14 09:10:12

+0

真的,非常有用的查询找出索引应设置的长度。使用这段时间来确定索引的最佳长度。感谢你的分享! – 2018-01-27 19:35:29

17

此错误表示索引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字节。

+4

感谢您的回复,这工作,但在放弃utf8字符集的代价。不知何故可以克服这个限制(我有完整的服务器访问权限),有没有很好的理由? – CodeVirtuoso 2012-01-05 18:32:53

2

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

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

5

在创建或更改表之前运行此查询。

SET @@global.innodb_large_prefix = 1;

这将最大密钥长度设置为3072个字节

1

我刚才通过刚刚在原数据库改变“长度”的值总额约“1000做出绕过此错误“通过改变其结构,然后将其输出到服务器。 :)

相关问题