Migrating MySQL UTF8 to UTF8MB4 问题与疑问
Migrating MySQL UTF8 to UTF8MB4 problems and questions
我正在尝试将我的 UTF8 MySQL 5.5.30 数据库转换为 UTF8MB4。我看过这篇文章 https://mathiasbynens.be/notes/mysql-utf8mb4 但有一些问题。
这些我都做过
ALTER DATABASE database_name CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
ALTER TABLE table_name CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
最后一个是用 62 个表手动完成的,其中一个给了我这个警告
13:08:30 ALTER TABLE bradspelold.games CONVERT TO CHARACTER SET
utf8mb4 COLLATE utf8mb4_unicode_ci 101289 row(s) affected, 2
warning(s): 1071 Specified key was too long; max key length is 767
bytes 1071 Specified key was too long; max key length is 767 bytes
Records: 101289 Duplicates: 0 Warnings: 2 3.016 sec
- 这是个问题吗?我能做些什么来修复它?
下一步是
ALTER TABLE table_name CHANGE column_name column_name
VARCHAR(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
- 我不确定命令,为什么有 2 column_name?
- 我应该只对 VARCHAR(191) 列执行此操作吗?我想我一个也没有?
- 你知道还有更多像这样的文章可以解释更多 id 详细信息为什么以及如何吗?
编辑:
Table 显示游戏
CREATE TABLE `games` (
`id` int(10) unsigned NOT NULL DEFAULT \'0\',
`name` varchar(255) NOT NULL,
`description` mediumtext,
`yearPublished` datetime NOT NULL,
`minPlayers` int(10) unsigned NOT NULL,
`maxPlayers` int(10) unsigned NOT NULL,
`playingTime` varchar(127) NOT NULL,
`grade` double NOT NULL DEFAULT \'0\',
`updated` datetime NOT NULL,
`forumParentId` int(10) unsigned DEFAULT \'0\',
`lastVisited` datetime DEFAULT NULL,
`inactivatedDate` datetime DEFAULT NULL,
`bggGrade` double DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `inactivatedDate` (`inactivatedDate`),
KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8'
编辑 2:
'CREATE TABLE `forum_threads` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(150) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '''',
`description` varchar(150) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '''',
`createdDate` datetime NOT NULL DEFAULT ''0000-00-00 00:00:00'',
`createrId` int(10) unsigned DEFAULT NULL,
`replys` int(10) unsigned NOT NULL DEFAULT ''0'',
`lastPostUserId` int(10) unsigned DEFAULT NULL,
`lastPostId` int(10) unsigned DEFAULT NULL,
`forumId` int(10) unsigned DEFAULT NULL,
`visits` int(10) unsigned NOT NULL DEFAULT ''0'',
`lastPostCreated` datetime NOT NULL DEFAULT ''0000-00-00 00:00:00'',
`lastPostNickName` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '''',
`createrNickName` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '''',
`solved` tinyint(1) NOT NULL DEFAULT ''0'',
`locked` tinyint(1) NOT NULL DEFAULT ''0'',
`lockedByUserId` int(10) unsigned NOT NULL DEFAULT ''0'',
`lockedDate` datetime NOT NULL DEFAULT ''0000-00-00 00:00:00'',
`alteredDate` datetime NOT NULL DEFAULT ''0000-00-00 00:00:00'',
`alteredUserId` int(10) unsigned DEFAULT NULL,
`glued` tinyint(1) NOT NULL DEFAULT ''0'',
`pollId` int(10) unsigned DEFAULT NULL,
`facebookPostId` bigint(20) DEFAULT NULL,
`facebookImportedDate` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FK_forum_threads_1` (`forumId`),
KEY `FK_forum_threads_2` (`pollId`),
KEY `createdDate` (`createdDate`),
KEY `createrId` (`createrId`),
KEY `lastPostCreated` (`lastPostCreated`),
CONSTRAINT `FK_forum_threads_1` FOREIGN KEY (`forumId`) REFERENCES `forum` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=4306 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci'
'CREATE TABLE `forum` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(80) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '''',
`description` varchar(150) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '''',
`createdDate` datetime NOT NULL DEFAULT ''0000-00-00 00:00:00'',
`threads` int(10) unsigned NOT NULL DEFAULT ''0'',
`createrId` int(10) unsigned DEFAULT NULL,
`lastPostUserId` int(10) unsigned DEFAULT NULL,
`lastThreadId` int(10) unsigned DEFAULT NULL,
`parentForumId` int(10) unsigned DEFAULT NULL,
`lastPostNickName` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '''',
`lastPostCreated` datetime NOT NULL DEFAULT ''0000-00-00 00:00:00'',
`lastThreadTitle` varchar(80) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '''',
`alteredDate` datetime NOT NULL DEFAULT ''0000-00-00 00:00:00'',
`alteredUserId` int(10) unsigned DEFAULT NULL,
`placeOrder` int(10) unsigned NOT NULL DEFAULT ''0'',
`separator` tinyint(1) NOT NULL DEFAULT ''0'',
`rightLevel` int(10) unsigned NOT NULL DEFAULT ''1'',
`createChildForum` tinyint(3) unsigned NOT NULL DEFAULT ''1'',
`createThreads` tinyint(3) unsigned NOT NULL DEFAULT ''1'',
PRIMARY KEY (`id`),
KEY `Index_1` (`id`,`parentForumId`)
) ENGINE=InnoDB AUTO_INCREMENT=375 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci'
- 索引的大小有限制。您遇到了限制,因为 utf8mb4 每个 字符 最多需要 4 字节 ,而 utf8 只需要 3 个。同时 INDEX 大小限制在 字节.
'solution'是决定索引超大的处理方法。 (更多内容在下方)
2.
ALTER TABLE t CHANGE col col ...
与逻辑相同
ALTER TABLE t MODIFY col ...
前者允许您更改列的名称,因此当您不需要更改名称时,列名称的两个副本。
很可能您有 VARCHAR(255)
,它在 utf8 中占用 767 个字节(3*255+2;“2”是长度字段的大小)。 4 字节 utf8mb4 中的等价物是 (191) (4*191+2=766; 没有超过 191 的空间).
我还没有看到关于它的文章。我怀疑我刚才说的是大部分需要说的。
所以...
计划A:你有foo VARCHAR(255)
并且是utf8吗?其中的数据是否总是(现在和将来)小于 191 个字符?如果是这样,那么只需执行 ALTER。
计划B:如果你需要超过191,你真的需要INDEX吗? DROP INDEX 可能是替代方案。
计划 C:或者,您可以使用 "prefix" 索引:INDEX(foo(191))
,同时保留它 VARCHAR(255)
。通常 "prefix" 索引是无用的,但您 可能 有它适用的用例。
要进一步讨论这个问题,请为有问题的 table 提供 SHOW CREATE TABLE
,并讨论该特定字段及其索引的含义。
DB="database_name"
USER="mysql_user"
PASS="mysql_password"
(
echo 'ALTER DATABASE `'"$DB"'` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'
mysql -p$PASS -u $USER "$DB" -e "SHOW TABLES" --batch --skip-column-names \
| xargs -I{} echo 'ALTER TABLE `'{}'` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'
) \
| mysql -p$PASS -u $USER "$DB"
要获取您使用的脚本,请打开命令行并使用
以下步骤:
- 纳米convert_to_utf8mb4.sh
- 粘贴脚本并保存
- sudo chmod 755 convert_to_utf8mb4.sh(在终端中)
- 运行 脚本类型 ./convert_to_utf8mb4.sh
是的,排序规则已更改!
这是一个老问题,但正如我发现的那样,5 年后按照这里的一些答案是一个坏主意。
不要更改 VARCHAR
字段的大小,否则可能会损坏数据并破坏所有内容。
在 MySQL 和 MaraiDB 的当前版本中,将此添加到您的配置中,它将支持 UTF8mb4
所需的更大密钥
innodb_large_prefix=1
我还建议添加
innodb_file_per_table =1
innodb_file_format=Barracuda
那么转换将在没有 errors/warnings 密钥长度的情况下发生
我正在尝试将我的 UTF8 MySQL 5.5.30 数据库转换为 UTF8MB4。我看过这篇文章 https://mathiasbynens.be/notes/mysql-utf8mb4 但有一些问题。
这些我都做过
ALTER DATABASE database_name CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
ALTER TABLE table_name CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
最后一个是用 62 个表手动完成的,其中一个给了我这个警告
13:08:30 ALTER TABLE bradspelold.games CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci 101289 row(s) affected, 2 warning(s): 1071 Specified key was too long; max key length is 767 bytes 1071 Specified key was too long; max key length is 767 bytes Records: 101289 Duplicates: 0 Warnings: 2 3.016 sec
- 这是个问题吗?我能做些什么来修复它?
下一步是
ALTER TABLE table_name CHANGE column_name column_name
VARCHAR(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
- 我不确定命令,为什么有 2 column_name?
- 我应该只对 VARCHAR(191) 列执行此操作吗?我想我一个也没有?
- 你知道还有更多像这样的文章可以解释更多 id 详细信息为什么以及如何吗?
编辑:
Table 显示游戏
CREATE TABLE `games` (
`id` int(10) unsigned NOT NULL DEFAULT \'0\',
`name` varchar(255) NOT NULL,
`description` mediumtext,
`yearPublished` datetime NOT NULL,
`minPlayers` int(10) unsigned NOT NULL,
`maxPlayers` int(10) unsigned NOT NULL,
`playingTime` varchar(127) NOT NULL,
`grade` double NOT NULL DEFAULT \'0\',
`updated` datetime NOT NULL,
`forumParentId` int(10) unsigned DEFAULT \'0\',
`lastVisited` datetime DEFAULT NULL,
`inactivatedDate` datetime DEFAULT NULL,
`bggGrade` double DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `inactivatedDate` (`inactivatedDate`),
KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8'
编辑 2:
'CREATE TABLE `forum_threads` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(150) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '''',
`description` varchar(150) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '''',
`createdDate` datetime NOT NULL DEFAULT ''0000-00-00 00:00:00'',
`createrId` int(10) unsigned DEFAULT NULL,
`replys` int(10) unsigned NOT NULL DEFAULT ''0'',
`lastPostUserId` int(10) unsigned DEFAULT NULL,
`lastPostId` int(10) unsigned DEFAULT NULL,
`forumId` int(10) unsigned DEFAULT NULL,
`visits` int(10) unsigned NOT NULL DEFAULT ''0'',
`lastPostCreated` datetime NOT NULL DEFAULT ''0000-00-00 00:00:00'',
`lastPostNickName` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '''',
`createrNickName` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '''',
`solved` tinyint(1) NOT NULL DEFAULT ''0'',
`locked` tinyint(1) NOT NULL DEFAULT ''0'',
`lockedByUserId` int(10) unsigned NOT NULL DEFAULT ''0'',
`lockedDate` datetime NOT NULL DEFAULT ''0000-00-00 00:00:00'',
`alteredDate` datetime NOT NULL DEFAULT ''0000-00-00 00:00:00'',
`alteredUserId` int(10) unsigned DEFAULT NULL,
`glued` tinyint(1) NOT NULL DEFAULT ''0'',
`pollId` int(10) unsigned DEFAULT NULL,
`facebookPostId` bigint(20) DEFAULT NULL,
`facebookImportedDate` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FK_forum_threads_1` (`forumId`),
KEY `FK_forum_threads_2` (`pollId`),
KEY `createdDate` (`createdDate`),
KEY `createrId` (`createrId`),
KEY `lastPostCreated` (`lastPostCreated`),
CONSTRAINT `FK_forum_threads_1` FOREIGN KEY (`forumId`) REFERENCES `forum` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=4306 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci'
'CREATE TABLE `forum` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(80) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '''',
`description` varchar(150) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '''',
`createdDate` datetime NOT NULL DEFAULT ''0000-00-00 00:00:00'',
`threads` int(10) unsigned NOT NULL DEFAULT ''0'',
`createrId` int(10) unsigned DEFAULT NULL,
`lastPostUserId` int(10) unsigned DEFAULT NULL,
`lastThreadId` int(10) unsigned DEFAULT NULL,
`parentForumId` int(10) unsigned DEFAULT NULL,
`lastPostNickName` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '''',
`lastPostCreated` datetime NOT NULL DEFAULT ''0000-00-00 00:00:00'',
`lastThreadTitle` varchar(80) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '''',
`alteredDate` datetime NOT NULL DEFAULT ''0000-00-00 00:00:00'',
`alteredUserId` int(10) unsigned DEFAULT NULL,
`placeOrder` int(10) unsigned NOT NULL DEFAULT ''0'',
`separator` tinyint(1) NOT NULL DEFAULT ''0'',
`rightLevel` int(10) unsigned NOT NULL DEFAULT ''1'',
`createChildForum` tinyint(3) unsigned NOT NULL DEFAULT ''1'',
`createThreads` tinyint(3) unsigned NOT NULL DEFAULT ''1'',
PRIMARY KEY (`id`),
KEY `Index_1` (`id`,`parentForumId`)
) ENGINE=InnoDB AUTO_INCREMENT=375 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci'
- 索引的大小有限制。您遇到了限制,因为 utf8mb4 每个 字符 最多需要 4 字节 ,而 utf8 只需要 3 个。同时 INDEX 大小限制在 字节.
'solution'是决定索引超大的处理方法。 (更多内容在下方)
2.
ALTER TABLE t CHANGE col col ...
与逻辑相同
ALTER TABLE t MODIFY col ...
前者允许您更改列的名称,因此当您不需要更改名称时,列名称的两个副本。
很可能您有
VARCHAR(255)
,它在 utf8 中占用 767 个字节(3*255+2;“2”是长度字段的大小)。 4 字节 utf8mb4 中的等价物是 (191) (4*191+2=766; 没有超过 191 的空间).我还没有看到关于它的文章。我怀疑我刚才说的是大部分需要说的。
所以...
计划A:你有foo VARCHAR(255)
并且是utf8吗?其中的数据是否总是(现在和将来)小于 191 个字符?如果是这样,那么只需执行 ALTER。
计划B:如果你需要超过191,你真的需要INDEX吗? DROP INDEX 可能是替代方案。
计划 C:或者,您可以使用 "prefix" 索引:INDEX(foo(191))
,同时保留它 VARCHAR(255)
。通常 "prefix" 索引是无用的,但您 可能 有它适用的用例。
要进一步讨论这个问题,请为有问题的 table 提供 SHOW CREATE TABLE
,并讨论该特定字段及其索引的含义。
DB="database_name"
USER="mysql_user"
PASS="mysql_password"
(
echo 'ALTER DATABASE `'"$DB"'` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'
mysql -p$PASS -u $USER "$DB" -e "SHOW TABLES" --batch --skip-column-names \
| xargs -I{} echo 'ALTER TABLE `'{}'` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'
) \
| mysql -p$PASS -u $USER "$DB"
要获取您使用的脚本,请打开命令行并使用 以下步骤:
- 纳米convert_to_utf8mb4.sh
- 粘贴脚本并保存
- sudo chmod 755 convert_to_utf8mb4.sh(在终端中)
- 运行 脚本类型 ./convert_to_utf8mb4.sh
是的,排序规则已更改!
这是一个老问题,但正如我发现的那样,5 年后按照这里的一些答案是一个坏主意。
不要更改 VARCHAR
字段的大小,否则可能会损坏数据并破坏所有内容。
在 MySQL 和 MaraiDB 的当前版本中,将此添加到您的配置中,它将支持 UTF8mb4
所需的更大密钥innodb_large_prefix=1
我还建议添加
innodb_file_per_table =1
innodb_file_format=Barracuda
那么转换将在没有 errors/warnings 密钥长度的情况下发生