【问题标题】:#1071 - Specified key was too long; max key length is 1000 bytes#1071 - 指定的密钥太长;最大密钥长度为 1000 字节
【发布时间】:2012-02-03 11:54:24
【问题描述】:

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

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

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。

【问题讨论】:

    标签: mysql phpmyadmin mysqldump


    【解决方案1】:

    正如@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 选项,否则无效。

    【讨论】:

    • 非常感谢您的详细解释。除了解决问题,我还学到了一些有价值的东西。
    • 确实,非常有用的查询,用于找出应该设置索引的长度。曾多次使用它来确定索引的最佳长度。谢谢分享!
    • 在您方便的查询中存在一个微妙的错误,用于测量字符串的实际长度:您假设字符串都存在;也就是说,他们都在那里。如果有些是空的,它会抛出你的计算并少报短字符串。您想使用 count([field_name]) 而不是 count(*)。
    • 它不会使用超过第一个“前缀”索引。
    • 这是一个救命稻草,当您找到一个 mysql 数据库,其所有 varchar 字段的长度都设置为 255(即使在现实生活中最多 10 个字符的列上)并且您尝试添加复合指数。谢谢!
    【解决方案2】:

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

    【讨论】:

    • 感谢回复,这可行,但代价是放弃 utf8 字符集。能否以某种方式克服此限制(我拥有完整的服务器访问权限),是否有充分的理由?
    • 这是一个糟糕的建议,请了解什么是字符集以及这将如何在未来引起重大问题。混合字符集数据库不仅在使用连接或子选择时会导致问题,而且会将您的数据转换为非规范化格式,并且以后几乎不可能更正。
    • 我将字符集移动到 latin1,让它工作,然后再次将其更改为 utf-8
    【解决方案3】:

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

    原因

    MySQL 存在一个与 MyISAM(UTF8 字符)相关的已知错误 您可以在此处查看设置和索引。

    分辨率

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

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

      set GLOBAL storage_engine='InnoDb';

    • 对于 MySQL 5.6 及更高版本,请使用以下内容:

      SET GLOBAL default_storage_engine = 'InnoDB';

    • 最后确保您遵循Migrating to MySQL 中提供的说明。

    Reference

    【讨论】:

    • 在大多数情况下,我们忘记将存储引擎配置为“InnoDB”。以下答案是最简单的答案,可能会解决这里大多数用户的问题。谢谢。
    【解决方案4】:

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

    SET @@global.innodb_large_prefix = 1;

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

    【讨论】:

      【解决方案5】:

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

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

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

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

      【讨论】:

      • 这为我指明了正确的方向。对我来说,我不得不将排序规则更改为:utf8_general_ci
      • 这不是正确的方向,不要这样做! MySQL 的utf8 不是utf8,它是一种错误的专有格式,不应该实现。 utf8mb4 为真 utf8 并且是正确的 utf8 支持的推荐默认值。
      • utf8mb4 是在 mysql 上使用的正确编码,而不是 utf8
      • 这只是一个例子——无论如何我已经更新了答案。
      【解决方案6】:

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

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

      【讨论】:

        【解决方案7】:

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

        【讨论】:

          【解决方案8】:

          我遇到了这个错误,我将索引外键列的表列长度更改为更小,因此我将其更改为:

          VARCHAR(1024)
          

          收件人:

          VARCHAR(512)
          

          然后再次运行查询。

          【讨论】:

            【解决方案9】:

            我用过很多技巧……但没有一个对我有用 然后我找到了……最好最简单的一个

                   if you are working with mysql-workbench,
                   while altering index select (Engine:)-innoDb.
                   apply and enjoy...
            

            【讨论】:

              猜你喜欢
              • 2017-02-27
              • 2017-03-13
              • 1970-01-01
              • 2015-06-22
              • 2012-05-25
              • 2010-11-05
              • 2011-09-03
              相关资源
              最近更新 更多