1. 为什么单机数据库迟早会撞上那堵墙
做后端的人大概都有过这样的经历:项目初期一台数据库服务器跑得好好的,业务量一上来,读写压力陡增,最先扛不住的往往就是数据库。加索引、优化慢查询、升级硬件,这些手段都用过一遍之后,你会发现单机MySQL的性能天花板其实很有限。更让人头疼的是高可用问题——那台机器一旦宕机,整个系统就跟着瘫痪,业务方电话打过来的时候,你只能一边冒汗一边重启服务。
我在几年前接手过一个内部管理系统,日活不算高,但数据写入非常频繁,单机MySQL在高峰期经常出现主从延迟,读请求分发到从库之后拿到的是旧数据,业务侧反馈了好几次“刚提交的数据查不到”。当时用的是传统主从复制,异步模式下主库写完就返回,从库什么时候同步完完全看运气。后来我们开始调研多主同步方案,才真正接触到Galera Cluster这套数据库集群技术。
Galera Cluster的核心价值在于:它提供了一种多主同步复制的集群方案,任意一个节点都可以接受读写请求,事务在提交时会同步复制到集群中所有节点,保证数据强一致性。这意味着你不再需要担心主从延迟导致的数据不一致问题,也不用在应用层做读写分离的复杂路由。任何一个节点挂了,其他节点仍然可以正常提供服务,客户端只需要切换到存活节点即可。
这篇文章适合哪些人看?如果你正在为数据库高可用方案选型,或者已经决定用Galera Cluster但不确定安装配置过程中有哪些坑,又或者你只是听说过这个名字想了解它到底怎么回事,那接下来的内容应该能帮到你。我会从架构原理讲起,然后一步步走完安装配置的完整流程,最后重点分享我在实际部署中踩过的坑和总结出来的经验。整个过程基于MySQL 8.0 + Galera 4的组合来演示,这也是目前生产环境中比较主流的搭配。
2. Galera Cluster的同步复制机制到底怎么运转
2.1 从异步复制到同步复制:本质区别在哪里
要理解Galera Cluster,得先搞清楚它和传统MySQL主从复制的根本差异。传统主从复制是异步的:主库执行完事务,写入binlog,然后返回给客户端成功。从库通过IO线程拉取binlog,再通过SQL线程回放,这个过程是独立于主库事务提交的。也就是说,主库提交成功的那一刻,从库可能还没收到binlog,更别说应用了。如果主库这时候宕机,那些还没同步到从库的事务就丢了。
Galera Cluster用的是同步复制模型。当一个事务在某个节点上提交时,该节点会把写集(write set)广播给集群中所有其他节点,每个节点都要确认收到并可以应用这个写集之后,发起节点才会真正提交事务并返回成功给客户端。这个过程中,所有节点要么都提交,要么都不提交,不存在中间状态。这就是所谓的“认证提交”(certification-based replication)。
注意:Galera的“同步”指的是事务在集群层面的同步认证,而不是说所有节点的磁盘数据时刻完全一致。实际数据应用是异步进行的,但认证通过意味着所有节点都保证能应用这个写集。
2.2 写集广播与认证:一次事务提交的完整旅程
具体来说,一次事务提交在Galera Cluster中会经历以下几个阶段:
- 本地执行:客户端连接到某个节点,发起事务。该节点在本地执行事务,生成写集(包含修改了哪些行、修改前后的值等信息)。
- 广播写集:本地执行完成后,节点将写集广播给集群中所有其他节点。
- 全局认证:每个节点收到写集后,会进行认证检查——判断这个写集是否与之前已经认证过的写集存在冲突。如果存在冲突(比如两个事务修改了同一行),则后到的那个会被回滚。
- 本地应用:认证通过后,各节点将写集应用到本地数据库。
- 返回结果:发起节点收到所有节点的认证确认后,向客户端返回提交成功。
这个流程的关键在于认证阶段。Galera使用了一种叫做“认证数据库”的内存结构来记录最近认证过的写集,通过比较写集的键值来判断冲突。这也是为什么Galera对高并发写入场景比较敏感——冲突概率会随着并发度上升而增加。
2.3 多主架构下的冲突处理与流量控制
多主架构最大的优势是任意节点可写,但这也带来了冲突问题。假设两个客户端同时分别向节点A和节点B发起事务,都修改了同一行数据。两个节点几乎同时广播写集,认证时必然有一个会失败。失败的那个事务会被回滚,客户端收到死锁或冲突错误,需要重试。
这种冲突在高并发场景下会变得频繁,所以Galera引入了流量控制机制。当某个节点的认证队列积压过多时,它会向集群发送流控信号,让其他节点暂停广播新的写集,等队列消化后再继续。这个机制保证了集群不会因为某个节点处理不过来而雪崩,但代价是整体吞吐量会受到影响。
实际使用中,如果你的业务写入冲突率很高,Galera可能不是最优选择。它更适合读多写少或者写入冲突概率低的场景。这一点在选型时一定要想清楚,不要因为“多主”听起来很美好就盲目上马。
2.4 SST与IST:新节点加入时数据怎么同步
当一个新节点加入集群时,它需要从现有节点获取全量数据,这个过程叫做SST(State Snapshot Transfer)。SST有三种方式:
- mysqldump:逻辑备份,速度慢,适合小数据量,会锁表。
- rsync:物理拷贝,速度较快,但需要锁表。
- xtrabackup:物理热备,不锁表,适合大数据量,是生产环境的首选。
如果新节点之前已经加入过集群,只是短暂离线后重新加入,并且离线期间产生的写集还在其他节点的缓存中,那么它可以只同步缺失的那部分数据,这叫IST(Incremental State Transfer)。IST比SST快得多,所以尽量让节点在离线后能通过IST恢复,而不是触发全量SST。
提示:IST能否成功取决于
gcache.size参数的大小。如果离线时间太长,写集已经被挤出缓存,就只能走SST了。生产环境建议把gcache.size设置得足够大,比如1G到4G,具体看写入频率。
3. 安装前的环境准备与关键参数决策
3.1 操作系统与MySQL版本的选择逻辑
Galera Cluster对操作系统没有特别苛刻的要求,主流Linux发行版都可以。我一般用CentOS 7或者Ubuntu 20.04,这两个系统的软件包管理和依赖处理都比较成熟。不过要注意,不同发行版的防火墙和SELinux策略不一样,后面配置的时候需要针对性处理。
MySQL版本方面,Galera 4对应的是MySQL 8.0,Galera 3对应MySQL 5.7。新项目直接上Galera 4 + MySQL 8.0,老项目如果已经在用5.7,升级到8.0需要评估兼容性。我建议新部署一律用8.0,因为Galera 4在流控、认证算法、大事务处理上都有明显改进。
具体版本号上,MySQL选8.0.32以上的小版本,Galera选4.14以上。这些版本修复了不少早期版本的bug,稳定性更好。安装方式我推荐用官方源或者Percona的源,不要用系统自带的MySQL包,因为系统自带的往往版本较旧,而且可能缺少Galera需要的某些编译选项。
3.2 节点数量与网络延迟的硬性约束
Galera Cluster的节点数量建议是奇数,最少3个。为什么是奇数?因为集群需要多数派(quorum)来决策,3个节点允许1个故障,5个节点允许2个故障。偶数节点(比如4个)在脑裂场景下无法形成多数派,反而更危险。
网络延迟是Galera的硬约束。因为每次事务提交都要等待所有节点的认证确认,所以节点间的网络往返时间(RTT)直接影响写入性能。官方建议节点间RTT在5ms以内,最好是在同一个机房或者同城数据中心。跨地域部署基本不可行,除非你能接受极低的写入吞吐量。
我见过有人把节点分布在三个不同的云可用区,RTT大概2-3ms,跑起来还可以。但如果跨城市,RTT超过20ms,写入性能会断崖式下降。这一点在规划阶段就要确认好,不要等部署完了才发现网络不行。
3.3 端口规划与防火墙策略
Galera Cluster需要开放以下端口:
| 端口 | 协议 | 用途 |
|---|---|---|
| 3306 | TCP | MySQL客户端连接 |
| 4567 | TCP | Galera集群通信(组播/单播) |
| 4568 | TCP | IST增量同步 |
| 4444 | TCP | SST全量同步 |
| 4567 | UDP | 组播通信(如果启用) |
防火墙方面,如果用的是firewalld,需要把这些端口都放行。SELinux如果开启,还需要配置相应的策略,否则Galera可能无法正常通信。我一般建议在测试环境先关闭SELinux和防火墙,确认集群能正常跑起来之后,再逐步加上安全策略。这样排错的时候少一个变量。
注意:生产环境不要长期关闭防火墙和SELinux,这是安全底线。测试通过后一定要把规则补上,并且验证集群仍然正常。
3.4 系统内核参数的调优建议
Galera Cluster对系统资源有一些要求,特别是文件描述符和网络缓冲区。以下是我常用的sysctl配置:
# /etc/sysctl.d/99-galera.conf net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 net.ipv4.tcp_rmem = 4096 87380 16777216 net.ipv4.tcp_wmem = 4096 65536 16777216 net.core.netdev_max_backlog = 65536文件描述符方面,修改/etc/security/limits.conf:
mysql soft nofile 65535 mysql hard nofile 65535这些参数的作用是让Galera在高并发通信时不会因为缓冲区不足而丢包,也不会因为文件描述符耗尽而拒绝连接。别小看这些细节,我遇到过好几次集群莫名其妙不稳定,最后查出来是文件描述符不够。
4. 逐节点安装与集群初始化实操
4.1 安装MySQL与Galera插件包
以CentOS 7为例,先配置Percona源:
yum install -y https://repo.percona.com/yum/percona-release-latest.noarch.rpm percona-release setup ps80然后安装MySQL Server和Galera:
yum install -y percona-server-server percona-xtrabackup-80 percona-server-galeraUbuntu的话用apt:
wget https://repo.percona.com/apt/percona-release_latest.generic_all.deb dpkg -i percona-release_latest.generic_all.deb percona-release setup ps80 apt install -y percona-server-server percona-xtrabackup-80 percona-server-galera安装完成后,先不要启动MySQL服务。因为Galera需要在MySQL启动之前就加载wsrep插件,所以我们要先改配置。
4.2 配置文件的关键参数逐项解读
Galera的配置主要写在/etc/my.cnf或者/etc/mysql/my.cnf中。以下是一个节点的完整配置示例:
[mysqld] # 基础配置 datadir = /var/lib/mysql socket = /var/lib/mysql/mysql.sock pid-file = /var/run/mysqld/mysqld.pid log-error = /var/log/mysql/error.log # Galera配置 wsrep_on = ON wsrep_provider = /usr/lib64/galera-4/libgalera_smm.so wsrep_cluster_name = "my_galera_cluster" wsrep_cluster_address = "gcomm://192.168.1.101,192.168.1.102,192.168.1.103" wsrep_node_name = "node1" wsrep_node_address = "192.168.1.101" wsrep_sst_method = xtrabackup-v2 wsrep_sst_auth = "sstuser:password" # 复制相关 binlog_format = ROW default_storage_engine = InnoDB innodb_autoinc_lock_mode = 2 innodb_flush_log_at_trx_commit = 0 # 其他优化 wsrep_slave_threads = 4 wsrep_sst_donor = "node2,node3" gcache.size = 2G逐项解释一下关键参数:
wsrep_provider:指定Galera插件库的路径。不同发行版路径可能不同,用find / -name "libgalera_smm.so"确认。wsrep_cluster_address:集群中所有节点的地址列表。第一个启动的节点可以写gcomm://(空地址),表示初始化新集群。wsrep_node_address:当前节点的IP,必须和其他节点能互通。wsrep_sst_method:SST方式,生产环境强烈建议用xtrabackup-v2。wsrep_sst_auth:SST用户和密码,需要提前在MySQL中创建。innodb_autoinc_lock_mode = 2:Galera要求设置为2(交错模式),否则可能死锁。innodb_flush_log_at_trx_commit = 0:这个参数降低持久性换取性能,因为Galera本身已经保证了集群层面的同步,单节点的刷盘可以适当放松。但如果对数据安全性要求极高,可以设为1。
4.3 第一个节点引导启动的完整流程
所有节点配置完成后,先启动第一个节点。注意,第一个节点要用--wsrep-new-cluster参数启动,表示初始化新集群:
systemctl stop mysql mysqld_safe --wsrep-new-cluster &或者用systemd的话:
systemctl start mysql@bootstrap启动后检查集群状态:
SHOW STATUS LIKE 'wsrep_cluster_size';如果返回1,说明第一个节点已经成功初始化。然后创建SST用户:
CREATE USER 'sstuser'@'localhost' IDENTIFIED BY 'password'; GRANT RELOAD, LOCK TABLES, PROCESS, REPLICATION CLIENT ON *.* TO 'sstuser'@'localhost'; FLUSH PRIVILEGES;这个用户是给xtrabackup做SST时用的,权限必须给够,否则SST会失败。
4.4 后续节点加入与状态验证
第一个节点跑起来之后,启动第二个节点:
systemctl start mysql第二个节点会自动连接到集群,触发SST从第一个节点同步数据。这个过程的时间取决于数据量大小,可以在error log中看到进度。SST完成后,检查集群状态:
SHOW STATUS LIKE 'wsrep_cluster_size';应该返回2。然后启动第三个节点,同样等待SST完成,最终wsrep_cluster_size应该返回3。
验证集群健康状态,重点看这几个指标:
SHOW STATUS LIKE 'wsrep_cluster_status'; -- 应该是Primary SHOW STATUS LIKE 'wsrep_connected'; -- 应该是ON SHOW STATUS LIKE 'wsrep_ready'; -- 应该是ON SHOW STATUS LIKE 'wsrep_local_state_comment'; -- 应该是Synced如果wsrep_local_state_comment不是Synced,说明节点还在同步或者有问题,需要查error log。
5. 那些让我熬夜排查的坑与修复过程
5.1 SST失败:xtrabackup版本不匹配的连锁反应
第一次部署的时候,SST一直失败,error log里报的是xtrabackup相关错误。排查了半天,发现是xtrabackup的版本和MySQL版本不匹配。MySQL 8.0.32需要xtrabackup 8.0.32,但我装的是8.0.28,导致备份文件格式不兼容。
修复方法很简单,卸载旧版xtrabackup,安装对应版本:
yum remove percona-xtrabackup-80 yum install percona-xtrabackup-80-8.0.32但这个问题的排查过程很折磨人,因为error log里的报错信息不够明确,只说是“SST failed”,没有直接指出是版本问题。后来我养成了一个习惯:部署前先确认MySQL、Galera、xtrabackup三者的版本兼容性,官方文档有对应的兼容矩阵,花五分钟查一下能省几小时排错。
5.2 节点加入后一直卡在Joining状态
有一次新节点加入集群,wsrep_local_state_comment一直显示Joining,过了很久也没变成Synced。查error log发现是IST失败了,然后尝试SST,但SST又因为磁盘空间不足失败了。
这里有两个问题:一是gcache.size设得太小(默认只有128M),新节点离线稍微久一点就无法IST;二是SST需要大量磁盘空间,xtrabackup会在数据目录下创建临时文件,如果磁盘剩余空间不够就会失败。
修复方法是先把gcache.size调大到2G,然后清理磁盘空间,重新加入节点。调整gcache.size需要重启节点,所以最好在部署初期就设置好。
提示:
gcache.size的大小应该根据你的写入频率来估算。如果每秒写入1MB,离线1小时需要3.6G的缓存。实际设置时可以稍微大一点,留些余量。
5.3 多主写入冲突导致的死锁频发
上线初期,业务侧反馈偶尔会出现死锁错误。查了之后发现是两个节点同时修改同一行数据导致的认证冲突。虽然Galera会自动回滚其中一个事务,但应用层没有做重试,所以用户看到了错误。
这个问题本质上不是Galera的bug,而是多主架构的固有特性。解决方案有两个方向:一是应用层做重试,捕获死锁错误后自动重试;二是调整业务逻辑,尽量避免并发修改同一行。我们最后是两者结合,应用层加了重试机制,同时把一些高频更新的计数器类操作改成了异步处理。
如果冲突非常频繁,那可能要考虑是不是真的适合用多主。有些场景下,单主写入(其他节点只读)反而更稳定。Galera支持把某个节点设为非主节点,只接受读请求,这样就能避免写入冲突。
5.4 集群脑裂后的恢复操作
有一次机房网络抖动,三个节点之间的通信中断了,集群分裂成了两个部分。由于是3节点,一个部分有2个节点,另一个部分有1个节点。多数派那边继续提供服务,少数派那边变成了非Primary状态,拒绝写入。
网络恢复后,少数派节点需要重新加入集群。这时候如果直接启动,它可能会尝试IST,但如果离线期间写集已经被挤出gcache,就会走SST。SST会清空该节点的数据,从其他节点全量同步。
恢复操作的关键是:先确认多数派那边的集群状态正常,然后把少数派节点的MySQL停掉,清理数据目录(如果需要SST),再重新启动。启动后它会自动加入集群并同步数据。
注意:脑裂恢复时千万不要同时启动所有节点,否则可能再次分裂。一定要先确保多数派集群稳定运行,再逐个加入少数派节点。
6. 生产环境下的监控与日常维护要点
6.1 必须监控的核心指标清单
Galera Cluster有一套自己的状态变量,以下这些是必须监控的:
| 指标 | 含义 | 告警阈值 |
|---|---|---|
| wsrep_cluster_size | 集群节点数 | 小于预期节点数 |
| wsrep_cluster_status | 集群状态 | 非Primary |
| wsrep_connected | 是否连接到集群 | OFF |
| wsrep_ready | 是否准备好接受查询 | OFF |
| wsrep_local_state_comment | 本地状态 | 非Synced |
| wsrep_flow_control_paused | 流控暂停比例 | 持续大于0.05 |
| wsrep_cert_deps_distance | 认证依赖距离 | 持续大于100 |
| wsrep_local_recv_queue_avg | 接收队列平均长度 | 持续大于10 |
这些指标可以通过SHOW STATUS获取,也可以用Prometheus的mysqld_exporter采集。我一般会配置告警规则,一旦wsrep_cluster_status不是Primary或者wsrep_ready是OFF,立即发通知。
6.2 流控频繁触发的排查思路
流控(flow control)是Galera的自我保护机制,但如果频繁触发,说明集群处理能力跟不上写入速度。排查思路如下:
- 先看
wsrep_flow_control_paused的值,如果持续大于0.05,说明流控比较严重。 - 检查各节点的
wsrep_local_recv_queue_avg,看是哪个节点处理不过来。 - 如果是某个节点磁盘IO慢,考虑升级磁盘或者调整
wsrep_slave_threads。 - 如果是所有节点都慢,可能是写入量确实超过了集群承载能力,需要考虑分库分表或者换方案。
wsrep_slave_threads的调整需要谨慎,不是越大越好。一般设置为CPU核心数的1-2倍比较合适。设置太大反而会增加上下文切换开销。
6.3 节点滚动重启的标准操作流程
生产环境难免需要重启节点(比如升级版本、调整参数)。Galera支持滚动重启,即一次只重启一个节点,保证集群始终有足够节点在线。
标准流程如下:
- 确认集群当前状态健康,所有节点都是Synced。
- 选择第一个节点,停止MySQL服务。
- 等待该节点完全停止后,进行维护操作(改配置、升级等)。
- 启动该节点,等待其状态变为Synced。
- 确认集群状态正常后,再对下一个节点重复上述步骤。
整个过程的关键是一次只动一个节点,并且每次都要确认节点恢复Synced后再继续。如果某个节点启动后一直无法Synced,要先排查问题,不要继续操作其他节点。
6.4 备份策略:逻辑备份与物理备份的取舍
Galera Cluster本身提供了数据冗余,但这不能替代备份。因为如果发生误操作(比如误删表),Galera会把这个操作同步到所有节点,冗余反而变成了灾难。
备份策略我一般建议:
- 每日逻辑备份:用mysqldump或者mydumper,备份到远程存储。逻辑备份恢复灵活,可以单表恢复。
- 每周物理备份:用xtrabackup做全量备份,速度快,适合大数据量。
- binlog备份:如果开启了binlog,也要定期备份,用于时间点恢复。
备份操作最好在单独的从节点上做,避免影响主集群性能。如果集群规模较小,也可以在业务低峰期在某个节点上做。
7. 一些个人体会和后续可以折腾的方向
Galera Cluster这套方案我从第一次接触到比较熟练地用于生产,大概花了小半年时间。中间踩的坑不少,但整体来说,它确实解决了传统主从复制延迟和高可用切换的问题。如果你的业务场景是读多写少、写入冲突概率低、对数据一致性要求高,那Galera是一个值得考虑的选择。
不过也要清醒地认识到它的局限:写入性能受限于最慢的节点,网络延迟敏感,高并发写入冲突需要应用层配合处理。它不是银弹,不能解决所有数据库问题。选型的时候一定要结合自己的业务特点来评估。
后续如果还想深入,可以研究几个方向:一是Galera的认证算法细节,理解冲突检测的具体实现;二是结合ProxySQL做读写分离和负载均衡,把读请求分发到多个节点;三是研究Galera与Kubernetes的集成,实现自动化部署和故障恢复。这些内容展开来又是另一个话题了,有机会再单独聊。