MariaDB 中文社区

从 MySQL 8.0 迁移到 MariaDB 11.x

跨分支迁移的完整 checklist——风险点、工具、代码差异、切流方案

MySQL 8 ↔ MariaDB 11 不再是简单 drop-in 兼容。两个分支自 2020 年起明显分叉。这一篇假设你的生产负载非平凡,需要严肃测试。

谁应该迁?

  • 想脱离 Oracle 控制(许可、商业模式风险)
  • 想用 ColumnStore / Xpand / VECTOR 等 MariaDB 独有特性
  • 想要更彻底的开源(HeatWave 闭源)
  • 想要更活跃的社区路线

谁应该迁?

  • 用了 MySQL 8 的 JSON_TABLE 重度查询
  • 用了 X-Plugin / X Protocol
  • 用了 Group Replication 集群(迁过去要换 Galera)
  • 重度依赖 InnoDB Cluster 工具链
  • 用了 caching_sha2_password 且客户端不易改

风险点全集

1. 默认 utf8mb4 collation 不同

数据库默认
MySQL 8utf8mb4_0900_ai_ci
MariaDB 11.5+utf8mb4_uca1400_ai_ci
MariaDB 11.4utf8mb4_general_ci

影响

  • 排序结果可能不同
  • ORDER BY 后端分页顺序变
  • 唯一约束哈希不同 → 同样的字符串可能不再"相同"

对策:导出时把 collation 替换:

sed -i 's/utf8mb4_0900_ai_ci/utf8mb4_unicode_ci/g' dump.sql
sed -i 's/utf8mb4_0900_as_cs/utf8mb4_unicode_ci/g' dump.sql

2. 认证插件

MySQL 8 默认 caching_sha2_passwordMariaDB 不存在

-- 在 MariaDB 端重建账号
CREATE USER 'app'@'%' IDENTIFIED VIA mysql_native_password USING PASSWORD('xxx');
GRANT ... TO 'app'@'%';

老客户端驱动也需要支持 mysql_native_password

3. JSON 函数差异

功能MySQL 8MariaDB 11
JSON_TABLE✅ 完整⚠️ 部分实现
JSON_VALUE
JSON_OVERLAPS⚠️ 8.0.17+ 才有,11.x 部分支持
->> 运算符
JSON_TABLE 嵌套

如果你的报表查询用了 JSON_TABLE 拆数组,需要改写成 CROSS APPLY 或物化成关系表。

4. CTE 性能差异

两边都支持 CTE,但 MariaDB 10.6+ 引入了 CTE inlining,有时比 MySQL 8 快。EXPLAIN 二者输出格式略不同。

5. 优化器 hint

MySQL 8 的 /*+ HASH_JOIN(...) */ 在 MariaDB 不识别(MariaDB 已自动选 hash join)。STRAIGHT_JOIN 两边都支持。

6. 存储引擎

引擎MySQL 8MariaDB 11
InnoDB
MyRocks❌(除非自编)
ColumnStore
Aria
旧 MyISAM✅(建议别用)

7. Galera 替代 Group Replication

MySQL 8 的 Group Replication(InnoDB Cluster 一部分)不能搬到 MariaDB。MariaDB 用 Galera——拓扑相似但配置文件不同。

# MariaDB Galera
[mariadb]
wsrep_on=ON
wsrep_provider=/usr/lib/galera/libgalera_smm.so
wsrep_cluster_address="gcomm://node1,node2,node3"
wsrep_cluster_name="my_cluster"
wsrep_node_name=node1
wsrep_sst_method=mariabackup

8. 复制 GTID 格式不同

MariaDB GTID 格式:<domain>-<server_id>-<seq> MySQL GTID 格式:<server_uuid>:<seq>

两边不能直接复制(无法把 MariaDB 当 MySQL 8 的从库或反之)。要迁移用 dump + reload。

9. 加密表空间格式不同

如果用了 InnoDB Transparent Data Encryption,两边密钥环 / 加密格式不兼容,必须解密再迁。

10. 系统表差异

MySQL 8 用了 InnoDB 系统表(mysql.user 是 InnoDB),MariaDB 用 Aria。重建即可:

mariadb-install-db --user=mysql --basedir=/usr --datadir=/var/lib/mysql

迁移方案对照

方案停机难度适合
离线 dump + reload几小时~几天小库、可停机
物理拷贝(仅同版本)❌ 不可行(格式差异)
逻辑复制工具分钟级⭐⭐需要持续追平
双写切流0⭐⭐⭐关键业务

推荐:双写切流

1. 在 MariaDB 端准备空库

docker run -d --name mariadb-target \
  -e MARIADB_ROOT_PASSWORD=xxx \
  -p 3307:3306 mariadb:11.4

2. 初始全量同步

mysqldump -h mysql-source --single-transaction --routines --triggers \
  --set-gtid-purged=OFF --all-databases | \
  sed 's/utf8mb4_0900_ai_ci/utf8mb4_unicode_ci/g' | \
  mariadb -h mariadb-target -uroot -pxxx

3. 用 Debezium / Maxwell 持续同步 binlog

# Debezium connector
name: mysql-to-mariadb
config:
  connector.class: io.debezium.connector.mysql.MySqlConnector
  database.hostname: mysql-source
  database.server.id: 184054
  database.include.list: app
  table.include.list: app.*
  topic.prefix: app

下游写一个 sink 消费 binlog 应用到 MariaDB(或用 GoldenGate / Striim)。

4. 应用层双写

async function write(sql, params) {
  await Promise.all([
    sourcePool.query(sql, params),
    targetPool.query(sql, params).catch((e) => log.warn('shadow write fail', e)),
  ]);
}

5. 跑数据一致性对比

# 工具:pt-table-checksum / mysql-table-sync / 自写
pt-table-checksum --replicate=app.checksums h=source --recursion-method=hosts

6. 切只读流量到 MariaDB

灰度 5% → 50% → 100%,观察 1 周。

7. 切写流量

应用层 feature flag 切,秒级回滚预案保留 2 周。

必须改的代码

  1. 驱动连接字符串里的 auth plugin
  2. utf8mb4 collation 在 ORM 配置里固定(避免运行时检测出错)
  3. JSON_TABLE 重度使用的 SQL 重写
  4. 依赖 INFORMATION_SCHEMA.INNODB_* 表的监控 重命名为 MariaDB 等价
  5. mysql shell → mariadb shell(兼容但建议)

验证清单

  • 所有应用层 e2e 测试通过
  • 主键唯一性检查(重 dump 后做)
  • pt-upgrade 跑过一遍,对比关键查询结果
  • 性能基准:sysbench oltp_read_write 持平或更好
  • 慢日志数量没暴涨
  • 备份策略改用 mariabackup(而非 XtraBackup
  • 监控 dashboard 改用 MariaDB 兼容版本
  • DBA 培训:会用 SHOW REPLICA STATUS(MariaDB 11.4+ 标准)

常见踩坑

  1. mysql.user 表结构差异:直接拷贝会失败,必须用 mariadb-install-db 初始化再 GRANT
  2. general_log 行为差异:MariaDB 写更详细
  3. tmp 目录满:MariaDB 优化器对大查询更激进,临时文件可能更大
  4. HikariCP 连接池:默认 validateConnection/* mysql-connector */ 注释,要换成 SELECT 1

参考

本页目录