ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Cassandra备份恢复实战:从快照到完整恢复的关键策略

Cassandra备份恢复实战:从快照到完整恢复的关键策略 Cassandra 备份恢复是我这几年维护分布式数据库时最不敢放松的一环。标题里那句保障大数据安全的关键策略不是口号我在生产环境里维护过几十个节点的 Cassandra 集群见过太多以为有备份、实际恢复不了的案例。这篇内容就把我之前做过的备份恢复方案、踩过的坑、以及最终沉淀下来的操作流程完整梳理一遍给正在用 Cassandra 或者准备上 Cassandra 的团队一个参考。1. 为什么说 Cassandra 的备份比普通数据库更硬核1.1 分布式架构决定了备份不能只盯着一个节点很多人第一次接触 Cassandra 都是从单机 MySQL 或 PostgreSQL 转过来的思维惯性还在数据库 一个进程 一堆数据文件这个层面。到了 Cassandra 这里第一件事就是要打破这种惯性。Cassandra 天生是分布式数据库一个集群少则三五个节点多则上百个节点每个节点上只有整份数据的一部分而且同一份数据可能同时在多个节点上有副本。这就意味着备份根本不是找一台机器把数据文件拷走那么简单而是要在集群维度去做规划。实际生产中我见过一种特别常见的误操作某个节点磁盘快满了运维同学直接把数据目录 tar 包压缩拷贝到备份服务器以为这样就安全了。这种操作本质上只是文件拷贝既没有通过 Cassandra 的备份机制生成一致性快照也没有考虑其他副本节点的数据真到了恢复的时候要么文件不完整要么数据根本无法对齐到某个一致的时间点。所以理解 Cassandra 备份的第一步是理解它的数据分布模型。1.2 副本策略和一致性级别对备份的约束Cassandra 用 replication factorRF控制每个 row 在集群里存几份配合 SimpleStrategy 或 NetworkTopologyStrategy 决定副本怎么分布。RF 越高数据冗余越多单节点丢失的容错能力越强但备份时的数据量也随之增加。如果使用 NetworkTopologyStrategy——生产环境里几乎都要求这样配——每个机架或者每个可用区都有自己的副本集合备份方案必须覆盖到所有机架否则就可能出现某个区域数据完全没有备份的情况。一致性级别consistency level也对备份有直接影响。如果平时读写都是 QUORUM那备份时也要注意节点级别的备份只能保证这个节点上当前落盘的数据不能保证这些数据就是全局一致视图。Cassandra 不是事务型数据库没有一个全局的checkpoint概念所以备份数据的一致性最终要靠全集群所有节点几乎同时打快照来近似保证。这个近似已经能满足绝大多数业务场景的需求但你要知道它并不是强一致的。1.3 表结构、压缩算法和底层文件格式Cassandra 的每张表在底层对应一组 SSTable 文件加上 commitlog、hints 等辅助文件。SSTable 是不可变的immutable数据更新和删除都通过写入新的 SSTable 来实现后台再由 compaction 合并。这个特性对备份来说其实是个好消息SSTable 只增不改对某个时刻的 SSTable 做快照就相当于记住了那个时刻的持久化状态。但同时也带来一个问题——如果备份策略没有跟 compaction 协调好备份出来的可能是一堆很快就要被合并清理的中间文件恢复时虽然能用但效率极低还会拖慢恢复后的 compaction 过程。还有压缩因素。很多团队开启了 Cassandra 的 compression比如 LZ4Compressor备份出来的 SSTable 文件是压缩后的格式恢复时不需要手动解压Cassandra 节点会用表定义里的 compression 参数自动处理。这一点理解清楚能避免在恢复阶段做很多无用功。2. 快照备份的正确打开方式从 nodetool snapshot 说起2.1 快照的原理硬链接不是复制Cassandra 的官方备份方案核心就是快照snapshot通过nodetool snapshot命令触发。很多人第一次看到快照目录里全是硬链接时很困惑——明明数据文件那么大快照目录怎么看起来是瞬间生成的因为快照并不是把数据文件复制一份而是给当前所有 SSTable 文件创建了硬链接。硬链接指向的是同一个 inode文件内容并不重复占用磁盘只有在文件后续发生变化时旧的内容才会因为原本的链接数被减一而保留下来。这个原理直接决定了你的磁盘规划只要还有多个节点同时在写数据、compaction 还在跑快照目录占用的空间就会慢慢增长。因为 compaction 会把旧的 SSTable 替换掉但快照的硬链接仍指向那些旧的、即将被删除的 inode文件系统会把这些数据保留到最后一个链接被删除为止。这就是为什么打了快照之后必须要有一个自动清理机制否则备份空间会无限膨胀最终把磁盘吃满。2.2 生产环境的快照操作流程我实际用的快照流程是这样的每一步都有明确目的确认集群健康状态。执行nodetool status观察每个节点的状态是 UNUp Normal如果某个节点处于 DN 或者有 pending 任务先不要打快照。因为节点不同步时快照出来的数据可能是缺失的。逐个节点执行快照命令。命令示例nodetool snapshot -t backup_20250601-t参数是快照标签tag建议带上日期方便后续管理。如果需要备份单个 keyspace 或单张表可以这样nodetool snapshot -t backup_20250601 -kt my_keyspace # 或者指定多张表 nodetool snapshot -t backup_20250601 -kt my_keyspace -cf my_table1,my_table2收集快照文件。快照会生成在数据目录的snapshots/tag/子目录下例如/data/cassandra/data/my_keyspace/my_table-xxx/snapshots/backup_20250601/。此时你需要把这些文件同步到备份服务器。我的做法是每台节点上用 rsync 同步rsync -avzP /data/cassandra/data/my_keyspace/ remote_backup:/backup/cassandra/node1/my_keyspace/注意 rsync 同步时要保留目录结构不要随意改名因为恢复时 Cassandra 要按照固定的目录结构去找文件。快照完成后立刻执行清理nodetool clearsnapshot -t backup_20250601这一步我特别强调要立刻执行。我见过有团队打完快照后忘记清理半个月后磁盘报警查了半天才发现是快照目录把空间耗尽了。2.3 快照只包含 SSTable 文件不包含元数据有一个非常关键的认知要建立nodetool snapshot备份的是数据文件但 keyspace 定义、table schema、用户权限这些元数据并不在快照里。恢复一个完整的 Cassandra 服务必须单独导出和保存 schema。常规做法是在打快照的同时把 schema 导一份cqlsh -e DESCRIBE SCHEMA schema_backup_20250601.cql如果集群里用户权限配置比较复杂用了CREATE ROLE、GRANT等还需要cqlsh -e LIST ROLES roles_backup.cql很多恢复失败案例就栽在这里——数据文件倒是恢复出来了结果表结构是空的数据无处安放。所以我的备份包结构里schema 和 roles 永远和快照放同一个目录命名带一样的日期标签。3. 增量备份与 commitlog 归档两个容易搞混的环节3.1 增量备份解决的是全量之间的空隙全量快照备份如果频率低比如每天一次那么当天快照之后写入的数据在第二天快照之前就处于裸奔状态。要覆盖这个空隙就需要增量备份。Cassandra 的增量备份是在节点级别开启的在cassandra.yaml中设置incremental_backups: true开启后节点每次 flush 新的 SSTable 时会在backups/目录下保留一份拷贝。注意增量备份不是基于时间点的连续日志而是flush 时的数据文件快照所以它也有一个硬伤如果某个数据从写入到 flush 之间一直停留在 memtable 里增量备份是抓不到的。要抓到这部分数据你需要在做增量备份前主动 flush 一次nodetool flush或者干脆把增量备份和 commitlog 归档配合起来用。3.2 commitlog 归档才是最后一根救命稻草commitlog 是 Cassandra 的预写日志所有写入操作会先落 commitlog再进 memtable最后才 flush 成 SSTable。如果节点在 flush 之前宕机commitlog 是恢复未持久化数据的唯一来源。默认情况下commitlog 文件在写完并确认对应数据已 flush 后就会被删除想要持久保存这些日志需要开启commitlog_archiving.properties配置。具体配置方法是在conf/目录下创建commitlog_archiving.properties如果不存在。定义归档命令例如archive_command/usr/local/bin/cas_archive.sh %path %name restore_command/usr/local/bin/cas_restore.sh %from %to restore_directories/backup/cassandra/commitlog_archive编写归档脚本把 commitlog 同步到备份服务器同时保留原文件直到确认不需要它。需要特别注意的是commitlog 归档脚本必须非常健壮要是归档命令执行失败Cassandra 会阻塞后续写入来防止数据丢失。这听起来是保护机制但也意味着归档命令一旦写得有 bug用户写入就会全部卡住影响面比丢掉几个 commitlog 大得多。3.3 全量 增量 commitlog 的组合策略我在生产环境里最终落地的是这样一套组合每天凌晨 2 点做一次全量快照rsync 到备份服务器快照保留本地 2 天远程保留 14 天。开启incremental_backups: true配合每小时的nodetool flush尽量把增量备份的覆盖范围做小。commitlog 归档配置成实时同步归档保留 3 天。这样设计的原因是全量快照恢复虽然最干净但文件量大、耗时长增量备份可以帮助缩短全量到当前的数据差commitlog 归档则把最后一段 memtable 里的数据也兜住。三者配合理论上可以做到最多丢失几秒钟的数据——对于大多数非金融强一致类业务这个 RPO 完全够用。这套组合里最容易出问题的还是增量备份目录的清理。backups/目录不会自动清理如果不管它时间久了磁盘会被撑爆。我在 crontab 里加了清理任务只保留最近 48 小时的增量文件。4. 恢复演练全流程从 schema 到数据的完整链路4.1 恢复的总体思路先起集群再造表再灌数据恢复 Cassandra 和大数据生态里其他组件不同不能简单把文件拷回去就完事。一个标准的恢复流程是这样的准备一个全新的 Cassandra 集群版本要和备份集群一致或兼容严格来说大版本必须一致。用之前导出的 schema 文件重建所有 keyspace 和表结构。停止节点把快照文件放入对应的数据目录。启动节点等待数据加载和 compaction。用业务查询验证数据完整性。这个顺序不能乱。如果先把数据文件灌进去再重建 schemaCassandra 会因为找不到表对应的 metadata 而直接跳过那些 SSTable数据等于白恢复。4.2 分步操作一次可复现的恢复演练我在一个测试环境里完整跑过恢复演练环境是 3 节点的 Cassandra 3.11.x这里把关键步骤贴出来。第一步重建 schemacqlsh 192.168.1.11 -f schema_backup_20250601.cql注意 schema 文件里如果包含CREATE KEYSPACE ... WITH replication ...恢复时根据实际集群拓扑调整 RF 值。备份集群如果是 5 节点 RF3恢复集群只有 3 节点却还是 RF3数据分布就会不均后面容易出现单个节点过载。第二步停掉节点准备数据目录systemctl stop cassandra把快照文件放回原来的位置。假设快照目录结构是/data/cassandra/data/my_keyspace/my_table-xxxxx/snapshots/backup_20250601/恢复时清空当前数据目录里对应表的 SSTable但保留snapshots目录。然后把这层快照文件复制到表的当前目录cp /data/cassandra/data/my_keyspace/my_table-xxxxx/snapshots/backup_20250601/*.db /data/cassandra/data/my_keyspace/my_table-xxxxx/如果有多个表、多个节点需要用脚本批处理。我自己写过一个简单的 bash 脚本遍历snapshots/tag/目录把所有表快照文件复制到各自的父目录顺便加个日志方便回查哪些表恢复了多少文件。第三步启动节点systemctl start cassandra启动后观察日志重点看有没有CompactionExecutor相关的大量任务。因为恢复的 SSTable 会被触发重新做 compaction这是正常现象但耗时取决于数据量。第四步验证数据。最简单的方式是抽查几行业务数据cqlsh 192.168.1.11 -e SELECT count(*) FROM my_keyspace.my_table LIMIT 100;这里要注意count(*)在 Cassandra 里是压测级别的全表扫描数据量大时别在生产环境干这事。我一般用具体主键抽查或者对比备份前后某些聚合值。4.3 恢复后的数据一致性验证技巧判断恢复是否成功的唯一标准不是节点起来了、查询不报错而是业务数据对不对。我在演练中发现只看行数容易自欺欺人因为如果 schema 重建时有字段类型变化数据可能被静默丢弃。推荐做法是在备份前记录几个关键表的行数估算值用nodetool tablestats的输出恢复后再跑一遍nodetool tablestats对比。选几条已知主键的数据备份前导出它们的原始值恢复后查询比对。如果表里有时间戳字段抽查最近一小时的数据是否完整。这套验证机制最好做成自动化脚本每次恢复演练都跑一遍不要在恢复成功后拍个节点状态正常的照片就算完事。5. 恢复演练踩过的坑与完整排查过程5.1 坑一版本不一致导致的 schema 不兼容我第一次做恢复演练时备份环境是 Cassandra 3.11.4恢复环境图省事直接装了个最新版 3.11.latest。结果导入 schema 时报错提示某个表使用了旧版本才有的 compaction 参数新版本已经移除。这个坑的教训是恢复环境的版本必须与备份环境保持一致最好是同一个小版本。如果你做了跨版本升级数据文件可以用upgradesstables之类的工具处理但备份和恢复之间不要跳版本。5.2 坑二数据目录多了一层目录结构节点启动直接失败有次用 rsync 恢复数据因为源数据目录本身就带着snapshots子目录rsync 的时候用了-a参数保留层级关系结果快照文件被同步到了.../my_table-xxx/snapshots/backup_tag/snapshots/backup_tag/这种嵌套路径节点启动时无法识别。排查过程是这样的启动 Cassandra 后日志里不断出现Cannot find file /data/.../snapshots/backup_tag/...之类的报错。我第一反应是权限问题检查了一遍数据目录的属主和权限发现都是cassandra:cassandra没问题。然后才注意到目录层级不对劲。修复办法很简单重新放置文件去掉多余的快照嵌套层级。但这个过程浪费了大半个小时如果在正式恢复场景下这半小时可能就是服务不可用时间。这个坑的根因是我没有在恢复前先列一遍目标目录结构直接依赖 rsync 的参数行为。后来我在恢复脚本里加了前置检查恢复前先find出所有快照文件的相对路径确认层级正确才允许复制。5.3 坑三commitlog 归档导致写入阻塞前面提到归档命令如果出问题会阻塞写入这个坑我在测试环境真踩过。当时用commitlog_archiving.properties配置了归档脚本脚本里用scp把 commitlog 推到备份机但备份机的磁盘满了scp静默失败而脚本没有对退出码做检查Cassandra 认为归档没完成就一直等待业务写入全部超时。排查链路是先是业务方反馈写入耗时飙升然后我看到 Cassandra 日志里有大量Unable to archive commitlog的警告最后才定位到是归档脚本的scp退出码非零。修复方法是给归档脚本加上完整的错误处理scp $src backup_host:/backup/commitlog/ if [ $? -ne 0 ]; then echo archive failed: $src /var/log/cassandra_archive.log exit 1 fi同时给备份机加了磁盘监控磁盘使用率超过 85% 就告警。这个坑给我的最大教训是凡是会影响主流程的次要功能归档、同步等都必须设计成可失败、可降级的否则一个辅助脚本的 bug 能直接把核心业务拖垮。5.4 坑四忽略 hints 文件恢复后出现数据丢失假象Cassandra 的 hints 是节点不可达时暂存的写操作重放文件。如果某个节点宕机时间较长另外的节点会为它缓存大量 hints。在备份恢复时如果直接遗留旧的 hints 文件新节点启动后会尝试重放这些旧 hints造成一部分数据被当作新写入去覆盖。表面上看数据没丢实际上可能把恢复后的正确数据覆盖了或者重放失败导致数据不一致。我踩过这个坑后恢复流程里加了清理步骤启动节点前清空hints目录下的所有文件。这听起来违背直觉——hints 不是能帮我找回数据吗但在恢复场景里hints 的重放逻辑依赖的是原节点的状态恢复后的节点状态和原节点并不完全一致重放风险远大于收益。真正需要靠 hints 找回的数据应该在备份时通过 commitlog 和增量备份去覆盖而不是在恢复时依赖 hints。6. 备份策略之外那些决定生死的小细节6.1 备份监控不能只看有没有目录很多团队的备份监控就是检查备份服务器上有没有今天的文件夹这远远不够。我见过文件夹在、但里面是空文件的情况也见过 rsync 中途失败但 exit code 被忽略的情况。正确的监控维度应该是备份任务是否在预期时间窗口内完成。每个备份目录的文件总大小是否在合理区间跟上次比有没有明显异常。抽样校验几个关键表的快照文件大小是否变化。备份服务器的磁盘剩余空间。这些监控不需要很复杂的系统crontab 一个状态记录文件 简单的告警脚本就能跑起来。核心逻辑是只要备份结果有任何静默失败的可能就要有对应的指标去捕获。6.2 定期做恢复演练而且要演练最坏情况我强烈建议每个季度至少做一次完整的恢复演练。演练不能总在风和日丽的环境下进行要故意制造一些故障条件比如只恢复其中一台节点的数据验证其他节点能否通过流式复制把数据补齐。模拟整个 keyspace 误删除用备份完整重建集群。模拟备份服务器数据也丢失了一半只剩下部分节点的快照看能用残留数据恢复到什么程度。只有演练过这些场景你才能真正知道自己的备份方案在极端情况下的恢复时间RTO和数据丢失量RPO是多少。我所在的团队就发现过号称 RPO 5 分钟的设计实际演练下来数据丢失可能接近 30 分钟原因是增量备份的 flush 频率没有跟上业务写入速率。6.3 密钥、账号和配置文件的备份Cassandra 的 schema 之外还应该定期备份这些配置文件cassandra.yamlcassandra-env.shjvm.options各种权限相关的 roles 和 grants通过 cqlsh 导出如果你用了 SSL 客户端认证或者节点间加密通信TLS 证书和私钥也要一并纳入备份体系。否则数据文件恢复出来了节点之间无法建立加密通信照样服务不可用。这个细节在文档里经常被忽略但在生产环境里出现过真实事故我在这里专门提一下。6.4 备份与容灾的关系备份不是终点恢复才是最后想分享一个思维方式上的转变。Cassandra 备份恢复这件事表面上是技术操作本质上是风险管理和流程设计。我在实际维护中最大的体会是备份策略的成败不是看备份做得多勤快而是看恢复时能不能把数据完整捞回来。所以所有围绕备份的工作都应该默认以可恢复为最终验收标准。具体到落地就是每一套备份方案都要配一份恢复手册手册里写清楚备份文件目录结构和命名规范。每个恢复场景的操作步骤包括完整集群恢复单节点恢复误删表恢复等不同情况。验证数据完整性的具体方法和预期结果。恢复过程中常见报错的排查思路。这份手册不需要长篇大论但一定要是从实际演练中沉淀出来的而不是从文档里抄出来的。我们团队的恢复手册已经迭代了四个版本每次演练发现问题就往里补现在拿来应急基本不会手忙脚乱。最后再说一句实操心得如果你现在还没给自己的 Cassandra 集群规划过备份方案今天就可以开始。先用nodetool snapshot打一次快照导出 schema在测试环境跑一遍恢复把整个链路走通。备份这件事做完一次完整的恢复演练比看十篇文档都有用。等真正遇到故障、数据还能找回来的时候你会感谢那个提前演练的自己。
返回列表