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

Cod*_*oso 85 mysql mysqldump phpmyadmin

我知道这个标题的问题之前已经得到了解答,但请继续阅读.在发布之前,我已经仔细阅读了有关此错误的所有其他问题/答案.

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

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;
Run Code Online (Sandbox Code Playgroud)

有谁知道为什么以及如何解决它?问题是 - 这个查询在我的本地计算机上完美运行,并且在我以前的主机上运行良好.Btw.it来自一个成熟的项目 - phpdevshell - 所以我猜这些家伙知道他们在做什么,虽然你从来不知道.

有任何线索赞赏.

我正在使用phpMyAdmin.

Bil*_*win 146

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

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

最佳做法是使用前缀索引,因此您只索引数据的左子字符串.无论如何,您的大部分数据都会短于255个字符.

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

...
KEY `index` (`parent_menu_id`,`menu_link`(50),`plugin`(50),`alias`(50))
...
Run Code Online (Sandbox Code Playgroud)

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

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`;
Run Code Online (Sandbox Code Playgroud)

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

+---------------+---------------+---------------+----------------+
| pct_length_10 | pct_length_20 | pct_length_50 | pct_length_100 |
+---------------+---------------+---------------+----------------+
|         21.78 |         80.20 |        100.00 |         100.00 |
+---------------+---------------+---------------+----------------+
Run Code Online (Sandbox Code Playgroud)

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

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

  • 非常感谢您的详细解释.除了解决问题,我还学到了一些有价值的东西. (13认同)
  • 在测量字符串实际长度的方便查询中存在一个微妙的错误:您假设字符串全部存在;也就是说,它们都在那里。如果其中一些为空,它将导致您的计算失败并低估短字符串。您想使用 count([field_name]) 而不是 count(*)。 (2认同)
  • @Jon 我不知道你想让我说什么。MySQL 无法在如此长的字符串上创建唯一或非唯一索引。您可以添加另一列来存储字符串的哈希值,并在其上放置唯一索引。 (2认同)

Dev*_*art 26

此错误意味着索引的长度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;
Run Code Online (Sandbox Code Playgroud)

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;
Run Code Online (Sandbox Code Playgroud)

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

  • 感谢您的回复,这是有效的,但代价是放弃utf8字符集.可以以某种方式克服这种限制(我有完整的服务器访问权限),它有充分的理由吗? (6认同)
  • 这是一个糟糕的建议,请了解什么是字符集以及将来如何导致重大问题。混合字符集数据库不仅会在使用联接或子选择时引起问题,还会将您的数据设置为非规范化格式,并且以后几乎无法纠正。 (2认同)

Raz*_*med 9

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

SET @@global.innodb_large_prefix = 1;

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

  • FWIW,MySQL 8.0 删除了这个变量,因为它在该版本中始终启用。不再需要将其作为变量。 (6认同)

Vis*_*ant 9

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

原因

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

解析度

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

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

    set GLOBAL storage_engine='InnoDb';

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

    SET GLOBAL default_storage_engine = 'InnoDB';

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

参考

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

Raz*_*thi 7

如果您使用 Laravel 7 或 Laravel 8,请转到 config/database.php

'engine' => 'innoDb',
Run Code Online (Sandbox Code Playgroud)

尤其是使用 Wamp 或 Xampp 时应该有效。


Sud*_*mal 6

我面临着同样的问题;使用以下查询来解决它。

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

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

编辑:

(考虑评论中的建议)

变成utf8_binutf8mb4

  • 这不是正确的方向,不要这样做!MySQL 的“utf8”不是“utf8”,它是一种有缺陷的专有格式,永远不应该实现。`utf8mb4` 是真正的 `utf8`,并且是正确支持 `utf8` 的推荐默认值。 (4认同)
  • 这为我指明了正确的方向。对我来说,我必须将排序规则更改为:utf8_general_ci (3认同)