Reference: http://dev.guanghe.tv/2016/06/recovering-a-wiredtiger-collection-from-a-corrupt-wt-file.html

常在河边走,哪能不湿鞋。虽然说只要不使用kill -9杀进程,一般不会导致MongoDB出问题(Mongo本身有对kill做处理),但是程序总有跑偏的时候,也许哪次服务器重启或者遇到断电之类的,没准就会导致数据库文件损坏。

当然一般的异常关闭后启动不了时可能也就是删除一下lock、pid文件或者tmp下的sock文件即可搞定,根本不是什么问题,偶尔的数据异常--repair也就可以了(数据量大要建一堆索引的时候慎用,等很长时间给你抛出一个修复失败是最容易让人崩溃的……),而且其实开启了journal的情况下非正常关闭mongo时还有比较好用的数据文件自动修复功能,MongoDB的可靠性其实还不错。

我们这次要处理的就是一个没有开journal而且还遇到wt数据文件出现异常的数据库。

根据MongoDB的启动日志,WT_SESSION.open_cursor: unable to read root page from file:collection-3659--4168324323017494102.wt: WT_ERROR: non-specific WiredTiger error,是3659这个wt文件头出错了,这个要怎么办呢?如果按照某些「专业数据库修复」专家的建议,我们可以把这个损坏的wt文件替换掉。然后你猜会发生什么?WT_SESSION.open_cursor: collection-3659--4168324323017494102.wt read error: failed to read 4096 bytes at offset 42864640: WT_ERROR: non-specific WiredTiger error,肯定还会报错嘛,Mongo怎么可能不对文件做校验,暴力地替换肯定行不通啊……再往后怎么发展呢?这就到了专家们的营收环节了,只有到这一步才能体现出他们的『文件修复技术』是有价值的嘛……好了,话不多说,修复个小文件开口就四位数起步,说什么都不能忍……接下来我们自己动手,一步步开启MongoDB数据库修复从入门到放弃系列教程。

以下步骤可以通过直接读取wt文件恢复出对应的集合。关于MongoDB的WT引擎数据的目录布局可以到这里补习。

1.准备工作

1.1 wt实用工具包

wt实用工具包是本文用到的核心工具。下面是ubuntu下的操作示例,编译过程嘛,无非就是根据报错信息安装一些必要的依赖,一般来只要有gcc、g++之类的编译必备工具就OK了,唯一一个比较特殊的依赖是Google家的snappy,因为WT引擎默认的表数据压缩方式是snappy(而不是zlib)。

  1. wget http://source.wiredtiger.com/releases/wiredtiger-2.7.0.tar.bz2
  2. tar xvf wiredtiger-2.7.0.tar.bz2 && cd wiredtiger-2.7.0
  3. sudo apt-get install libsnappy-dev build-essential
  4. ./configure --enable-snappy
  5. make

1.2 要恢复的文件

准备好要恢复的collection*****.wt,以及读取它必备的_mdb_catalog.wtsizeStorer.wtstorage.bsonWiredTigerWiredTiger.basecfgWiredTiger.lockWiredTiger.turtleWiredTiger.wt。我们可以把这些文件放到一个新目录,比如本例放到mongo_bak下,目录结构如下:

  1. collection********.wt
  2. _mdb_catalog.wt
  3. sizeStorer.wt
  4. storage.bson
  5. WiredTiger
  6. WiredTiger.basecfg
  7. WiredTiger.lock
  8. WiredTiger.turtle
  9. WiredTiger.wt

2.开始干吧

2.1 『打捞』出可以被恢复的部分

  1. ./wt -v -h ../mongo-bak -C "extensions=[./ext/compressors/snappy/.libs/libwiredtiger_snappy.so]" -R salvage collection******.wt

这一步操作会读取我们指定的collection*****.wt,忽略所有无法被恢复的数据,然后把新数据覆盖回去。当然你也可以修改参数让它把salvage后的文件写到另一个地方。

运行上述命令会输出WT_SESSION.salvage 639400这样的结果,后面那个数量其实就是所有能被恢复的数据。但是你现在还不能把这个直接读取到MongoDB。

2.2 做些必要的数据格式调整

因为上一步产生的wt文件还没法直接用MongoDB读,所以我们接下来这几步就利用wt的dump和load工具想办法把他们导入到MongoDB。

2.2.1 wt --> dump

  1. ./wt -v -h ../mongo-bak -C "extensions=[./ext/compressors/snappy/.libs/libwiredtiger_snappy.so]" -R dump -f ../collection.dump collection******

这一步会把我们刚打捞出来的健康的wt文件dump到上级目录的collection.dump文件(当然这个文件叫什么名、存哪里你自己定,下一步你还能找到就行)。注意这一步操作指定的collection不需要写wt扩展名了,程序很贴心有木有,省下3次敲键盘的体力可以干好多事情呢……我知道你都是复制粘贴的……

还要注意的是这一步程序是没有任何状态输出的(如果你看到了估计肯定是错误提示……),如果想看到进度的话可以用shell中的ls -l等命令通过观察collection.dump文件的变化来揣测进度,以及带给你一些程序确实运行成功了的安全感。

2.2.2 a new collection

这一步主要是为接下来的load做准备:我们要建立一个新的数据库,然后把上一步dump的数据导入进来,然后还有几个关键而鬼畜的步骤,后面都会提到,所以这一步还是老老实实跟着做一下吧。

来,我们先启动一个新的Mongo实例,我举个栗子,可以这么做:

  1. mongod --dbpath tmp-mongo --storageEngine wiredTiger --nojournal

然后我们要连接这个实例并创建一个新集合

  1. mongo
  2. > use Recovery
  3. > db.brokedCollection.insert({test: 1})
  4. > db.brokedCollection.remove({})
  5. > db.brokedCollection.stats()

我们创建了一个新的叫做Recovery的数据库,并且插入移除过文档,所以这些集合的数据文件会被生成。使用stats()方法可以查看集合所对应的wt文件名称,当然了,因为我们只使用了一个集合,所以跑到tmp-mongo目录下ls一下也就知道这个collecion对应的wt文件是哪个了……为什么要知道这个?当然是下一步要用了……

2.2.3 dump --> new wt

接下来是见证奇迹的时刻:我们的数据很快就可以重现Mongo了!

  1. ./wt -v -h ../tmp-mongo -C "extensions=[./ext/compressors/snappy/.libs/libwiredtiger_snappy.so]" -R load -f ../collection.dump -r collection******

这一步就是把前面转出来的dump文件读入上一步生成的collection文件,所以-h指定的当然是我们上一步用的新mongo实例的路径。执行这一步的时候需要先把Mongo关上,不然mongo进程会霸占着这个wt文件不让你操作。

这个操作是有一个进度展示的table:collection-******: 1386220

来来来,见证一下奇迹:现在可以再用2.2.2里的方式启动这个Mongo实例了

  1. mongo
  2. > show dbs //应该可以看到Recovery有数据了
  3. > use Recovery
  4. > show collections //应该可以看到brokedCollection里有数据了
  5. > db.brokedCollection.count() //是0?呐尼?

看到0的那一刻估计你又开始紧张了,别急我们慢慢来,如果到这一步就能解决问题的话我们何苦要折腾出2.2.2这一步。

  1. > db.brokedCollection.find({}, {_id: 1})

接着执行这一条能看到数据,所以我估计你会再次燃起希望,接下来我们继续,让奇迹2出现吧:

2.3 完善一下

需要注意为了防止出错执行下面的步骤要确保MongoDB版本不小于3.2(因为3.2版本才是基于WiredTiger2.7构建的)。

  1. mongodump
  1. mongorestore --drop

没错就是这么简单,接下来我们就可以验证奇迹了。

  1. mongo
  2. > show dbs
  3. > use Recovery
  4. > show collections
  5. > db.brokedCollection.count()

这次都回来了吧,一切正常了吧……接下来可以restore到任何你想要restore的地方了,如何使用可自由发挥。

3 收工

收工吧,累死了。

也许你已经受到上面步骤的启发想到更多有意思的恢复方法了,然而人生苦短,适可而止……

MongoDB使用建议

对重要数据库所在机器操作前一定要提前backup一下,把万万没想到的损失降到最低;还有,除非真的不care数据的高可用性,不要随便关journal;单台机器上有多个库的话最好在配置文件中设置下directoryPerDB:true,让每个库有一个单独文件夹;有富余机器的话尽量做一下复制集……

从损坏的wt文件中恢复出WiredTiger集合的更多相关文章

  1. 《Java虚拟机原理图解》1.5、 class文件中的方法表集合--method方法在class文件中是怎样组织的

    0. 前言 了解JVM虚拟机原理是每一个Java程序员修炼的必经之路.但是由于JVM虚拟机中有很多的东西讲述的比较宽泛,在当前接触到的关于JVM虚拟机原理的教程或者博客中,绝大部分都是充斥的文字性的描 ...

  2. 《Java虚拟机原理图解》1.4 class文件中的字段表集合--field字段在class文件中是怎样组织的

    0.前言 了解JVM虚拟机原理是每一个Java程序员修炼的必经之路.但是由于JVM虚拟机中有很多的东西讲述的比较宽泛,在当前接触到的关于JVM虚拟机原理的教程或者博客中,绝大部分都是充斥的文字性的描述 ...

  3. 转载---class文件中的字段表集合--field字段在class文件中是怎样组织的

    写的太好了! https://blog.51cto.com/1459294/1932331

  4. mongodump 失败且导致mongo服务挂掉【本质原因,wt文件损坏】

    ====================================================== 标题遇到的问题是我要解决的问题的中间环节. 原本问题是:需要在之前standlone的Mo ...

  5. 最牛MongoDB灾难恢复(WiredTiger.wt文件损坏,Mongo无法启动)

    WiredTiger.wt文件是mongoDB的元数据文件,存储了其他数据库表的元数据信息.笔者最近遇到了WiredTiger.wt文件损坏的情况,MongoDB无法启动,数据库中的重要数据危在旦夕. ...

  6. 其原因可能是堆被损坏,这说明**.exe中或它加载的任何DLL中有Bug

    最近在写一个写日志文件的线程时,调用了HeapAlloc/HeapFree 申请/释放堆缓冲内存.调用HeapFree释放有个条件就是,日志的空闲缓冲队列中内存块超过100个.在测试的时候,发现调用H ...

  7. Oracle DBA的神器: PRM恢复工具,可脱离Oracle软件运行,直接读取Oracle数据文件中的数据

    Oracle DBA的神器: PRM恢复工具,可脱离Oracle软件运行,直接读取Oracle数据文件中的数据 PRM 全称为ParnassusData Recovery Manager ,由 诗檀软 ...

  8. Tools下的mdscongiguer 文件中 43行 oracle 配置 发现需要连接库 -lclntsh libclntsh.so 库是个什么东西呢?

    Tools下的mdscongiguer     文件中 43行  oracle 配置      发现需要连接库 -lclntsh      libclntsh.so 库是个什么东西呢? 分想一个知乎网 ...

  9. springMVC从上传的Excel文件中读取数据

    示例:导入客户文件(Excle文件) 一.编辑customer.xlsx 二.在spring的xml文件设置上传文件大小 <!-- 上传文件拦截,设置最大上传文件大小 10M=10*1024*1 ...

随机推荐

  1. 第13章 Swing程序设计----标签组件与图标

    在Swing中显示文本或提示信息的方法是使用标签.本节将探讨Swing标签的用法.如何创建标签,以及如何在标签上放置文本和图标. 1.标签的使用 标签可以显示一行只读文本.一个图像或带图像的文本,它并 ...

  2. jq中的移除事件

    unbind([type],[data]); 第一个参数是事件类型,第二个参数是将要移出的函数 说明: 1,如果没有参数,则删除所有绑定的事件 2,如果只提供了事件类型作为参数,则只删除该类型的绑定事 ...

  3. winxp iis5中修改最大连接数及添加多个站点

    winxp iis5中修改最大连接数及添加多个站点 最 近用asp做一些东西,需要用到iis,还需要用photoshop做一些图片.以前都是在win2003下面做,可是photoshop里面很多中文字 ...

  4. java dom4j解析xml实例(3)

    代码运行前需要先导入dom4j架包. 需要解析的XML文件test.xml如下: <students> <student age="25"><!--如 ...

  5. UIView 视图切换

    UIView之间常用视图之间切换方式 转载自:http://www.jianshu.com/p/0d53f9402c07 在平时编写代码的过程中,页面之间的跳转可以说就和MVC模式一样是开发必须的.但 ...

  6. Android 自定义 SpinnerButton(转)

    Android 自定义 SpinnerButton 模仿Android4.0的Spinner Button写的一个MySpinnerButton.这样在1.6~4.0.4版本都可以实现这种下拉框的效果 ...

  7. USACO Section 1.2 Transformations 解题报告

    题目 题目描述 一块 N x N正方形的黑白瓦片的图案要被转换成新的正方形图案. 写一个程序来找出将原始图案按照以下列转换方法转换成新图案的最小方式: 转 90 度:图案按顺时针转 90 度. 转 1 ...

  8. 原生 JS Ajax,GET和POST 请求实例代码

    javascript/js的ajax的GET请求代码如下所示: <script type="text/javascript"> /* 创建 XMLHttpRequest ...

  9. docker k8s 1.3.8 + flannel

    docker k8s + flannel kubernetes 是谷歌开源的 docker 集群管理解决方案. 项目地址: http://kubernetes.io/ 测试环境: node-1: 10 ...

  10. python之路:进阶篇 内置函数

     li = [11, 22, 33] news = map(  li = [100, 2200, 3300] news = map(  [13, 24, 35] [11, 11, 11] [22, 4 ...