Galera Cluster部署 mariadb 节点down机,log sequence number恢复

一、场景现象

假设你有一个 3 节点的 MariaDB Galera Cluster(Node1: 192.168.31.185、Node2: 192.168.31.186、Node3: 192.168.31.187),由于机房断电、硬件故障或内核崩溃等原因,所有节点同时异常宕机 ,没有经过正常的 systemctl stop mariadb 流程。

恢复供电或修复硬件后,尝试启动 MariaDB 服务:

bash 复制代码
systemctl start mariadb

服务启动失败,查看日志发现关键错误:

bash 复制代码
[ERROR] WSREP: It may not be safe to bootstrap the cluster from this node.
It was not the last one to leave the cluster and may not contain all the updates.
To force cluster bootstrap with this node, edit the grastate.dat file manually
and set safe_to_bootstrap to 1.

[ERROR] WSREP: wsrep::connect(gcomm://192.168.31.185,192.168.31.186,192.168.31.187) failed: 7
[ERROR] Aborting

检查每个节点的 grastate.dat 文件:

bash 复制代码
cat /var/lib/mysql/grastate.dat

三个节点输出一致:

bash 复制代码
# GALERA saved state
version: 2.1
uuid:    00000000-0000-0000-0000-000000000000
seqno:   -1
safe_to_bootstrap: 0

业务端表现为:

bash 复制代码
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)

所有数据库服务完全不可用,业务中断。


二、错误分析

1.为什么 seqno: -1

grastate.dat 文件记录的是节点正常关闭时 的最后事务位置(seqno)。正常关闭时,Galera 会将当前已提交的最新事务序号写入该文件。但在异常宕机(断电、kill -9、内核崩溃)时,进程来不及写入这个值,因此 seqno 被标记为 -1,表示位置未知

2.为什么 safe_to_bootstrap 全部为 0

Galera 3.19+ 引入了 Safe-to-Bootstrap 保护机制 。正常关闭时,最后一个离开的节点会被标记为 safe_to_bootstrap: 1,其他节点标记为 0。但在全部异常宕机的场景下,没有任何节点能完成"最后离开"的标记流程,所以所有节点都是 0

3.为什么不能直接启动

Galera 集群重启的本质是创建一个全新的逻辑集群 。第一个启动的节点必须通过 bootstrap 模式引导,而这个节点必须拥有最新的数据。如果选错了节点,可能导致:

  • 数据丢失:最后提交的事务被丢弃
  • 数据不一致:不同节点间的数据出现分歧

因此 Galera 强制要求操作者明确指定哪个节点作为引导节点。

4.为什么 uuid 变成了全零

uuid: 00000000-0000-0000-0000-000000000000 说明节点在非事务操作(如 DDL)执行期间崩溃,或者 InnoDB 崩溃恢复后无法确定所属集群的 UUID。这是最严重的情况之一。

5.为什么从 error.log 找 log sequence number

grastate.dat 中的 seqno-1uuid 为全零时,Galera 层面的信息已经不可靠。此时需要下沉到 InnoDB 存储引擎层面 ,通过比较各节点 InnoDB 的 log sequence number(LSN) 来判断哪个节点拥有最新的数据。LSN 是 InnoDB redo log 中的递增序列号,LSN 越大,说明该节点在崩溃前写入的数据越多、越新。


三、完整恢复步骤详解

以下操作以 3 节点集群为例,假设你需要登录到每个节点分别执行操作。

第一步:确认所有节点 MariaDB 进程已停止

在每个节点上执行:

bash 复制代码
systemctl status mariadb
# 确保所有节点都处于 inactive (dead) 状态

# 如果有残留进程,强制清理
killall -9 mysqld 2>/dev/null

⚠️ 必须确保所有节点的 mysqld 进程完全停止,否则后续操作可能产生端口冲突(Address already in use)。

第二步:在每个节点的 error.log 中找到 log sequence number

在每个节点上执行:

bash 复制代码
vim /var/lib/mysql/error.log

在日志中搜索关键字 log sequence number,找到类似以下内容的行:

bash 复制代码
InnoDB: Doing recovery: scanned up to log sequence number 8521367

或者在崩溃恢复阶段会看到:

bash 复制代码
InnoDB: Starting crash recovery from checkpoint LSN=8521367

在三个节点上分别记录这个数值:

节点 log sequence number
Node1 (192.168.31.185) 8435210
Node2 (192.168.31.186) 8521367(最大)
Node3 (192.168.31.187) 8398455

Node2 的 log sequence number 最大(8521367),说明它拥有最新的 InnoDB 数据,应被选为引导节点。

💡 快速提取命令(避免手动翻日志):

bash 复制代码
grep -i "log sequence number" /var/lib/mysql/error.log | tail -1
第三步:修改选定节点的 grastate.dat

Node2(LSN 最大的节点)上执行:

bash 复制代码
vim /var/lib/mysql/grastate.dat

将内容修改为:

bash 复制代码
# GALERA saved state
version: 2.1
uuid:    00000000-0000-0000-0000-000000000000
seqno:   -1
safe_to_bootstrap: 1

核心改动:safe_to_bootstrap0 改为 1

💡 如果 error.log 中通过 --wsrep-recover 能恢复出具体的 uuidseqno,也可以一并填入,让 Galera 更精确地定位恢复点。但在 LSN 方法中,仅修改 safe_to_bootstrap: 1 即可满足引导条件。

第四步:在选定节点上引导集群

Node2 上执行:

bash 复制代码
galera_new_cluster

或者使用等效命令:

bash 复制代码
systemctl start mariadb@bootstrap
# 或
mysqld_safe --wsrep-new-cluster &

等待 Node2 完全启动后,验证其状态:

bash 复制代码
mysql -u root -p -e "SHOW STATUS LIKE 'wsrep_local_state_comment';"
# 期望输出:Synced

mysql -u root -p -e "SHOW STATUS LIKE 'wsrep_cluster_size';"
# 期望输出:1(当前只有这一个节点)

mysql -u root -p -e "SHOW STATUS LIKE 'wsrep_cluster_status';"
# 期望输出:Primary
第五步:依次启动其他节点

Node1Node3 上正常启动(不需要 galera_new_cluster):

bash 复制代码
# Node1
systemctl start mariadb

# Node3
systemctl start mariadb

⚠️ 建议逐个启动,等前一个节点完全加入后再启动下一个,避免多个节点同时发起 SST 请求导致捐赠节点压力过大。

第六步:验证集群恢复

在任意节点上执行:

bash 复制代码
-- 检查集群节点数
SHOW STATUS LIKE 'wsrep_cluster_size';
-- 期望:3

-- 检查集群状态
SHOW STATUS LIKE 'wsrep_cluster_status';
-- 期望:Primary

-- 检查每个节点自身状态
SHOW STATUS LIKE 'wsrep_local_state_comment';
-- 期望:Synced

-- 检查集群 UUID 一致性
SHOW STATUS LIKE 'wsrep_cluster_state_uuid';
-- 所有节点应输出相同的 UUID

四、恢复流程总结

bash 复制代码
全节点异常宕机
    │
    ▼
所有节点 seqno=-1, safe_to_bootstrap=0
    │
    ▼
在每个节点的 /var/lib/mysql/error.log 中
找到 "log sequence number" 的值
    │
    ▼
对比所有节点,找到 LSN 最大的节点
    │
    ▼
修改该节点 grastate.dat:safe_to_bootstrap=1
    │
    ▼
在该节点执行 galera_new_cluster 引导集群
    │
    ▼
依次启动其他节点,自动加入集群
    │
    ▼
验证 wsrep_cluster_size=3, 所有节点 Synced

五、常见踩坑与注意事项

  • 端口冲突 :如果之前启动失败残留了 mysqld 进程,后续 galera_new_cluster 会报 Address already in use(端口 4567 被占用)。务必先 killall mysqld 清理残留进程。
  • 选错引导节点 :如果选了 LSN 不是最大的节点引导集群,该节点会认为自己拥有最新数据,但实际上丢失了部分事务,造成静默数据丢失。务必仔细比对每个节点的 LSN。
  • uuid 为全零的节点 :如果某个节点的 uuid00000000-0000-0000-0000-000000000000,说明该节点在 DDL 操作期间崩溃,数据可能不完整,不应选为引导节点。
  • 恢复后建议做数据校验 :集群恢复后,建议对关键业务表执行 CHECKSUM TABLE 或行数比对,确认数据完整性。
  • LSN 与 seqno 的关系:LSN 是 InnoDB 引擎层面的日志序列号,seqno 是 Galera 层面的事务序列号。正常情况下两者正相关(LSN 越大,seqno 也越大),但在极端崩溃场景下,LSN 是最底层、最可靠的数据新旧判断依据。
相关推荐
xiaohaiAIgeo44 分钟前
【2026年】ASHRAE 110与EN 14175通风柜测试标准对比:进口与国产品牌性能差距
java·前端·数据库·科普知识
SelectDB1 小时前
Apache Doris 4.1 Spill to Disk:避免运行内存密集型查询发生 OOM
数据库
小此方1 小时前
Re:Linux系统篇(五十四)线程篇 · 七:互斥锁的底层实现原理:从硬件上下文、原子交换指令(Swap)到并发封装实战
linux·运维·驱动开发
郭涤生1 小时前
rootfs 详解与裁剪优化记录
linux·c++·bsp
范什么特西2 小时前
redis题目面渣重点
数据库·redis·缓存
二宝哥2 小时前
CentOS 7.9 离线安装 Nginx 并配置openssl1.1.1
linux·nginx·centos
TDengine (老段)2 小时前
TDengine taosAdapter — 多协议网关详解
大数据·数据库·物联网·时序数据库·iot·tdengine·涛思数据
Nturmoils2 小时前
SQL Server 迁移到 KingbaseES:一次复杂 BI 查询的性能实测
数据库
程序员老陆2 小时前
说一说FFmpeg6在Linux平台的编译
linux·运维·服务器·ffmpeg