将 MySQL-PXC 迁移到 MySQL-MGR

本指南提供了在 Alauda Container Platform 上从 MySQL-PXC(Percona XtraDB Cluster)5.7 迁移到 MySQL Group Replication(MGR)8.0 的完整操作说明。

背景

挑战

MySQL 5.7 已于 2023 年 10 月达到生命周期终止(EOL),并且从 Alauda Database Service for MySQL v4.3.0 开始,MySQL-PXC 已被弃用并移除。组织必须迁移到 MySQL 8.0,才能继续获得安全更新并利用新特性。

迁移生产数据库需要考虑诸多复杂因素:

  • 架构与 MySQL 8.0 保留关键字的兼容性
  • 字符集变更(utf8mb4)
  • 认证插件更新
  • 确保迁移过程中的数据完整性

解决方案

本指南提供了从 MySQL-PXC 5.7 迁移到 MySQL-MGR 8.0 的完整且经过验证的操作说明:

  • 成熟方案:已在 ACP v4.0+ 上使用 Alauda Database Service for MySQL 验证
  • 完整对象覆盖:迁移所有标准 MySQL 对象(表、视图、routine、trigger、event、用户、权限)
  • 架构兼容性:自动检查并修复 MySQL 8.0 兼容性问题
  • 全面验证:覆盖 9 类对象的验证
  • 风险最小化:提供详细的回滚步骤,并在每个步骤进行验证

环境信息

组件版本
源(PXC)Percona XtraDB Cluster 5.7.44
目标(MGR)MySQL Group Replication 8.0.44
ACP 版本v4.0 或更高
MySQL Operatorv4.0 或更高

PXC 与 MGR:主要差异

方面PXC 5.7(源)MGR 8.0(目标)
Pod 名称模式${NAME}-pxc-0${NAME}-0
Container 指定符不需要(默认 mysql)必需:-c mysql
主端点${NAME}-proxysql.${NS}.svc.cluster.local:3306${NAME}-read-write.${NS}.svc.cluster.local:3306
从节点端点与主端点相同(由 ProxySQL 负责路由)${NAME}-read-only.${NS}.svc.cluster.local:3306
复制类型Galera(同步多主)Group Replication(单主模式,异步从节点)
Secret 名称模式${NAME}mgr-${NAME}-password
WARNING

在运行迁移命令前,始终先使用 kubectl get pod -n <namespace> 检查实际的 Pod 名称。

常见场景

场景数据库大小预估停机时间
小型数据库< 10GB15-30 分钟
中型数据库10-50GB30-60 分钟
大型数据库50-200GB1-2 小时
架构问题任意大小额外 1-2 小时修复
字符集迁移任意大小额外 30-60 分钟

先决条件

在执行迁移之前,请确保具备以下条件:

  • ACP v4.0 或更高版本,且 MySQL Operator 为 v4.0 或更高版本
  • 一个健康的 MySQL-PXC 5.7 集群作为源端
  • 已启用 GTID 模式(@@gtid_mode = ON@@enforce_gtid_consistency = ON
  • 在迁移之前已创建新的 MySQL-MGR 8.0 集群作为目标端
  • 存储容量为源数据库大小的 2-3 倍
  • 本地机器可同时连通两个集群的网络

重要限制

  • 为确保一致性,在导出和导入期间需要应用停机
  • 推荐的最大数据库大小:200GB(更大的数据库可能需要其他方案)
  • 源集群必须启用 GTID
  • 必须在迁移开始前创建目标集群
  • 目标端的存储性能应与源端相当或更高

开始之前

1. 获取 MySQL Root 密码

# For PXC 5.7 source
kubectl get secret <source-name> -n <source-namespace> -o jsonpath='{.data.root}' | base64 -d

# For MGR 8.0 target
kubectl get secret mgr-<target-name>-password -n <target-namespace> -o jsonpath='{.data.root}' | base64 -d

2. 确认 Pod 名称

# Check source PXC pods
kubectl get pod -n <source-namespace> | grep <source-name>
# Example output: source-pxc-0, source-pxc-1, source-pxc-2

# Check target MGR pods
kubectl get pod -n <target-namespace> | grep <target-name>
# Example output: target-0, target-1, target-2

3. 验证集群状态

# Check PXC source status
kubectl get mysql <source-name> -n <source-namespace>
# Expected: STATE = ready, PXCSTATE = ready

# Check MGR target status
kubectl get mysql <target-name> -n <target-namespace>
# Expected: All 3 members ready, STATUS = Running

4. kubectl exec 最佳实践

针对 PXC 5.7(源端):

# No container specifier needed for PXC
kubectl exec <source-name>-pxc-0 -n <namespace> -- \
  mysql -uroot -p<password> -e "SQL_HERE"

针对 MGR 8.0(目标端):

# Always use -c mysql for MGR
kubectl exec <target-name>-0 -n <namespace> -c mysql -- \
  mysql -uroot -p<password> -e "SQL_HERE"
TIP
  • 始终使用参数顺序:kubectl exec -n <namespace> <pod-name> -- <command>
  • 在命令前使用 --(双短横线)将 kubectl 选项与命令分隔开
  • 避免在 kubectl exec 中使用 heredoc(<<EOF)——由于 shell 引号处理问题,它们通常会失败

执行指南

步骤 1:架构兼容性分析

请在计划迁移前一周执行此分析。

运行以下命令以检测架构兼容性问题:

# Check for reserved keyword columns
kubectl exec ${SOURCE_NAME}-pxc-0 -n ${SOURCE_NAMESPACE} -- \
  mysql -uroot -p${SOURCE_MYSQL_PASSWORD} -e "
    SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME
    FROM information_schema.COLUMNS
    WHERE LOWER(COLUMN_NAME) IN ('rank','groups','function','tables','indexes','procedure','file','process');
  "

# Check for ZEROFILL columns
kubectl exec ${SOURCE_NAME}-pxc-0 -n ${SOURCE_NAMESPACE} -- \
  mysql -uroot -p${SOURCE_MYSQL_PASSWORD} -e "
    SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_TYPE
    FROM information_schema.COLUMNS
    WHERE COLUMN_TYPE LIKE '%ZEROFILL%';
  "

# Check for invalid date defaults
kubectl exec ${SOURCE_NAME}-pxc-0 -n ${SOURCE_NAMESPACE} -- \
  mysql -uroot -p${SOURCE_MYSQL_PASSWORD} -e "
    SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT
    FROM information_schema.COLUMNS
    WHERE COLUMN_DEFAULT = '0000-00-00'
       OR COLUMN_DEFAULT = '0000-00-00 00:00:00';
  "

修复架构问题

# Fix reserved keyword columns (example)
kubectl exec ${SOURCE_NAME}-pxc-0 -n ${SOURCE_NAMESPACE} -- \
  mysql -uroot -p${SOURCE_MYSQL_PASSWORD} -e "
    USE db1;
    ALTER TABLE users CHANGE COLUMN rank user_rank INT;
  "

# Fix ZEROFILL columns
kubectl exec ${SOURCE_NAME}-pxc-0 -n ${SOURCE_NAMESPACE} -- \
  mysql -uroot -p${SOURCE_MYSQL_PASSWORD} -e "
    USE db1;
    ALTER TABLE products MODIFY COLUMN price DECIMAL(10,2);
  "

步骤 2:字符集与排序规则分析

检查非 utf8mb4 的表:

kubectl exec ${SOURCE_NAME}-pxc-0 -n ${SOURCE_NAMESPACE} -- \
  mysql -uroot -p${SOURCE_MYSQL_PASSWORD} -e "
    SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_COLLATION
    FROM information_schema.TABLES
    WHERE TABLE_SCHEMA NOT IN ('information_schema','mysql','performance_schema','sys')
      AND TABLE_COLLATION NOT LIKE 'utf8mb4%';
  "

转换为 utf8mb4

# Convert databases to utf8mb4
for db in ${DATABASES}; do
  kubectl exec ${SOURCE_NAME}-pxc-0 -n ${SOURCE_NAMESPACE} -- \
    mysql -uroot -p${SOURCE_MYSQL_PASSWORD} -e "
      ALTER DATABASE ${db} CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
    "
done

# Convert tables to utf8mb4
for db in ${DATABASES}; do
  TABLES=$(kubectl exec ${SOURCE_NAME}-pxc-0 -n ${SOURCE_NAMESPACE} -- \
    mysql -uroot -p${SOURCE_MYSQL_PASSWORD} -N -e "
      SELECT TABLE_NAME FROM information_schema.TABLES
      WHERE TABLE_SCHEMA = '${db}' AND TABLE_TYPE = 'BASE TABLE';
    ")

  for table in ${TABLES}; do
    echo "Converting ${db}.${table}..."
    kubectl exec ${SOURCE_NAME}-pxc-0 -n ${SOURCE_NAMESPACE} -- \
      mysql -uroot -p${SOURCE_MYSQL_PASSWORD} ${db} -e "
        ALTER TABLE ${table} CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
      "
  done
done
WARNING

对于具有较长 VARCHAR/TEXT 索引(>191 个字符)的表,你可能需要调整索引长度:

ALTER TABLE users DROP INDEX idx_email;
ALTER TABLE users ADD UNIQUE INDEX idx_email (email(191));

步骤 3:创建目标 MySQL-MGR 8.0 实例

在数据迁移阶段开始前不久创建目标 MySQL-MGR 8.0 实例。

使用 Web 控制台:

  1. 选择 MySQL 版本 8.0
  2. 配置资源(建议由于 MySQL 8.0 的额外开销,内存比源集群增加 10-20%
  3. 将存储大小设置为源数据库大小的 2-3 倍

使用命令行:

TARGET_NAME="mysql-8-target"
NAMESPACE="your-namespace"
STORAGE_SIZE="500Gi"

cat << EOF | kubectl -n $NAMESPACE apply -f -
apiVersion: middleware.alauda.io/v1
kind: Mysql
metadata:
  name: $TARGET_NAME
  namespace: $NAMESPACE
  labels:
    mysql/arch: mgr
spec:
  mgr:
    enableStorage: true
    image: {}
    members: 3
    monitor:
      enable: true
      exporter: {}
    resources:
      server:
        limits:
          cpu: "2"
          memory: 4Gi
        requests:
          cpu: "2"
          memory: 4Gi
    router:
      replicas: 1
      resources:
        limits:
          cpu: 500m
          memory: 512Mi
        requests:
          cpu: 500m
          memory: 512Mi
      svcRO:
        type: ClusterIP
      svcRW:
        type: ClusterIP
    strictSecurityModeEnabled: true
    upgradeOption: {}
    volumeClaimTemplate:
      metadata: {}
      spec:
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            storage: ${STORAGE_SIZE}
        storageClassName: dataservice-topolvmsc
      status: {}
  params:
    mysql: {}
    router:
      DEFAULT:
        max_total_connections: "200"
      logger:
        level: info
  upgradeOption:
    autoUpgrade: false
  version: "8.0"
EOF

验证目标集群:

kubectl -n $NAMESPACE get mysql $TARGET_NAME -w
# Expected: STATE = Ready, MGRSTATE = ready

步骤 4:迁移数据、用户和权限

关键:停止应用写入

从此步骤开始,应用必须保持停止状态(或严格只读),直到切换完成。此步骤之后写入源数据库的任何数据都将丢失。

操作步骤:

  1. 停止应用写入:将应用副本数缩放为 0:

    kubectl scale deployment <app-name> --replicas=0 -n <app-namespace>
  2. 确定需要迁移的数据库

    # List user databases (DO NOT include: information_schema, mysql, performance_schema, sys)
    kubectl exec ${SOURCE_NAME}-pxc-0 -n ${SOURCE_NAMESPACE} -- \
      mysql -uroot -p${SOURCE_MYSQL_PASSWORD} -N -e "
        SELECT SCHEMA_NAME FROM information_schema.SCHEMATA
        WHERE SCHEMA_NAME NOT IN ('information_schema','mysql','performance_schema','sys');
      "
  3. 导出并导入数据

    SOURCE_NAME="source"
    SOURCE_NAMESPACE="your-namespace"
    SOURCE_MYSQL_PASSWORD="source-root-password"
    
    TARGET_NAME="mysql-8-target"
    TARGET_NAMESPACE="your-namespace"
    TARGET_MYSQL_PASSWORD="target-root-password"
    
    DATABASES="db1 db2 db3"
    
    for db in ${DATABASES}; do
      echo "Migrating database: ${db}..."
    
      # Export from PXC source
      kubectl exec ${SOURCE_NAME}-pxc-0 -n ${SOURCE_NAMESPACE} -- \
        mysqldump -uroot -p${SOURCE_MYSQL_PASSWORD} \
          --single-transaction \
          --quick \
          --lock-tables=false \
          --set-gtid-purged=ON \
          --routines \
          --events \
          --triggers \
          --databases ${db} \
        | grep -v "SET @@GLOBAL.GTID_PURGED" \
        | kubectl exec -i ${TARGET_NAME}-0 -n ${TARGET_NAMESPACE} -c mysql -- \
          mysql -uroot -p${TARGET_MYSQL_PASSWORD}
    
      echo "Migrated: ${db}"
    done
  4. 迁移用户和权限

    # List non-system users from PXC source
    USERS=$(kubectl exec ${SOURCE_NAME}-pxc-0 -n ${SOURCE_NAMESPACE} -- \
      mysql -uroot -p${SOURCE_MYSQL_PASSWORD} -N -e "
        SELECT CONCAT('''', User, '''@''', Host, '''')
        FROM mysql.user
        WHERE User NOT IN ('root','mysql.sys','mysql.session','mysql.infoschema',
                           'monitor','operator','clustercheck','xtrabackup','replication')
          AND User NOT LIKE 'mysql.%'
          AND Host != 'localhost';
      ")
    
    # For each user, create user and apply grants on target
    for user_host in ${USERS}; do
      echo "Migrating user: ${user_host}..."
    
      # Get CREATE USER statement (includes auth plugin and password hash)
      CREATEUSER=$(kubectl exec ${SOURCE_NAME}-pxc-0 -n ${SOURCE_NAMESPACE} -- \
        mysql -uroot -p${SOURCE_MYSQL_PASSWORD} -N -e "SHOW CREATE USER ${user_host};")
    
      # Create user on target
      kubectl exec ${TARGET_NAME}-0 -n ${TARGET_NAMESPACE} -c mysql -- \
        mysql -uroot -p${TARGET_MYSQL_PASSWORD} -e "${CREATEUSER};"
    
      # Get GRANT statements from source
      GRANTS=$(kubectl exec ${SOURCE_NAME}-pxc-0 -n ${SOURCE_NAMESPACE} -- \
        mysql -uroot -p${SOURCE_MYSQL_PASSWORD} -N -e "SHOW GRANTS FOR ${user_host};")
    
      # Apply grants on target
      echo "${GRANTS}" | while read grant; do
        kubectl exec ${TARGET_NAME}-0 -n ${TARGET_NAMESPACE} -c mysql -- \
          mysql -uroot -p${TARGET_MYSQL_PASSWORD} -e "${grant};"
      done
    done
    
    # Flush privileges on target
    kubectl exec ${TARGET_NAME}-0 -n ${TARGET_NAMESPACE} -c mysql -- \
      mysql -uroot -p${TARGET_MYSQL_PASSWORD} -e "FLUSH PRIVILEGES;"
    TIP

    SHOW CREATE USER 会保留原始认证插件和密码哈希,因此迁移后用户仍可使用现有密码登录。如果你需要为客户端兼容性切换到 mysql_native_password,请运行:

    kubectl exec ${TARGET_NAME}-0 -n ${TARGET_NAMESPACE} -c mysql -- \
      mysql -uroot -p${TARGET_MYSQL_PASSWORD} -e "
        ALTER USER ${user_host} IDENTIFIED WITH mysql_native_password BY '<password>';
      "
TIP

上面的 mysqldump 流式处理方式不需要为 dump 文件额外预留磁盘空间。

步骤 5:验证迁移

TARGET_NAME="mysql-8-target"
TARGET_NAMESPACE="your-namespace"
TARGET_MYSQL_PASSWORD="target-root-password"
DATABASES="db1 db2 db3"

for db in ${DATABASES}; do
  echo "=== Verifying database: ${db} ==="

  # Compare table counts
  echo "Tables:"
  kubectl exec ${SOURCE_NAME}-pxc-0 -n ${SOURCE_NAMESPACE} -- \
    mysql -uroot -p${SOURCE_MYSQL_PASSWORD} -N -e "SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = '${db}' AND TABLE_TYPE = 'BASE TABLE';"

  kubectl exec ${TARGET_NAME}-0 -n ${TARGET_NAMESPACE} -c mysql -- \
    mysql -uroot -p${TARGET_MYSQL_PASSWORD} -N -e "SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = '${db}' AND TABLE_TYPE = 'BASE TABLE';"

  # Verify views execute correctly
  kubectl exec ${TARGET_NAME}-0 -n ${TARGET_NAMESPACE} -c mysql -- \
    mysql -uroot -p${TARGET_MYSQL_PASSWORD} -N -e "
      SELECT TABLE_NAME FROM information_schema.VIEWS WHERE TABLE_SCHEMA = '${db}';
    " | while read view; do
      kubectl exec ${TARGET_NAME}-0 -n ${TARGET_NAMESPACE} -c mysql -- \
        mysql -uroot -p${TARGET_MYSQL_PASSWORD} ${db} -e "SELECT 1 FROM ${view} LIMIT 1;" > /dev/null 2>&1 \
        && echo "✓ View ${view} OK" \
        || echo "✗ View ${view} FAILED"
    done
done

验证清单:

  • 每个数据库中的表数量一致
  • 每张表的行数一致
  • 视图数量一致
  • 所有视图都能成功执行
  • 所有存储过程和函数都存在
  • 所有 trigger 和 event 都已迁移
  • 所有用户账户都存在
  • 所有权限都已迁移

步骤 6:迁移后优化

1. 更新表统计信息

for db in ${DATABASES}; do
  echo "Analyzing tables in ${db}..."
  TABLES=$(kubectl exec ${TARGET_NAME}-0 -n ${TARGET_NAMESPACE} -c mysql -- \
    mysql -uroot -p${TARGET_MYSQL_PASSWORD} -N -e "
      SELECT TABLE_NAME FROM information_schema.TABLES
      WHERE TABLE_SCHEMA = '${db}' AND TABLE_TYPE = 'BASE TABLE';
    ")

  for table in ${TABLES}; do
    kubectl exec ${TARGET_NAME}-0 -n ${TARGET_NAMESPACE} -c mysql -- \
      mysql -uroot -p${TARGET_MYSQL_PASSWORD} ${db} -e "ANALYZE TABLE ${table};" 2>&1 | grep -v "Table"
  done
done

2. 检查碎片

# Build properly-quoted comma-separated list
DB_LIST=$(echo $DATABASES | sed "s/ /','/g")
DB_LIST="'$DB_LIST'"

kubectl exec ${TARGET_NAME}-0 -n ${TARGET_NAMESPACE} -c mysql -- \
  mysql -uroot -p${TARGET_MYSQL_PASSWORD} -e "
    SELECT TABLE_SCHEMA, TABLE_NAME,
           ROUND(DATA_FREE / 1024 / 1024, 2) AS 'Fragmentation (MB)'
    FROM information_schema.TABLES
    WHERE TABLE_SCHEMA IN ($DB_LIST)
      AND DATA_FREE > 0
    ORDER BY DATA_FREE DESC;
  "

如果发现明显碎片(>100MB),请重建表:

OPTIMIZE TABLE db1.orders;

应用切换

步骤 7:切换应用到目标端

1. 验证应用已停止

# Ensure application is scaled down
kubectl scale deployment <app-name> --replicas=0 -n <app-namespace>

# Verify no active connections on source
kubectl exec ${SOURCE_NAME}-pxc-0 -n ${SOURCE_NAMESPACE} -- \
  mysql -uroot -p${SOURCE_MYSQL_PASSWORD} -e "SHOW PROCESSLIST;" | grep -v "Sleep"

2. 更新应用连接字符串

# Update ConfigMap or environment variables
kubectl patch configmap <app-config> -n <app-namespace> --type=json \
  -p='[{"op": "replace", "path": "/data/database-host", "value":"mysql-8-target-read-write.'${TARGET_NAMESPACE}'.svc.cluster.local"}]'

kubectl patch configmap <app-config> -n <app-namespace> --type=json \
  -p='[{"op": "replace", "path": "/data/database-port", "value":"3306"}]'

3. 重启应用

# Scale up application
kubectl scale deployment <app-name> --replicas=<original-replica-count> -n <app-namespace>

# Wait for pods to be ready
kubectl -n <app-namespace> rollout status deployment <app-name>

4. 验证应用功能

# Test database connectivity
kubectl exec <app-pod> -n <app-namespace> -- \
  mysql -h mysql-8-target-read-write.${TARGET_NAMESPACE}.svc.cluster.local \
    -uroot -p${TARGET_MYSQL_PASSWORD} -e "SELECT 1 AS test;"

# Check application logs for errors
kubectl logs -n <app-namespace> <app-pod> --tail=100 | grep -i error

监控

对迁移后的实例进行 24-48 小时监控:

# Check MySQL 8.0 instance health
kubectl -n ${TARGET_NAMESPACE} get mysql ${TARGET_NAME} -w

# Monitor error logs
kubectl logs -n ${TARGET_NAMESPACE} ${TARGET_NAME}-0 -c mysql --tail=100 -f

灾难恢复

回滚计划

如果切换后发现关键问题:

# 1. Stop application
kubectl scale deployment <app-name> --replicas=0 -n <app-namespace>

# 2. Update connection string back to source
kubectl patch configmap <app-config> -n <app-namespace> --type=json \
  -p='[{"op": "replace", "path": "/data/database-host", "value":"'${SOURCE_NAME}'-proxysql.'${SOURCE_NAMESPACE}'.svc.cluster.local"}]'

# 3. Restart application
kubectl scale deployment <app-name> --replicas=<original-replica-count> -n <app-namespace>

# 4. Verify connectivity
kubectl exec <app-pod> -n <app-namespace> -- \
  mysql -h ${SOURCE_NAME}-proxysql.${SOURCE_NAMESPACE}.svc.cluster.local \
    -uroot -p${SOURCE_MYSQL_PASSWORD} -e "SELECT 1 AS test;"
WARNING

在迁移验证完成且回滚窗口结束之前,不要删除 PXC 自定义资源。

常见问题与解决方案

问题:GTID_PURGED 错误

症状:

ERROR 3546 (HY000) at line XX: Cannot update GTID_PURGED with the Group Replication plugin running

解决方案: 迁移步骤中已通过 grep -v "SET @@GLOBAL.GTID_PURGED" 过滤,问题已被处理。

问题:字符集转换错误

症状:

ERROR 1366 (HY000): Incorrect string value

解决方案:

kubectl exec ${TARGET_NAME}-0 -n ${TARGET_NAMESPACE} -c mysql -- \
  mysql -uroot -p${TARGET_MYSQL_PASSWORD} -e "
    ALTER DATABASE ${db} CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
  "

问题:DEFINER 权限错误

症状:

ERROR 1449 (HY000): The user specified as a definer ('user'@'host') does not exist

解决方案:

# Find all objects with missing definers
kubectl exec ${TARGET_NAME}-0 -n ${TARGET_NAMESPACE} -c mysql -- \
  mysql -uroot -p${TARGET_MYSQL_PASSWORD} -e "
    SELECT DISTINCT DEFINER
    FROM information_schema.VIEWS
    WHERE TABLE_SCHEMA = '${db}';
  "

问题:认证插件错误

症状:

ERROR 2059 (HY000): Authentication plugin 'caching_sha2_password' cannot be loaded

解决方案:

kubectl exec ${TARGET_NAME}-0 -n ${TARGET_NAMESPACE} -c mysql -- \
  mysql -uroot -p${TARGET_MYSQL_PASSWORD} -e "
    ALTER USER 'app_user'@'%' IDENTIFIED WITH mysql_native_password BY 'password';
    FLUSH PRIVILEGES;
  "

故障排查

诊断命令

检查迁移进度:

kubectl exec ${TARGET_NAME}-0 -n ${TARGET_NAMESPACE} -c mysql -- \
  mysql -uroot -p${TARGET_MYSQL_PASSWORD} -e "SHOW PROCESSLIST;"

kubectl exec ${SOURCE_NAME}-pxc-0 -n ${SOURCE_NAMESPACE} -- \
  mysql -uroot -p${SOURCE_MYSQL_PASSWORD} -e "SHOW PROCESSLIST;"

验证数据完整性:

for db in ${DATABASES}; do
  echo "=== Database: ${db} ==="
  kubectl exec ${SOURCE_NAME}-pxc-0 -n ${SOURCE_NAMESPACE} -- \
    mysql -uroot -p${SOURCE_MYSQL_PASSWORD} -N -e "
      SELECT TABLE_NAME, TABLE_ROWS
      FROM information_schema.TABLES
      WHERE TABLE_SCHEMA = '${db}' AND TABLE_TYPE = 'BASE TABLE'
      ORDER BY TABLE_NAME;
    " > /tmp/source_counts.txt

  kubectl exec ${TARGET_NAME}-0 -n ${TARGET_NAMESPACE} -c mysql -- \
    mysql -uroot -p${TARGET_MYSQL_PASSWORD} -N -e "
      SELECT TABLE_NAME, TABLE_ROWS
      FROM information_schema.TABLES
      WHERE TABLE_SCHEMA = '${db}' AND TABLE_TYPE = 'BASE TABLE'
      ORDER BY TABLE_NAME;
    " > /tmp/target_counts.txt

  diff /tmp/source_counts.txt /tmp/target_counts.txt || echo "Row count differences detected!"
done

检查 MySQL 8.0 错误日志:

kubectl logs -n ${TARGET_NAMESPACE} ${TARGET_NAME}-0 -c mysql --tail=100 -f | grep -i error

最佳实践

迁移前规划

  • 先在预生产环境测试:始终先在非生产环境执行测试迁移
  • 架构清理:在正式迁移前修复所有架构兼容性问题
  • 字符集迁移:尽早转换为 utf8mb4(建议提前 3-5 天)
  • 备份策略:确保迁移前可以获取最新备份
  • 维护窗口:根据数据库大小安排足够的停机时间

迁移期间

  • 停止应用写入:确保导出/导入期间无写入,以保证一致性
  • 监控进度:定期跟踪导出/导入进度
  • 保留源端运行:在验证迁移完成前不要删除源端

迁移后

  • 全面测试:对应用功能进行充分测试
  • 性能监控:监控 24-48 小时的查询性能
  • 保留源端用于回滚:保留源集群 24-48 小时作为回滚窗口
  • 更新文档:更新连接字符串和监控仪表盘

参考

大小与时间估算

数据库大小导出时间导入时间总停机时间
< 10GB1-5 分钟2-10 分钟15-30 分钟
10-50GB5-20 分钟10-30 分钟30-60 分钟
50-100GB20-40 分钟30-60 分钟1-2 小时
100-200GB40-80 分钟1-2 小时2-4 小时

mysqldump 标志参考

标志目的
--single-transaction使用 MVCC(InnoDB)获取一致性快照
--quick一次获取一行(节省内存)
--lock-tables=false不锁表(依赖 single-transaction)
--set-gtid-purged=ON包含 GTID 信息
--routines导出存储过程和函数
--events导出 event
--triggers导出 trigger
--databases指定要导出的数据库

有用链接