使用 Wiredtiger 存储引擎的 MongoDB 无法启动,数据能否恢复回来?
问题出现的场景是「mongod磁盘写满了,导致进程 crash」,尝试重新启动,结果 wiredtiger 报错,错误信息类似如下,说明此时 MongoDB 数据文件已经损坏。
2017-03-28T22:06:05.315-0500 W - [initandlisten] Detected unclean shutdown - /data/mongodb/mongod.lock is not empty.
2017-03-28T22:06:05.315-0500 W STORAGE [initandlisten] Recovering data from the last clean checkpoint.
2017-03-28T22:06:05.324-0500 I STORAGE [initandlisten] wiredtiger_open config: create,cache_size=13G,session_max=20000,eviction=(threads_max=4),statistics=(fast),log=(enabled=true,archive=true
,path=journal,compressor=snappy),file_manager=(close_idle_time=100000),checkpoint=(wait=60,log_size=2GB),statistics_log=(wait=0),
2017-03-28T22:06:05.725-0500 E STORAGE [initandlisten] WiredTiger (0) [1454119565:724960][1745:0x7f2ac9534bc0], file:WiredTiger.wt, cursor.next: read checksum error for 4096B block at offset 6
799360: block header checksum of 1769173605 doesn't match expected checksum of 4176084783
2017-03-28T22:06:05.725-0500 E STORAGE [initandlisten] WiredTiger (0) [1454119565:725067][1745:0x7f2ac9534bc0], file:WiredTiger.wt, cursor.next: WiredTiger.wt: encountered an illegal file form
at or internal value
2017-03-28T22:06:05.725-0500 E STORAGE [initandlisten] WiredTiger (-31804) [1454119565:725088][1745:0x7f2ac9534bc0], file:WiredTiger.wt, cursor.next: the process must exit and restart: WT_PANI
C: WiredTiger library panic
2017-03-28T22:06:05.725-0500 I - [initandlisten] Fatal Assertion 28558
MongoDB 3.2及以后的版本已经很少会出现这样的问题,至少从我接触 MongoDB 到现在还没实际遇到过这个问题,不过既然问题已经发生,我们来看看遇到这种情况应该怎么恢复数据?
如何恢复 MongoDB 数据?
第一招: 从复制集其他节点同步数据
MongoDB 通过复制集能保证高可靠的数据存储,通常生产环境建议使用「3节点复制集」,这样即使其中一个节点崩溃了无法启动,我们可以直接将其数据清掉,重新启动后,以全新的 Secondary 节点加入复制集,它会自动的同步数据,这样也就达到了恢复数据的目的。
然而不幸的是,该用户的 MongoDB 实例 只部署了一个节点 ... 我只能呵呵了 ...
第二招:从最近的一个备份集恢复数据
有的时候可能出现一些极端的case,比如遇到自然灾害,复制集所有节点都挂了(或者像上面的用户这样,你的复制集只部署一个节点...),这时第一招就没法用了。
此时,如果靠谱的你刚好对数据做了备份,此时就排上用场了,比如你每天对 MongoDB 做一次全量备份,那么你就可以把数据恢复到最近一天的数据了;如果你更靠谱的还对数据做了增量本分,能恢复的数据就更多了。
但是可想而知,这个用户既然能部署一个「只有单个节点的复制集」,肯定也不会想到去对数据库进行备份了 ...
第三招: repair 模式启动 MongoDB
当 MongoDB 无法启动时,通常是因为数据文件出现了不一致,mongod 支持以 repair 的模式启动,mongod 会尽可能的尝试自己去修复数据的不一致状态,修复过程中尽可能多的保留有效的数据。
但 repair 也不是对所有的场景都有效,repair 会先加载 MongoDB 所有的集合信息,然后针对每个集合来 repair,如果存储元数据的数据文件损坏,repair 也是没法工作的。
mongod --repair // 用户尝试按这种方式启动,仍然报相同的错误
第四招:使用 wireditger 工具恢复
以上3招都不行,我的第一想法就是通过 wiredtiger 的 salvage 功能去尽可能的恢复数据(salvage 可翻译为数据打捞,即针对一个wt的数据文件,尽可能多的从中提取有效的数据),本来是想写个工具来做这个事情。不过调研了一下发现
1. repair 模式启动,实现时也是调用的 wiredtiger 的 salvage 接口实现。
2. wireditger 自带的一个命令行工具 wt,包含了 salvage 的功能。
3. 找到一篇使用 wt 工具恢复 MongoDB 数据的文章,写的非常赞。
网友总结的使用 wiredtiger 工具 wt 恢复数据的方法原理很简单,就是通过恢复 wiredtiger 数据文件来恢复MongoDB数据,我实验了一下,的确可行,而且原文的步骤介绍已经非常详细,这里就不再赘述。需要注意的是
MongoDB 3.2 最新版本已经是了 wiredtiger 2.8,所以编译 wt 工具时,可以下载 2.8 版本的 wiredtiger 源代码。
MongoDB 默认会对集合数据进行 snappy 压缩,所以一定要确保 snappy 正确安装,在执行 wt 工具时,通过扩展的形式加载 snappy lib,否则运行时会报错。
如果需要恢复的集合很多,本文的方法效率是很低的。
第五招:从文件里提取bson文档来恢复
MongoDB json格式的文档,最终是以BSON (Binary json)格式持久化存储。
假设我们有个工具叫bsonextract(有兴趣的同学可以尝试实现下贡献到社区里,直接调 BSON 的接口,实现起来不难),它能从一个数据文件里分析并提取出所有 BSON 格式的内容,那么我们也就达到了恢复数据的目的。
分析时,一段数据满足2个条件,我们即可认为是一个合法的 MongoDB 文档
这段数据是一个合法的 BSON 文档
包含一个id 字段 (oplog 集合不包含id 字段,但通常也没有去恢复 oplog 的必要)
上面这个方法不仅只能恢复 wiredtiger 的数据,对 MongoDB 所有存储引擎都有效。
总结
MongoDB 一直在优化 MongoDB ,让它能在 repair 模式里自动处理各种数据文件损坏(或部分丢失)的场景,目标就是万一遇到数据集损坏的场景,repair都能自动修复掉。
下面是 repair 以后能自动处理的一些场景及处理方法
Database files missing
An entry for a file will exist in the catalogue, but on disk file is gone
Will be impossible to recover from, remove the entry from the catalogue
Warn the user strongly about this (Error message)
Database files corrupted
An entry for a file will exist in the catalogue, but on disk file is unable to be opened
Attempt to rename the collection with WiredTiger to a new table that has some mention of it being corrupted in the name
Re-create the same collection with the same name (in order to continue repair)
Warn the user strongly about this problem, the creation of the new collection
Index files missing
An entry will exist in the catalogue, but on disk file is gone
Build the index as part of repair
Index files corrupted
An entry will exist in the catalogue, but on disk file is unable to be opened
Drop, then rebuild the index as part of repair
MongoDB catalogue metadata may be out of alignment with the WT files on disk
When something is missing on disk, then this should be resolved by the changes above
When something is missing from the catalogue metadata but exists as a wt table on disk we have no recourse. We would need a user accessible function to import
If the WiredTiger metadata is corrupt, then the database is corrupt