【问题标题】:Updating MySQL database from latin1 to utf8mb4: max key length is 767 bytes将 MySQL 数据库从 latin1 更新为 utf8mb4:最大密钥长度为 767 字节
【发布时间】:2015-04-13 17:43:27
【问题描述】:

我正在尝试将 MySQL 数据库(Rails 3 应用程序的一部分)中的字符编码从 latin1 升级到 utfmb8。我已经完成了some reading,但我被困在最后两张桌子上:userstrigrams

尝试更新users 表会得到可怕的ERROR 1071

mysql> ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ERROR 1071 (42000): Specified key was too long; max key length is 767 bytes

按照this answer,我可以通过在有问题的VARCHAR(255) 列上删除并重新创建索引来解决这个问题:

mysql> DROP INDEX index_users_on_remember_token ON users;
mysql> CREATE INDEX index_users_on_remember_token ON users (remember_token(191));

我担心这会把事情搞砸,因为我不太了解索引。不过暂且不说……

我的主要问题是如何为我的最后一张桌子做类似的事情:trigrams。以下是trigrams 表的一些信息:

mysql> SHOW CREATE TABLE trigrams;
CREATE TABLE `trigrams` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `trigram` varchar(3) DEFAULT NULL,
    `score` smallint(6) DEFAULT NULL,
    `owner_id` int(11) DEFAULT NULL,
    `owner_type` varchar(255) DEFAULT NULL,
    `fuzzy_field` varchar(255) DEFAULT NULL,
    PRIMARY KEY (`id`),
    KEY `index_for_match` (`owner_id`,`owner_type`,`fuzzy_field`,`trigram`,`score`),
    KEY `index_by_owner` (`owner_id`,`owner_type`)
    ) ENGINE=InnoDB AUTO_INCREMENT=41531 DEFAULT CHARSET=latin1

mysql> SHOW INDEXES IN trigrams;
+----------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table    | Non_unique | Key_name        | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+----------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| trigrams |          0 | PRIMARY         |            1 | id          | A         |       35502 |     NULL | NULL   |      | BTREE      |         |               |
| trigrams |          1 | index_for_match |            1 | owner_id    | A         |       35502 |     NULL | NULL   | YES  | BTREE      |         |               |
| trigrams |          1 | index_for_match |            2 | owner_type  | A         |       35502 |     NULL | NULL   | YES  | BTREE      |         |               |
| trigrams |          1 | index_for_match |            3 | fuzzy_field | A         |       35502 |     NULL | NULL   | YES  | BTREE      |         |               |
| trigrams |          1 | index_for_match |            4 | trigram     | A         |       35502 |     NULL | NULL   | YES  | BTREE      |         |               |
| trigrams |          1 | index_for_match |            5 | score       | A         |       35502 |     NULL | NULL   | YES  | BTREE      |         |               |
| trigrams |          1 | index_by_owner  |            1 | owner_id    | A         |       35502 |     NULL | NULL   | YES  | BTREE      |         |               |
| trigrams |          1 | index_by_owner  |            2 | owner_type  | A         |       35502 |     NULL | NULL   | YES  | BTREE      |         |               |
+----------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

我的问题是:如何解决以下错误?

mysql> ALTER TABLE trigrams CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ERROR 1071 (42000): Specified key was too long; max key length is 767 bytes

我要对trigrams 进行哪些更改?进行这些更改有哪些风险?

(如果相关:trigrams 表是由某些第三方代码自动创建的,fuzzily gem。)

【问题讨论】:

    标签: mysql ruby-on-rails character-encoding


    【解决方案1】:

    试试

    CREATE TABLE tmp LIKE triggrams;
    SET GLOBAL innodb_large_prefix=1;
    ALTER TABLE tmp CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
    LOCK TABLE trigrams, tmp WRITE;
    INSERT INTO tmp SELECT * FROM trigrams;
    RENAME TABLE trigrams to trigrams_old, tmp to trigrams;
    UNLOCK TABLES;
    

    第二个选项是减少 varchar 列的索引前缀。

    【讨论】:

    • 第二步失败:mysql> ALTER TABLE tmp CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ERROR 1071 (42000):指定的密钥太长;最大密钥长度为 767 字节
    • 呃,好的。但基本思想是,除了 tmp 表,您可以在不影响原始表或其中的数据的情况下使用表定义。为什么不使用问题中您自己的 CREATE TABLE 语句,而是将 default charset=latin1 替换为 default charset=utf8mb4?
    • 对,好的:我基于CREATE TABLE 语句为trigrams 创建了一个tmp 表,但在两个地方都使用VARCHAR(191) 代替VARCHAR(255),并使用@987654328 @ 代替 latin1。好像可以了,谢谢建议。奇怪的是,仅仅用长度为191 的索引替换现有trigrams 表上的索引似乎不像users 表那样工作。不知道为什么。
    • index_for_match 索引涵盖两个 varchar(255) 字段,总共为您提供 510 * 4 = 最多 2040 字节的索引键。默认情况下,索引键的限制为 AFAIR 1000 字节,您可以使用 innodb_large_prefix 全局变量将其更改为 3000 字节。
    • 这本质上是一个duplicate,它提供了 3 个答案。
    猜你喜欢
    • 2016-10-04
    • 2017-11-23
    • 2017-02-01
    • 2016-04-15
    • 2012-05-31
    • 2015-06-22
    • 2013-12-21
    • 2015-06-29
    • 2014-08-17
    相关资源
    最近更新 更多