MariaDB Galera Cluster 实战:三节点多主同步复制、故障自动转移与跨机房部署取舍

MySQL 主从复制的痛点,用过的人都懂:从库是异步的,主库写入了、从库还没收到就宕机,这部分数据就丢了;主库挂了要手动切换,切的过程中应用报错;读写在应用层散落一地,改一次架构要动一堆代码。传统主从能解决"读扩展"和"备份",但解决不了"高可用"——真正的故障自动转移,需要别的方案。

MariaDB Galera Cluster 就是冲着这个问题来的。它提供多主同步复制:任意一个节点都能读写,写入时通过认证协议同步到所有节点,所有节点数据强一致,任意节点宕机集群继续服务,不用改一行应用代码(前提是表用 InnoDB)。这篇文章讲清楚 Galera 的原理、三节点集群的搭建、跨机房部署的坑,以及它到底适合什么样的个人站场景。

一、Galera 的核心:同步复制与认证协议

先建立一个最重要的认知:Galera 是"写集"(write set)级别的同步复制,不是语句级、也不是行级。它的工作流程是这样的:

  1. 客户端连到某个节点,执行事务提交。
  2. 该节点把这次事务的所有改动打包成一个 write set。
  3. write set 通过 galera 组通信广播给集群里所有其他节点。
  4. 其他节点做认证(certification)——检查这个 write set 和自己本地是否有冲突(比如是否同时改了同一行)。
  5. 认证通过后,所有节点(包括发起节点)才真正 apply 到本地存储引擎,然后返回提交成功。

这里有两个关键点。第一,提交必须等到所有节点确认才返回,所以它是同步的——一致性级别最高,但写延迟取决于最慢的那个节点。第二,认证阶段是冲突检测的核心:如果两个节点同时改了同一行,其中一个会在认证阶段被拒绝,返回死锁错误(ER_LOCK_DEADLOCK),应用需要重试。

这带来一个必须理解的行为:Galera 集群下,和主从不同,"读"也不一定能读到最新的数据?不,恰恰相反——因为提交是同步的,任何节点提交成功后,其他所有节点都已经有这份数据了。所以从任意节点读取都能读到最新值,这是 Galera 相对异步主从最大的优势(代价是写放大、延迟高)。

二、多主 vs 主从:什么时候选 Galera

Galera 的卖点是"任意节点可读写",但现实中往往不应该真的让所有节点都写。原因很简单:多主意味着并发写冲突的概率大增,认证失败(死锁)会更频繁,尤其是那些"读-改-写"逻辑(先 SELECT 再 UPDATE)在多个节点同时进行时几乎必然冲突。

所以业界的实际做法是:用 Galera 搭集群,但写入只走一个节点(单主模式),其他节点做只读和故障转移。这样既拿到了"节点挂了自动切换"的高可用,又避免了多主冲突。多主的价值在于——当主节点挂掉时,另一个节点立刻可以接手,不需要传统主从那样提升从库、改 VIP、改配置的一堆操作。

选型建议:

  • 需要高可用、不能容忍切换期间业务中断 → Galera。
  • 只需要读扩展、能容忍短暂停机 → 传统主从更简单、性价比更高。
  • 多机房分布式 → Galera 支持跨机房,但对网络延迟极度敏感,见后文。
  • 写入量极大、单表高并发 → 慎用 Galera,同步复制会成为写性能瓶颈。

三、前置条件:节点数、InnoDB、主机名

搭 Galera 有几个硬性要求,踩了这几个点基本搭不起来:

1. 至少三个节点。 Galera 用类 Paxos 的多数派机制仲裁。三节点能容忍一个节点故障(存活 2 个仍过半数);两节点是最糟糕的配置——任一个挂了剩下的都无法形成多数派。所以集群规模应该是 3、5、7 这样的奇数。

2. 所有表必须用 InnoDB 引擎。 MyISAM 等非事务引擎不支持 Galera 的复制,遇到 MyISAM 表会直接报错。建库前检查一遍:SELECT table_schema, table_name, engine FROM information_schema.tables WHERE engine != 'InnoDB';

3. 每张表必须有主键。 没有主键的表,认证阶段无法做行级冲突检测,会大幅降低性能。这是 Galera 的一条硬规则。

4. 主机名必须唯一且能互相解析。 Galera 节点间靠主机名通信。三台机器都要在 /etc/hosts 里配好彼此的 hostname → IP 映射,且 hostname 要和机器实际 hostname 一致。

四、三节点集群搭建(逐台操作)

假设三台机器:db1=10.0.0.11、db2=10.0.0.12、db3=10.0.0.13。

第一步:主机名与 hosts。每台机器执行(以 db1 为例):

hostnamectl set-hostname db1
echo "10.0.0.11 db1" >> /etc/hosts
echo "10.0.0.12 db2" >> /etc/hosts
echo "10.0.0.13 db3" >> /etc/hosts

第二步:安装 MariaDB 与 Galera。

apt update
apt install -y mariadb-server galera-4 rsync
systemctl stop mariadb

这里同时装了 rsync,因为新节点加入集群时默认用 rsync 做 SST(State Snapshot Transfer,全量数据同步)。

第三步:Galera 配置。在 /etc/mysql/mariadb.conf.d/60-galera.cnf 里写:

[mysqld]
binlog_format = ROW
default_storage_engine = InnoDB
innodb_autoinc_lock_mode = 2
innodb_flush_log_at_trx_commit = 0
query_cache_size = 0
query_cache_type = 0

bind-address = 0.0.0.0
wsrep_on = ON
wsrep_provider = /usr/lib/galera/libgalera_smm.so
wsrep_cluster_name = "my_personal_cluster"
wsrep_cluster_address = "gcomm://10.0.0.11,10.0.0.12,10.0.0.13"
wsrep_node_name = "db1"
wsrep_node_address = "10.0.0.11"
wsrep_sst_method = rsync

逐条解释关键项:

  • innodb_autoinc_lock_mode = 2:Galera 强制要求,因为多主场景下自增 ID 不能依赖表级锁。
  • innodb_flush_log_at_trx_commit = 0:官方推荐值,配合 Galera 的同步机制使用,能显著降低写延迟。
  • query_cache 关闭:Galera 下查询缓存会导致各节点数据不一致,必须关。
  • wsrep_cluster_address:列出所有节点地址。注意这是"首节点引导"用的列表——正常节点启动时它会去连这些地址,找到集群后加入。
  • wsrep_sst_method = rsync:全量同步方式。rsync 会锁表(阻塞写),xtrabackup-v2 不锁表(推荐生产用,但要装 percona-xtrabackup)。

注意:每台机器只有 wsrep_node_name 和 wsrep_node_address 不同,其余配置一致。

五、引导第一个节点(关键步骤,最容易出错)

集群的第一个节点必须用"引导"方式启动,否则它会尝试去连接一个还不存在的集群,一直失败重试:

# 只在 db1 上执行
galera_new_cluster

这个命令做的事,本质是把 wsrep_cluster_address 临时设成 gcomm://(空地址)启动,表示"我是新集群的起点"。启动后验证:

mysql -e "SHOW STATUS LIKE 'wsrep_cluster_size';"

应该看到 wsrep_cluster_size 1——集群里目前就它一个。

然后正常启动 db2 和 db3:

# db2 和 db3 上执行
systemctl start mariadb

它们会用配置里的 wsrep_cluster_address 找到 db1、加入集群,并触发 SST 把 db1 上的全量数据同步过来。稍等片刻,在任意节点查:

mysql -e "SHOW STATUS LIKE 'wsrep_cluster_size';"
mysql -e "SHOW STATUS LIKE 'wsrep_cluster_status';"
mysql -e "SHOW STATUS LIKE 'wsrep_ready';"

期望结果:cluster_size = 3、cluster_status = Primary、wsrep_ready = ON。三项都对了,集群才算真正健康。

六、验证同步复制:写一个节点,读其他节点

在 db1 上建库建表并插入:

mysql -e "CREATE DATABASE IF NOT EXISTS testdb;"
mysql -e "CREATE TABLE testdb.t (id INT PRIMARY KEY, v VARCHAR(50)) ENGINE=InnoDB;"
mysql -e "INSERT INTO testdb.t VALUES (1,'hello galera');"

立刻在 db2 和 db3 上查:

mysql -e "SELECT * FROM testdb.t;"

数据已经在了——注意是"立刻",没有主从那种延迟。这直观地展示了同步复制的意义。

七、故障演练:干掉一个节点

在 db3 上直接停库模拟宕机:

# db3 上
systemctl stop mariadb

然后回到 db1 观察集群状态:

mysql -e "SHOW STATUS LIKE 'wsrep_cluster_size';"   # 变成 2
mysql -e "SHOW STATUS LIKE 'wsrep_cluster_status';" # 仍是 Primary
mysql -e "SHOW STATUS LIKE 'wsrep_cluster_members';"

集群规模从 3 变 2,但状态依然是 Primary——业务继续可用,写读都不受影响。这就是 Galera 高可用的体现:三节点集群中挂一台,剩下的两台仍能形成多数派(2 > 3/2),照常服务。

再把 db3 启动回来:

# db3 上
systemctl start mariadb

它会自动重新加入集群,把宕机期间遗漏的数据同步(IST 增量同步或 SST 全量,取决于差异量),几分钟后 wsrep_cluster_size 回到 3。全程零人工干预——对比传统主从的故障转移流程,这是巨大的运维优势。

但要注意:如果你把 db2 和 db3 同时干掉,只剩 db1 一个节点,它会失去多数派,进入 Non-Primary 状态、拒绝写入。这是 Galera 的保护机制,不是 bug——防止脑裂时两边各自写导致数据分叉。恢复时必须手动引导其中一个节点(galera_new_cluster),这一点官方文档有专节讲,务必读懂再操作。

八、跨机房部署:能,但很痛

站长常常幻想"两个机房各放一台,机房全挂也不怕"。Galera 确实支持跨机房,但它的同步复制对网络延迟极度敏感——提交延迟 = 最慢节点的一次网络往返。同机房内网延迟通常 0.1~0.5ms,跨机房可能 10~50ms,写入吞吐会被直接砍到几分之一。

如果一定要跨机房,两个原则:

  • 多数派放在主机房。 例如主机房三节点 + 备机房一节点(甚至备机房只放一个仲裁节点 arbiter),保证主机房独立时仍能形成多数派。绝不要把节点平均分到两个机房(2+2),那样任何一边断了都无法形成多数派。
  • 用 gmcast.segment 划分网段。 Galera 支持把节点标记成不同 segment,让复制流量优先在 segment 内传播,减少跨机房往返。

但说实话,对于个人站点的数据量,与其折腾跨机房 Galera,不如老老实实"单机房三节点 Galera + 异地异步备份"。高可用(Galera)解决的是"机器坏了服务不断",异地备份解决的是"机房没了数据还在"——两者是不同层次的需求,不要用 Galera 去替代备份。

九、五个必踩的坑

坑一:用两节点搭集群。两节点没有多数派意义,一台挂了集群就不可用,等于白搭。最小规模是三节点,节点数要奇数。

坑二:忘了 galera_new_cluster,一直用 systemctl start。首次启动集群时必须用 galera_new_cluster 引导,否则节点反复尝试连接一个不存在的集群,wsrep_ready 永远是 OFF。已引导过之后,后续启动一律用 systemctl start,不能再用 galera_new_cluster(会尝试另起一个新集群)。

坑三:表没有主键,或者混入 MyISAM。无主键表导致认证阶段全表扫描、性能暴跌;MyISAM 表直接无法复制。上 Galera 前必须审计表结构。

坑四:SST 用 rsync 又赶上大库。rsync 做 SST 会锁住 donor 节点的表(FLUSH TABLES WITH READ LOCK),大库同步期间业务写入全被阻塞。生产环境应换成 xtrabackup-v2,或者给集群单开一个"donor"节点专门做 SST 数据源,不影响业务节点。

坑五:应用层不处理认证死锁。多主模式下,冲突的写入会在认证阶段被拒,应用收到 ER_LOCK_DEADLOCK。这是 Galera 的正常行为,应用必须有重试逻辑(捕获该错误码、退回事务、重试 2~3 次)。如果直接从主从切过来而不改代码,会看到大量莫名其妙的死锁报错。

十、个人站长该不该上 Galera

诚实地讲:Galera 不是所有个人站的答案。它的复杂度、资源消耗(三台机器)、以及对网络和维护能力的要求都不低。给一个决策参考:

  • 单机跑站点、数据量不大、能接受几小时停机恢复:不需要 Galera。一份自动化备份 + 快速恢复脚本性价比高得多。
  • 站点有付费用户、对停机敏感、已有两台以上服务器:值得上。三节点里一台做只读从库其实也常见。
  • 单纯想扩读、可以接受切换期短暂中断:传统一主多从 + 中间件(ProxySQL)更简单。

Galera 的真正价值不是"多主并发写",而是"故障自动转移、无需人工干预"。想清楚你要的是高可用,而不是把三台机器都用满写能力,选型就不会跑偏。对于把网站当正经事业在做的站长,花几天时间把 Galera 搭起来、把故障演练跑一遍,是值得的投资——因为它换来的,是半夜服务器宕机时你能安心睡觉的底气。

Last modification:October 6th, 2026 at 09:25 pm

Leave a Comment