复制深度:异步 / 半同步 / Galera
MariaDB 三种复制拓扑的对比、故障转移、GTID、延迟排查
复制是高可用的基础,但每种方案都有不同的"一致性 vs 可用性"取舍。这一篇讲清楚怎么选、怎么排查。
三种主流模式
| 模式 | 一致性 | 写延迟 | 可用性 | 典型场景 |
|---|---|---|---|---|
| 异步 (async) | 最终一致 | 最低 | 主挂可能丢数据 | 读多写少、海量从库 |
| 半同步 (semi-sync) | 准强一致 | 主等至少 1 从 ack | 主挂数据基本不丢 | 中型业务,默认推荐 |
| Galera Cluster | 强一致 | 主等多数 ack | 单节点挂不影响 | 多写、跨 AZ |
异步复制
# 主
[mariadb]
server-id=1
log-bin=mysql-bin
binlog_format=ROW
gtid_strict_mode=ON
expire_logs_days=7
# 从
[mariadb]
server-id=2
relay-log=relay-bin
read-only=ON
log_slave_updates=ON # 从也写 binlog,便于级联启动复制:
-- 在主上
CREATE USER 'repl'@'%' IDENTIFIED BY 'xxx';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
-- 主备份 + 记 GTID
mariabackup --backup --target-dir=/backup -uroot
-- 从恢复 + 启复制
CHANGE MASTER TO
MASTER_HOST='primary',
MASTER_USER='repl',
MASTER_PASSWORD='xxx',
MASTER_USE_GTID=slave_pos;
START SLAVE;
SHOW SLAVE STATUS\G关键状态
SHOW SLAVE STATUS\G
-- Slave_IO_Running: Yes ← IO 线程,拉取 binlog
-- Slave_SQL_Running: Yes ← SQL 线程,应用 binlog
-- Seconds_Behind_Master: 0 ← 复制延迟
-- Gtid_Slave_Pos ← 当前 GTID延迟排查
SHOW SLAVE STATUS\G
-- Seconds_Behind_Master 持续 > 10 秒 → 报警常见原因:
- 从库硬件弱于主库 → 升配
binlog_format=STATEMENT触发慢 SQL → 改 ROW- 大事务(百万行 UPDATE)→ 拆批
- 从库被 OLTP 占用 → 拆专用副本
- 网络抖动 → 看
Last_IO_Error
平滑故障转移(手动)
-- 在从库等到追平
STOP SLAVE IO_THREAD;
-- 等 SQL 线程跑完
SHOW SLAVE STATUS\G -- Exec_Master_Log_Pos 与 Read_Master_Log_Pos 相同
-- 提升为新主
STOP SLAVE;
RESET SLAVE ALL;
SET GLOBAL read_only=OFF;
-- 把流量切过去自动故障转移用 MariaDB MaxScale 或 Orchestrator。
半同步复制
主在提交时等至少一个从 ack 写到 relay log,不丢数据(除非主和那一个从同时挂)。
-- 主
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
SET GLOBAL rpl_semi_sync_master_enabled = 1;
SET GLOBAL rpl_semi_sync_master_timeout = 1000; -- 1 秒等不到 ack 退回异步
-- 从
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
SET GLOBAL rpl_semi_sync_slave_enabled = 1;
STOP SLAVE IO_THREAD; START SLAVE IO_THREAD;监控:
SHOW STATUS LIKE 'Rpl_semi_sync%';
-- Rpl_semi_sync_master_status: ON
-- Rpl_semi_sync_master_clients: 1+
-- Rpl_semi_sync_master_no_tx: 0 (越大说明掉到异步越多)timeout 别设太低:网络抖动会让半同步退化到异步。但太高又会阻塞主库写入。1–5 秒是常用值。
Galera Cluster
完全不同的模型:多主写、同步、多数派。
[mariadb]
wsrep_on=ON
wsrep_provider=/usr/lib/galera/libgalera_smm.so
wsrep_cluster_address="gcomm://node1,node2,node3"
wsrep_cluster_name="prod"
wsrep_node_name=node1
wsrep_sst_method=mariabackup
wsrep_sst_auth=sst:sstpass
binlog_format=ROW
default_storage_engine=InnoDB
innodb_autoinc_lock_mode=2启动顺序:
# 首次:启动第一个节点
galera_new_cluster
# 其他节点
systemctl start mariadb监控
SHOW STATUS LIKE 'wsrep_cluster_size'; -- 应该等于节点数
SHOW STATUS LIKE 'wsrep_cluster_status'; -- Primary 才正常
SHOW STATUS LIKE 'wsrep_ready'; -- ON
SHOW STATUS LIKE 'wsrep_local_state_comment'; -- SyncedGalera 的特性
✅ 任何节点都可写 ✅ 节点挂掉不影响其他节点 ✅ 数据强一致
❌ 写并发不会线性扩展——每次写要全节点投票
❌ 长事务 / 大事务很难(容易触发 wsrep_max_ws_size)
❌ DDL 是全集群阻塞的(需要 wsrep_OSU_method=RSU 改 schema)
❌ 跨 region 慢(同步等远端 ack)
何时用 Galera
- 多 AZ 同写
- 不能容忍主备切换的秒级抖动
- 已经能控制写并发不爆炸
否则 半同步 + MaxScale 通常更香。
GTID(全局事务 ID)
MariaDB GTID 格式:<domain>-<server_id>-<seq>,例如 0-1-12345。
SELECT @@gtid_current_pos;
-- 0-1-12345,1-2-678关键参数
gtid_strict_mode=ON # 强校验,推荐生产
gtid_domain_id=0 # 多源复制时用不同 domain
log_slave_updates=ON # 从写 binlog,便于级联用 GTID 启复制(简单)
CHANGE MASTER TO
MASTER_HOST='primary',
MASTER_USER='repl',
MASTER_PASSWORD='xxx',
MASTER_USE_GTID=slave_pos;不需要手动记 binlog 文件名和位置——MariaDB 自己根据 GTID 找。
跨 region 复制
| 方案 | RPO | RTO | 复杂度 |
|---|---|---|---|
| 异步 binlog | 秒级到分钟级 | 主 region 挂可手动切 | 低 |
| Galera 跨 region | 同步(受网络限制) | 自动 | 高 |
| MariaDB MaxScale | 看后端 | 自动路由 | 中 |
| MariaDB Xpand (SkySQL) | 强一致 | 自动 | 商业方案 |
维护操作
跳过坏事务
-- 复制因为某个事务失败卡住
STOP SLAVE;
SET GLOBAL sql_slave_skip_counter = 1;
START SLAVE;危险!会导致从库与主数据不一致。只在确实需要时用。
重新做主从
# 在主上备份
mariabackup --backup --target-dir=/backup --slave-info
# 拷到从
rsync -av /backup/ slave:/restore/
# 从恢复 + 启复制
mariabackup --prepare --target-dir=/restore
systemctl stop mariadb
rm -rf /var/lib/mysql/*
mariabackup --copy-back --target-dir=/restore
chown -R mysql:mysql /var/lib/mysql
systemctl start mariadb
# 读 /restore/xtrabackup_slave_info 拿到 GTID,CHANGE MASTER多源复制(MariaDB 独有)
一个从库可以从多个主库拉数据:
CHANGE MASTER 'shard1' TO MASTER_HOST='s1', MASTER_USE_GTID=slave_pos;
CHANGE MASTER 'shard2' TO MASTER_HOST='s2', MASTER_USE_GTID=slave_pos;
START ALL SLAVES;适合:分库 → 汇总到一个分析库。