MySQL 5.5에서 utf8 테이블을 utf8mb4로 쉽게 변환하는 방법


71

이제 4 바이트 문자 (중국어)를 지원 해야하는 데이터베이스가 있습니다. 운 좋게도 이미 프로덕션 환경에 MySQL 5.5가 있습니다.

utf8_bin에서 utf8mb4_bin에 이르는 모든 데이터 정렬을 만들고 싶습니다.

약간의 스토리지 오버 헤드 외에이 변경으로 인한 성능 손실 / 이득이 없다고 생각합니다.

답변:


93

내 가이드 MySQL 데이터베이스에서 전체 유니 코드를 지원하는 방법에서 데이터베이스, 테이블 또는 열의 문자 집합 및 데이터 정렬을 업데이트하기 위해 실행할 수있는 쿼리는 다음과 같습니다.

각 데이터베이스에 대해 :

ALTER DATABASE
    database_name
    CHARACTER SET = utf8mb4
    COLLATE = utf8mb4_unicode_ci;

각 테이블에 대해 :

ALTER TABLE
    table_name
    CONVERT TO CHARACTER SET utf8mb4
    COLLATE utf8mb4_unicode_ci;

각 열에 대해 :

ALTER TABLE
    table_name
    CHANGE column_name column_name
    VARCHAR(191)
    CHARACTER SET utf8mb4
    COLLATE utf8mb4_unicode_ci;

(맹목적으로 복사하여 붙여 넣지 마십시오! 정확한 설명은 열 유형, 최대 길이 및 기타 속성에 따라 다릅니다. 위의 행은 VARCHAR열의 예일뿐입니다 .)

완전히에서 변환을 자동화 할 수는 없습니다 utf8utf8mb4. 위에서 언급 한 가이드의 4 단계 에서 설명한대로 , 지정된 숫자 utf8mb4가 대신 사용되는 경우 다른 의미를 가지므로 열 및 인덱스 키의 최대 길이를 확인해야합니다 utf8.

MySQL 5.5 Reference Manual의 10.1.11 절에 이에 대한 자세한 정보가 있습니다.


31

몇 가지 명령을 실행하여 데이터베이스와 테이블을 변환하는 솔루션이 있습니다. 또한 유형의 모든 열을 변환 varchar, text, tinytext, mediumtext, longtext, char. 또한 문제 가 발생할 경우 데이터베이스백업해야합니다 .

다음 코드를 preAlterTables.sql이라는 파일에 복사하십시오.

use information_schema;
SELECT concat("ALTER DATABASE `",table_schema,"` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;") as _sql 
FROM `TABLES` where table_schema like "yourDbName" group by table_schema;
SELECT concat("ALTER TABLE `",table_schema,"`.`",table_name,"` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;") as _sql  
FROM `TABLES` where table_schema like "yourDbName" group by table_schema, table_name;
SELECT concat("ALTER TABLE `",table_schema,"`.`",table_name, "` CHANGE `",column_name,"` `",column_name,"` ",data_type,"(",character_maximum_length,") CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci",IF(is_nullable="YES"," NULL"," NOT NULL"),";") as _sql 
FROM `COLUMNS` where table_schema like "yourDbName" and data_type in ('varchar','char');
SELECT concat("ALTER TABLE `",table_schema,"`.`",table_name, "` CHANGE `",column_name,"` `",column_name,"` ",data_type," CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci",IF(is_nullable="YES"," NULL"," NOT NULL"),";") as _sql 
FROM `COLUMNS` where table_schema like "yourDbName" and data_type in ('text','tinytext','mediumtext','longtext');

"yourDbName"의 모든 항목을 변환하려는 데이터베이스로 바꾸십시오. 그런 다음 다음을 실행하십시오.

mysql -uroot < preAlterTables.sql | egrep '^ALTER' > alterTables.sql

그러면 데이터베이스를 변환하는 데 필요한 모든 쿼리가 포함 된 새로운 alterTables.sql 파일이 생성됩니다. 다음 명령을 실행하여 변환을 시작하십시오.

mysql -uroot < alterTables.sql

table_schema의 조건을 변경하여 여러 데이터베이스를 통해 실행되도록 조정할 수도 있습니다. 예를 들어 table_schema like "wiki_%"이름 접두어가있는 모든 데이터베이스를 변환합니다 wiki_. 모든 데이터베이스를 변환하려면 조건을로 바꿉니다 table_type!='SYSTEM VIEW'.

발생할 수있는 문제 mysql 키에 varchar (255) 열이 있습니다. 이로 인해 오류가 발생합니다.

ERROR 1071 (42000) at line 2229: Specified key was too long; max key length is 767 bytes

이 경우 varchar (150)과 같이 열을 더 작게 변경하고 명령을 다시 실행할 수 있습니다.

참고 :이 답변은 데이터베이스를 질문 utf8mb4_unicode_ci대신에 로 변환합니다 utf8mb4_bin. 그러나 간단히 이것을 바꿀 수 있습니다.


훌륭한 스크립팅, 몇 가지 참고 사항; 현재 MiariaDb를 설치하려면 암호를 입력해야하므로 mysql -uroot -pThatrootPassWord < alterTables.sql작동합니다. 그리고 이미 언급했듯이 utf8mb4_bin은 nextcloud가 권장하는 것입니다.
Julius

utf8mb4_0900_ai_ci는 현재 기본값입니다. monolune.com/what-is-the-utf8mb4_0900_ai_ci-collation
Julius

"SET foreign_key_checks = 0;"을 사용하고 변경 사항을 적용한 다음 "SET foreign_key_checks = 1;"을 사용해야했습니다.
dfrankow

고마워요. 이것이 Redmin에서 모두 utf8mb4로 변경하는 솔루션이었습니다.
Luciano Fantuzzi

5

다음 쉘 스크립트를 사용했습니다. 데이터베이스 이름을 매개 변수로 사용하고 모든 테이블을 다른 문자 세트 및 데이터 정렬로 변환합니다 (스크립트에 정의 된 다른 매개 변수 또는 기본값으로 제공됨).

#!/bin/bash

# mycollate.sh <database> [<charset> <collation>]
# changes MySQL/MariaDB charset and collation for one database - all tables and
# all columns in all tables

DB="$1"
CHARSET="$2"
COLL="$3"

[ -n "$DB" ] || exit 1
[ -n "$CHARSET" ] || CHARSET="utf8mb4"
[ -n "$COLL" ] || COLL="utf8mb4_general_ci"

echo $DB
echo "ALTER DATABASE \`$DB\` CHARACTER SET $CHARSET COLLATE $COLL;" | mysql

echo "USE \`$DB\`; SHOW TABLES;" | mysql -s | (
    while read TABLE; do
        echo $DB.$TABLE
        echo "ALTER TABLE \`$TABLE\` CONVERT TO CHARACTER SET $CHARSET COLLATE $COLL;" | mysql $DB
    done
)

3

information_schema (TABLES 및 COLUMNS)를 사용하여 모든 테이블을 탐색하고 모든 CHAR / VARCHAR / TEXT 필드에서 MODIFY COLUMN을 수행하는 스크립트를 Perl 등으로 작성합니다. 모든 수정 사항을 각 테이블에 대한 단일 ALTER로 수집합니다. 이것은 더 효율적입니다.

Raihan의 제안 이 테이블 의 기본값 만 변경한다고 생각하지만 확실하지 않습니다 .


3

이 상황에 부딪쳤다; 데이터베이스를 변환하는 데 사용한 접근 방식은 다음과 같습니다.

  1. 먼저 my.cnf기본 데이터베이스 연결 (응용 프로그램과 MYSQL 간)을 utf8mb4_unicode_ci와 호환 되도록 편집해야 합니다. 이모 지와 같은 문자가 없으면 앱에서 제출 한 유사한 문자가 올바른 바이트 / 인코딩으로 테이블에 표시되지 않습니다 (응용 프로그램의 DB CNN 매개 변수가 utf8mb4 연결을 지정하지 않는 한).

    여기에 지시 사항이 있습니다 .

  2. 다음 SQL을 실행하십시오 (개별 열을 변경하기 위해 SQL을 준비 할 필요가 없으므로 ALTER TABLE명령문이 그렇게합니다).

    아래 코드를 실행하기 전에 "DbName"을 실제 DB 이름으로 바꾸십시오.

    USE information_schema;
    
    SELECT concat("ALTER DATABASE `",table_schema,
                  "` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;") as _sql
      FROM `TABLES`
     WHERE table_schema like "DbName"
     GROUP BY table_schema;
    
    SELECT concat("ALTER TABLE `",table_schema,"`.`",table_name,
                  "` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;") as _sql
      FROM `TABLES`
     WHERE table_schema like "DbName"
     GROUP BY table_schema, table_name;
  3. 위의 SQL 출력을 도트 SQL 파일로 수집하여 저장 한 후 실행하십시오.

  4. #1071 - Specified key was too long; max key length is 1000 bytes.문제가있는 테이블 이름과 같은 오류가 발생 하면 MB4 문자열로 변환되어야하는 해당 테이블의 일부 열에 대한 인덱스 키가 매우 커 Varvar 열이 <= 250이어야합니다. 인덱스 키는 최대 1000 바이트입니다. 인덱스가있는 열을 확인하고 그 중 하나가 varchar> 250 (대부분 255)이면

    • 1 단계 : 해당 열의 데이터를 확인하여 해당 열의 최대 문자열 크기가 <= 250인지 확인하십시오.

      쿼리 예 :

      select `id`,`username`, `email`,
             length(`username`) as l1,
             char_length(`username`) as l2,
             length(`email`) as l3,
             char_length(`email`) as l4
        from jos_users
       order by l4 Desc;
    • 2 단계 : 인덱싱 된 열 데이터의 최대 문자 길이가 250보다 작 으면 col 길이를 250으로 변경하십시오. 불가능한 경우 해당 열에서 인덱스를 제거하십시오.

    • 3 단계 : 그런 다음 해당 테이블에 대한 테이블 변경 쿼리를 다시 실행하면 테이블이 utf8mb4로 변환됩니다.

건배!


191자를 초과하는 긴 VARCHAR에 색인을 사용하는 방법이 있습니다. 수행 할 DBA / SUPER USER 권한이 있어야합니다. 데이터베이스 매개 변수 설정 : innodb_large_prefix : ON; innodb_file_format : 바라쿠다; innodb_file_format_max : 바라쿠다;
Châu Hồng Lĩnh

2

나는이 가이드를 썼습니다 : http://hanoian.com/content/index.php/24-automate-the-converting-a-mysql-database-character-set-to-utf8mb4

직장에서 ALTER 데이터베이스와 테이블이 충분하지 않다는 것을 알았습니다. 나는 각 테이블로 가서 각 text / mediumtext / varchar 열을 변경해야했습니다.

운 좋게도 MySQL 데이터베이스의 메타 데이터를 감지하는 스크립트를 작성하여 테이블과 열을 반복하여 자동으로 변경했습니다.

MySQL 5.6의 긴 인덱스 :

수행 할 DBA / SUPER USER 권한이 있어야합니다. 데이터베이스 매개 변수 설정 :

innodb_large_prefix : ON
innodb_file_format : 바라쿠다 
innodb_file_format_max : 바라쿠다

이 질문에 대한 답변에는 위의 매개 변수를 설정하는 방법이 있습니다 : https : //.com/questions/35847015/mysql-change-innodb-large-prefix

물론, 내 기사에는 그렇게하는 지침도 있습니다.

MySQL 버전 5.7 이상 에서는 innodb_large_prefix가 기본적으로 ON이고 innodb_file_format도 기본적으로 바라쿠다입니다.


2

이 문제가있는 사람들에게 가장 좋은 해결책은이 표에 따라 먼저 열을 이진 유형으로 수정하는 것입니다.

  1. CHAR => 이진
  2. 텍스트 => BLOB
  3. TINYTEXT => TINYBLOB
  4. MEDIUMTEXT => MEDIUMBLOB
  5. LONGTEXT => LONGBLOB
  6. VARCHAR => VARBINARY

그런 다음 열을 이전 유형과 원하는 문자 세트로 다시 수정하십시오.

예 :

ALTER TABLE [TABLE_SCHEMA].[TABLE_NAME] MODIFY [COLUMN_NAME] LONGBLOB;
ALTER TABLE [TABLE_SCHEMA].[TABLE_NAME] MODIFY [COLUMN_NAME] VARCHAR(140) CHARACTER SET utf8mb4;

나는 여러 latin1 테이블에서 시도했으며 모든 분음 부호를 유지했습니다.

이 작업을 수행하는 모든 열에 대해이 쿼리를 추출 할 수 있습니다.

SELECT
CONCAT('ALTER TABLE ', TABLE_SCHEMA,'.', TABLE_NAME,' MODIFY ', COLUMN_NAME,' VARBINARY;'),
CONCAT('ALTER TABLE ', TABLE_SCHEMA,'.', TABLE_NAME,' MODIFY ', COLUMN_NAME,' ', COLUMN_TYPE,' CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;')
FROM information_schema.columns
WHERE TABLE_SCHEMA IN ('[TABLE_SCHEMA]')
AND COLUMN_TYPE LIKE 'varchar%'
AND (COLLATION_NAME IS NOT NULL AND COLLATION_NAME NOT LIKE 'utf%');

0

나는 스크립트를 만들어 이 더 많거나 적은 자동으로 수행합니다

<?php
/**
 * Requires php >= 5.5
 * 
 * Use this script to convert utf-8 data in utf-8 mysql tables stored via latin1 connection
 * This is a PHP port from: https://gist.github.com/njvack/6113127
 *
 * BACKUP YOUR DATABASE BEFORE YOU RUN THIS SCRIPT!
 *
 * Once the script ran over your databases, change your database connection charset to utf8:
 *
 * $dsn = 'mysql:host=localhost;port=3306;charset=utf8';
 * 
 * DON'T RUN THIS SCRIPT MORE THAN ONCE!
 *
 * @author hollodotme
 *
 * @author derclops since 2019-07-01
 *
 *         I have taken the liberty to adapt this script to also do the following:
 *
 *         - convert the database to utf8mb4
 *         - convert all tables to utf8mb4
 *         - actually then also convert the data to utf8mb4
 *
 */

header('Content-Type: text/plain; charset=utf-8');

$dsn      = 'mysql:host=localhost;port=3306;charset=utf8';
$user     = 'root';
$password = 'root';
$options  = [
    \PDO::ATTR_CURSOR                   => \PDO::CURSOR_FWDONLY,
    \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,
    \PDO::MYSQL_ATTR_INIT_COMMAND       => "SET CHARACTER SET latin1",
];


$dbManager = new \PDO( $dsn, $user, $password, $options );

$databasesToConvert = [ 'database1',/** database3, ... */ ];
$typesToConvert     = [ 'char', 'varchar', 'tinytext', 'mediumtext', 'text', 'longtext' ];

foreach ( $databasesToConvert as $database )
{
    echo $database, ":\n";
    echo str_repeat( '=', strlen( $database ) + 1 ), "\n";

    $dbManager->exec( "USE `{$database}`" );

    echo "converting database to correct locale too ... \n";

    $dbManager->exec("ALTER DATABASE `{$database}` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci");


    $tablesStatement = $dbManager->query( "SHOW TABLES" );
    while ( ($table = $tablesStatement->fetchColumn()) )
    {
        echo "Table: {$table}:\n";
        echo str_repeat( '-', strlen( $table ) + 8 ), "\n";

        $columnsToConvert = [ ];

        $columsStatement = $dbManager->query( "DESCRIBE `{$table}`" );

        while ( ($tableInfo = $columsStatement->fetch( \PDO::FETCH_ASSOC )) )
        {
            $column = $tableInfo['Field'];
            echo ' * ' . $column . ': ' . $tableInfo['Type'];

            $type = preg_replace( "#\(\d+\)#", '', $tableInfo['Type'] );

            if ( in_array( $type, $typesToConvert ) )
            {
                echo " => must be converted\n";

                $columnsToConvert[] = $column;
            }
            else
            {
                echo " => not relevant\n";
            }
        }


        //convert table also!!!
        $convert = "ALTER TABLE `{$table}` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci";

        echo "\n", $convert, "\n";
        $dbManager->exec( $convert );
        $databaseErrors = $dbManager->errorInfo();
        if( !empty($databaseErrors[1]) ){
            echo "\n !!!!!!!!!!!!!!!!! ERROR OCCURED ".print_r($databaseErrors, true)." \n";
            exit;
        }


        if ( !empty($columnsToConvert) )
        {
            $converts = array_map(
                function ( $column )
                {
                    //return "`{$column}` = IFNULL(CONVERT(CAST(CONVERT(`{$column}` USING latin1) AS binary) USING utf8mb4),`{$column}`)";
                    return "`{$column}` = CONVERT(BINARY(CONVERT(`{$column}` USING latin1)) USING utf8mb4)";
                },
                $columnsToConvert
            );

            $query = "UPDATE IGNORE `{$table}` SET " . join( ', ', $converts );

            //alternative
            // UPDATE feedback SET reply = CONVERT(BINARY(CONVERT(reply USING latin1)) USING utf8mb4) WHERE feedback_id = 15015;


            echo "\n", $query, "\n";


            $dbManager->exec( $query );

            $databaseErrors = $dbManager->errorInfo();
            if( !empty($databaseErrors[1]) ){
                echo "\n !!!!!!!!!!!!!!!!! ERROR OCCURED ".print_r($databaseErrors, true)." \n";
                exit;
            }
        }

        echo "\n--\n";
    }

    echo "\n";
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.