本文目录导读:

- 核心原则
- 场景一:Redis/缓存集群(如Redis Cluster, Codis)
- 场景二:ZooKeeper / Etcd(共识算法系统)
- 场景三:MySQL / PostgreSQL 分布式(如 ShardingSphere, MyCat)
- 场景四:Kafka / Pulsar(消息队列)
- 场景五:HBase / Cassandra(列式存储)
- 通用Java应用层恢复策略
- 关键注意点(避坑指南)
- 总结:手动恢复流程 checklist
在Java分布式系统中,“手动恢复”通常指当自动化恢复机制失效(如网络中断、数据损坏、脑裂、副本不一致等)时,由运维或开发人员通过人工干预来修复数据一致性和可用性的过程。
这是一个高风险操作,需要非常谨慎,以下是针对不同场景的Java分布式数据手动恢复方法论和具体步骤:
核心原则
- 先快照,后操作:在进行任何手动数据修改前,务必对受影响节点的数据目录、数据库、日志文件进行全量冷备份或快照。
- 确认“停止服务”或“隔离节点”:在恢复过程中,通常需要将该节点从集群中踢出,防止脏数据被其他节点读取。
- 明确数据源权威:确定哪个节点或副本的数据是“正确的”(通常基于时间戳、版本号、Raft/ Paxos Leader身份)。
Redis/缓存集群(如Redis Cluster, Codis)
典型问题:某节点宕机后重启,数据丢失;集群出现槽位不一致。
手动恢复步骤:
- 确定主从角色:使用
cluster nodes命令查看哪些节点是Master,哪些是Slave,找到数据完整的Master节点。 - 备份权威数据:在权威Master节点执行
SAVE或BGSAVE,得到dump.rdb文件。 - 恢复目标节点:
- 停止目标Redis节点。
- 将备份的
dump.rdb复制到目标节点的数据目录。 - 启动目标节点。
- 修复集群状态:
redis-cli --cluster fix <any_node_ip>:<port>:自动修复槽位指派问题。
- 强制选举(如果原主节点无法恢复):
- 手动将从节点提升为主节点:在从节点上执行
CLUSTER FAILOVER FORCE。
- 手动将从节点提升为主节点:在从节点上执行
ZooKeeper / Etcd(共识算法系统)
典型问题:磁盘损坏导致一个节点数据滞后,或节点数据目录损坏无法启动。
手动恢复步骤(以ZooKeeper为例):
- 识别“好”节点:连接集群中其他正常运行的节点,确认当前Leader和安全的Follower。
echo stat | nc <leader_ip> 2181。 - 清理损坏节点:
- 停止损坏节点的ZK进程。
- 删除
dataDir和dataLogDir目录下的所有文件(务必先备份)。
- 同步数据:
- 从正常的Follower节点(或Leader)上,同步整个
dataDir目录,通常包含version-2下的snapshot和log。 - 修改
myid文件,使其ID与集群配置一致。
- 从正常的Follower节点(或Leader)上,同步整个
- 启动并观察:
- 启动该节点。
- 查看日志,确认它成功与Leader同步(
LEADERELECTION完成,进入FOLLOWING或LEADING状态)。
- Etcd特殊处理:
- 如果整个集群都坏了,需要手动恢复一个快照:
etcdctl snapshot restore snapshot.db --data-dir /var/lib/etcd-restored,然后启动这个单节点集群,再通过member add添加其他节点。
- 如果整个集群都坏了,需要手动恢复一个快照:
MySQL / PostgreSQL 分布式(如 ShardingSphere, MyCat)
典型问题:一张表数据损坏,需要基于Binlog或WAL日志进行行级别恢复。
手动恢复步骤:
- 锁定数据来源:确认主库上的数据是否完整。
- 使用日志闪回:
- MySQL:使用
mysqlbinlog解析Binlog,找到误操作前的准确位置,通过mysqlbinlog --stop-datetime提取出正确的SQL,执行回滚。 - PostgreSQL:使用
pg_waldump分析WAL日志,还原到特定事务ID。
- MySQL:使用
- 分片级恢复:
- 如果整个分片节点挂了:
- 使用主从切换(
change master to)。 - 如果无可用从库,从全量备份恢复,并应用增量Binlog到故障前一刻。
- 使用主从切换(
- 如果整个分片节点挂了:
- 分库分表校验:
- 恢复后,使用
pt-table-checksum(Percona Toolkit) 或自定义脚本,对比分片间的数据一致性。
- 恢复后,使用
Kafka / Pulsar(消息队列)
典型问题:某个Partition的Leader副本损坏,导致数据不可消费;或磁盘爆满后部分日志段损坏。
手动恢复步骤:
- 优先Leader切换(最安全):
- 使用
kafka-leader-election.sh --bootstrap-server ... --election-type PREFERRED --topic my_topic将Leader切换到其他副本。
- 使用
- 手动恢复损坏分区:
- 如果所有副本都损坏:
- 停止所有Broker。
- 定位到该Topic的日志目录(
/data/kafka-logs/topic-name-partition-x)。 - 使用
kafka-run-class.sh kafka.tools.DumpLogSegments检查.log文件损坏程度。 - 最粗暴方式:手动删除该分区目录。
- 使用工具修复:
truncate --size <last_known_good_offset>截断最后的日志段文件。 - 使用
kafka-reassign-partitions.sh手动创建新副本。
- 如果所有副本都损坏:
- Pulsar特殊点:
- 如果BookKeeper的Ledger损坏,使用
bookkeeper shell recover命令尝试修复Ledger。 - 如果无法恢复,需使用Pulsar的
--ledger-manage工具手动删除损坏的Ledger元数据。
- 如果BookKeeper的Ledger损坏,使用
HBase / Cassandra(列式存储)
典型问题:Region Server宕机导致Region数据不一致,或HFile文件损坏。
手动恢复步骤(HBase为例):
- 归档损坏Region:
hbase hbck -fixAssignments尝试自动修复。- 手动介入:使用
hbase hbck -repair damaged_region。
- 手动合并HFile:
- 如果HFile损坏导致Major Compaction失败:
- 从备份中恢复HFile到
/.tmp目录。 - 使用
HBase HFileTool验证:hbase org.apache.hadoop.hbase.io.hfile.HFile -v /path/to/hfile。 - 使用
HFile Recover命令手动恢复。
- 从备份中恢复HFile到
- 如果HFile损坏导致Major Compaction失败:
- Cassandra:
- 如果SSTable损坏:
nodetool refresh -- <keyspace> <table>强制重加载SSTable。- 如果仍失败,使用
sstableloader/sstableutil手动清理损坏的SSTable。
- 如果SSTable损坏:
通用Java应用层恢复策略
如果上述底层组件都无法直接操作,你需要在应用代码层面进行恢复:
- 事务日志重放:
- 你的应用是否有记录操作日志?例如Spring的
@Transactional+ 手动记录event_store表。 - 编写一个补偿脚本(Java Main方法),读取事件存储,反向执行补偿操作(Saga模式)。
- 你的应用是否有记录操作日志?例如Spring的
- 手动执行补偿SQL:
- 通过MySQL客户端,手写SQL更新“总账户余额”和“账户状态表”。
- 关键:务必在应用暂停写入(或使用分布式锁)的情况下执行,防止写写冲突。
- 利用版本号/乐观锁:
- 在恢复前,查询当前记录的版本号(如
version列)。 - 在手动更新时,
UPDATE table SET version = version + 1 WHERE id = X AND version = Y,如果失败,说明有并发写入,停止恢复并告警。
- 在恢复前,查询当前记录的版本号(如
关键注意点(避坑指南)
- 永远不要在多节点上同时执行恢复命令:这可能导致数据分裂(Split-brain)。
- 优先使用高可用方案而非手动恢复:手动恢复是灾难场景的最后手段,应优先确保:
- 配置自动故障转移(如Redis Sentinel, Kafka Replicator)。
- 开启WAL、Binlog、CommitLog。
- 定期做全量+增量备份。
- 恢复后必须验证:使用求和校验、行数对比、业务正确性测试。
- Java版本兼容性:手动恢复时,确认使用的客户端工具(如hbase shell, redis-cli)版本与集群版本兼容。
手动恢复流程 checklist
- 暂停服务:停止该节点或整个集群的写入口。
- 全量备份:
cp -a或 云快照。 - 分析损坏范围:是全部数据还是部分分区?
- 选择权威副本:选Master或最新版本节点。
- 拷贝/修复数据:使用对应组件的工具。
- 重新加入集群:启动节点,确认同步。
- 恢复服务:逐步恢复写入流量。
- 事后分析:为什么自动化没有修复?补全监控和告警。