Fork me on GitHub

MySQL 8.0 数据库迁移

MySQL 8.0 → MySQL 8.0 数据库迁移

场景:Linux MySQL 8.0 → Windows MySQL 8.0,mysqldump 逻辑导出 + 客户端导入。
同版本之间不需要任何兼容性改写(utf8mb4_0900_*caching_sha2_password 两端都支持)。
配套脚本:export_db.sh(导出用,因为要按规则排除表)。导入只要一行命令。


一、导出脚本(源端)

#!/bin/bash
DB_HOST=""
DB_PORT=""
DB_USER="your_user"
DB_PASS="your_password"

DB_NAME="your_db"

BASE_NAME="your_db_backup"
OUTPUT_DIR="/root"
TIMESTAMP=$(date +"%Y%m%d_%H%M")
OUTPUT="${OUTPUT_DIR}/${BASE_NAME}_${TIMESTAMP}.sql"

# 排除表名含这些子串的表(空格分隔,留空不排除)
EXCLUDE_SUBSTRINGS="_20"

# 连接选项
CONN_OPTS=""
[ -n "$DB_HOST" ] && CONN_OPTS="$CONN_OPTS -h$DB_HOST"
[ -n "$DB_PORT" ] && CONN_OPTS="$CONN_OPTS -P$DB_PORT"
[ -n "$DB_USER" ] && CONN_OPTS="$CONN_OPTS -u$DB_USER"
PASS_OPT=""
[ -n "$DB_PASS" ] && PASS_OPT="-p$DB_PASS"

# 构建排除条件
EXCLUDE_COND=""
if [ -n "$EXCLUDE_SUBSTRINGS" ]; then
    CONDITIONS=""
    for PAT in $EXCLUDE_SUBSTRINGS; do
        if [ -z "$CONDITIONS" ]; then
            CONDITIONS="INSTR(TABLE_NAME, '$PAT') > 0"
        else
            CONDITIONS="$CONDITIONS OR INSTR(TABLE_NAME, '$PAT') > 0"
        fi
    done
    EXCLUDE_COND="AND ($CONDITIONS)"
fi

IGNORE_ARGS=""
if [ -n "$EXCLUDE_COND" ]; then
    for TBL in $(mysql $CONN_OPTS $PASS_OPT -N -e \
        "SELECT TABLE_NAME FROM information_schema.TABLES
         WHERE TABLE_SCHEMA='$DB_NAME' $EXCLUDE_COND;"); do
        IGNORE_ARGS="$IGNORE_ARGS --ignore-table=$DB_NAME.$TBL"
    done
fi

mysqldump $CONN_OPTS $PASS_OPT \
    --single-transaction \
    --quick \
    --add-drop-table \
    --no-tablespaces \
    --skip-triggers \
    --skip-routines \
    --skip-events \
    --set-gtid-purged=OFF \
    --default-character-set=utf8mb4 \
    --net-buffer-length=1M \
    --hex-blob \
    $IGNORE_ARGS \
    --databases $DB_NAME > $OUTPUT

if command -v pigz >/dev/null 2>&1; then
    pigz -9 -f "$OUTPUT"
else
    gzip -f "$OUTPUT"
fi

参数说明

参数 说明
--single-transaction InnoDB 一致性快照,不锁表
--quick 逐行读取,大表不爆内存
--hex-blob 二进制字段输出为 0x...,避免导入时被客户端当命令
--set-gtid-purged=OFF 不写 GTID_PURGED,避免目标端 GTID 冲突
--default-character-set=utf8mb4 防止中文乱码
--net-buffer-length=1M 单行 INSERT 约 1MB,导入更快
--no-tablespaces 不需要 PROCESS 权限
--ignore-table 排除历史/归档表

不要加 --skip-extended-insert:会变成每行一条 INSERT,几百 MB 要导好几天。


二、传输

必须用二进制模式(FTP ASCII、文本模式解压会改坏文件),落地后核对校验值:

md5sum your_db_backup_*.sql.gz
certutil -hashfile ".\your_db_backup.sql" MD5

三、导入(目标端)

一行命令:

C:\Program Files\MySQL\MySQL Server 8.0\bin\mysql.exe --binary-mode --default-character-set=utf8mb4 --max-allowed-packet=512M -u your_user -p -h 127.0.0.1 -P 3306 your_db < "D:\backup\your_db_backup.sql"

Linux:

gunzip -kf your_db_backup.sql.gz
mysql --binary-mode --default-character-set=utf8mb4 --max-allowed-packet=512M \
      -u your_user -p -h 127.0.0.1 -P 3306 your_db < your_db_backup.sql

密码由客户端交互提示,不写在命令行、不进历史记录。

参数说明

参数 说明
--binary-mode 非交互模式下禁用客户端对 \ 命令的解析(关键,见第四节)
--default-character-set=utf8mb4 与导出保持一致
--max-allowed-packet=512M 需大于单行 INSERT;服务端 max_allowed_packet 也要 ≥ 64M

密码由客户端交互提示,不写在命令行、不进历史记录。


四、常见报错:Unknown command '\''

ERROR at line 4949: Unknown command '\''.

原因:这是 mysql 客户端的错,不是服务端的。非交互模式下客户端仍会把行首的 \ 当客户端命令解析。之所以会走到行首,是因为数据里有未转义的裸二进制字节(0x27 提前闭合了字符串引号)。

--hex-blob 只覆盖 BINARY/VARBINARY/BLOB/BIT/空间类型覆盖不了 varchar/text 里塞的二进制(序列化串、gzcompress、加密串等)。

解决:导入加 --binary-mode。官方说明:非交互模式下禁用除 charsetdelimiter 外的所有客户端命令。

治本:查出把二进制存进文本列的字段,改成 BLOB/VARBINARY,或应用层改存 base64。

SELECT TABLE_NAME, COLUMN_NAME FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA='your_db'
  AND DATA_TYPE IN ('char','varchar','tinytext','text','mediumtext','longtext');

SELECT COUNT(*) FROM `表名` WHERE `列名` REGEXP '[[:cntrl:]]';

五、检查清单

posted @ 2026-09-14 14:40  秋夜雨巷  阅读(5)  评论(0)    收藏  举报