MySQL Error: Index column size too large. The maximum column size is 767 bytes

MySQL Error: Index column size too large. The maximum column size is 767 bytes

我通过 AWS 论坛和 Stack Overflow 试图找到以下错误的解决方案,但没有成功: Index column size too large. The maximum column size is 767 bytes

我正在 运行 建立一个 WordPress 网站,在 postmeta table 中有 150 万条记录。我最近向 postmeta table 添加了一个索引,并且所有测试都正常。但是,今天我的服务器发生了事故(僵尸网络扫描减少了我的 RDS 积分),并重新启动了我的 Lightsail 实例和我的 RDS MySQL 实例。重新启动后,我注意到该站点无法正常工作,经过进一步调查发现 postmeta table 返回错误 Index column size too large. The maximum column size is 767 bytes.

我运行正在 MySQL 8.0.20

table是:

    Engine = InnoDB  
    Charset = utf8mb4  
    Collation = utf8mb4_0900_ai_ci  
    Row Format = Compact  

许多现有的“解决方案”谈论重新创建 table,但是我需要 table 当前的数据。 不幸的是,这个问题出现在我最老的 AWS RDS 快照中,所以备份似乎不是一个选项。

每次我尝试 运行 ALTERSELECT 语句时,我都会得到相同的错误 Index column size too large. The maximum column size is 767 bytes。 我试过:

我可以看到默认的 ROWFORMAT 现在是“DYNAMIC”,但是这个 table 仍然是“COMPACT”,因为它在 MySQL 5.7[=27 上 运行ning =]

我还尝试将 AWS RDS MySQL 从 8.0.20 更新到 8.0.23,但是更新失败,因为它报告 table 在 PrePatchCompatibility.log 中损坏。
参考:https://dba.stackexchange.com/questions/234822/mysql-error-seems-unfixable-index-column-size-too-large#answer-283266

还有一些修改环境和文件系统的建议,运行宁“innodb_force_recovery”。
https://dba.stackexchange.com/questions/116730/corrupted-innodb-table-mysqlcheck-and-mysqldump-crash-server
但是,作为 RDS 实例,我无权访问该实例的较低级别。

我怀疑这个问题是列长度和 utf8mb4,但是我的首要任务是从当前 table.
中获取数据 我也知道将 ROWFORMAT 更改为 DYNAMIC 应该可以解决此问题 - 但是会出现相同的错误。

参考:http://mysql.rjweb.org/doc.php/limits#767_limit_in_innodb_indexes

我也试过“RDS 导出到 S3”选项,但没有成功。

请帮忙,我不知道还能尝试什么。

WordPress 的 wp_postmeta table normally 在其 meta_key 列上有一个索引,它是 varchar(255)。太长了。

首先,删除太大的索引。

SHOW CREATE TABLE wp_postmeta; -- to verify the name of the index

ALTER TABLE wp_postmeta DROP KEY meta_key;

我假设索引的名称是 meta_key,这是该列上索引的默认名称。但是要仔细检查索引名称才能确定。

然后,将索引添加回去,但使其成为前缀索引,使其不大于 767 字节。

由于您使用的是 utf8mb4,它允许多字节字符每个字符最多 4 个字节,因此您可以定义前缀长度为 floor(767/4) 或 191 的索引。

ALTER TABLE wp_postmeta ADD KEY (meta_key(191));

COMPACT 行格式允许该索引长度,并且它应该足够长以使索引与以前一样有用。您几乎不可能拥有许多具有相同前导字符且仅在第 191 个字符之后不同的元键值。


另一种方法是创建一个新的 table:

CREATE TABLE wp_postmeta_new (
 meta_id BIGINT UNSIGNED,
 post_id BIGINT UNSIGNED,
 meta_key VARCHAR(255),
 meta_value LONGTEXT,
 KEY (post_id),
 KEY (meta_key)
) ROW_FORMAT=DYNAMIC;

仔细检查它创建此 table 的动态行格式。

将所有旧数据复制到其中:

INSERT INTO wp_postmeta_new SELECT * from wp_postmeta;

然后交换 tables:

RENAME TABLE wp_postmeta TO wp_postmeta_old, 
             wp_postmeta_new TO wp_postmeta;

我假设在您执行此操作时没有创建新帖子。您必须确保没有人向此 WP 实例添加内容,这样您就不会错过该过程中的任何数据。

我遇到并解决了同样的问题。情况是这样的。

在旧版 MySQL table 格式中,VARCHAR 或 blob 列上索引的最大大小为 767 字节(不是字符)。这些 wp_somethingmeta WordPress tables 具有 VARCHAR(255) 数据类型的键列(如 meta_key)。当 utf8mb4 是字符集时,每个字符最多可以占用 767 个字节中的四个。这意味着必须将索引定义为前缀。 meta_key(191).

是什么让 MySQL table 成为遗产 table?

  1. MyISAM 访问方式,或
  2. MySQL 的旧版本(5.5,5.6 早期版本)仅支持较旧的 InnoDB Antelope ondisk table format and not the newer Barracuda 文件格式,或
  3. InnoDB 而 ROW_FORMAT 是 COMPACT (or REDUNDANT).

因此,要摆脱 varchar(255) 列上的前缀索引,table 需要是 InnoDB 并使用 DYNAMIC(或 COMPRESSED)ROW_FORMAT。

无需从头重建旧版 table。你可以通过说

来转换它
ALTER TABLE whatever ENGINE=InnoDB, ROW_FORMAT=DYNAMIC;

那么您就不会再遇到前缀键 (191) 问题了。

在你做这种事情之前备份你的数据库。你知道的。

并且,升级到 MySQL 或 MariaDB 的最新版本。严重地。 MySQL 5.6 长期支持于 1-Febru-2021 结束,越新的版本越好。 (GoDaddy!我在看着你。)