将MySQL UTF8迁移到UTF8MB4的问题和疑问

时间:2015-03-22 12:19:45

标签: mysql utf-8 migrate utf8mb4

我试图将我的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)受影响,2   警告:1071指定密钥太长;最大密钥长度为767   bytes 1071指定密钥太长;最大密钥长度为767字节   记录:101289重复:0警告:2 3.016秒

  1. 这是一个问题吗?我该怎么做才能解决它?
  2. 下一步是

    ALTER TABLE table_name CHANGE column_name column_name
             VARCHAR(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
    
    1. 我不确定该命令,为什么有2列column_name?
    2. 我应该只在VARCHAR(191)列上执行此操作吗?我不认为我有这些吗?
    3. 你知道更多这样的artickels解释了更多id详细的原因和方法吗?
    4. 编辑:

      桌上游戏

      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'
      

3 个答案:

答案 0 :(得分:5)

  1. INDEX的大小有限制。你达到了极限,因为utf8mb4每个字符需要最多4个字节 ,而utf8只需要3.同时INDEX大小限制为 bytes
  2. “解决方案”是决定如何处理超大索引。 (更多下面)

    2

    ALTER TABLE t CHANGE col col ...
    

    与更符合逻辑的

    相同
    ALTER TABLE t MODIFY col ...
    

    前者允许您更改列的名称,因此当您不需要更改名称时,可以更改列名的两个副本。

    1. 很可能你有VARCHAR(255)在utf8中需要767个字节(3 * 255 + 2;“2”是长度字段的大小)。 4字节utf8mb4中的等价物是(191)(4 * 191 + 2 = 766;超过191的空间)。

    2. 我还没有看过有关它的文章。我怀疑我刚才所说的是大部分需要说的内容。

    3. 因此...

      计划 A :您有foo VARCHAR(255)并且它是utf8吗?它中的数据总是(现在和将来)是否短于191个字符?如果是这样,那么只需做ALTER。

      计划 B :如果您需要超过191,您真的需要INDEX吗? DROP INDEX可能是另一种选择。

      计划 C :或者,您可以使用“前缀”索引:INDEX(foo(191)),同时保留VARCHAR(255)。通常“前缀”索引是无用的,但可能有一个用例。

      为了进一步讨论这个问题,请为相关表格提供SHOW CREATE TABLE,并讨论该特定字段及其INDEX的含义。

答案 1 :(得分:3)

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"
  • 要获取您运行的脚本,请打开命令行并使用 以下步骤:

    1. nano convert_to_utf8mb4.sh
    2. 粘贴脚本&保存
    3. sudo chmod 755 convert_to_utf8mb4.sh(在终端)
    4. 按类型./convert_to_utf8mb4.sh
    5. 运行脚本

      是的,整理已经改变了!

答案 2 :(得分:0)

这是一个古老的问题,但是据我发现,五年后遵循这里的一些答案是一个坏主意。 请勿更改#include <iostream> #define OutPut std::cout << int main() { OutPut "This sentence should appear in console."; return 0; }; 字段的大小,否则可能会损坏数据并破坏所有内容。

在当前版本的MySQL和MaraiDB中,将此添加到您的配置中,它将支持UTF8mb4所需的较大密钥。

VARCHAR

我还建议添加 innodb_large_prefix=1 innodb_file_per_table =1

然后将进行转换,而不会出现有关密钥长度的错误/警告