MongoDB复制集简介(三)--复制集中的回滚

一、复制集中回滚介绍

 

事务回滚在关系型数据库中是常见的,在MongoDB复制集中也存在回滚。这里的回滚是指当发生主从失败切换时,前主机重新加入复制集后,将回滚失败之前的写操作。回滚仅仅针对主机失败前,主机已经完成的写操作但从机没有复制的写操作。主机失败后,从机成为主机,由于没有复制刚刚主机的写操作,新的复制集中没有刚刚的数据。这时如果老主机作为新的从机重新加入到复制集,老主机将回滚刚刚未复制的写操作,以与其他成员保持数据一致性,对已经完成复制的写操作,MongoDB不会回滚。MongoDB复制集发生回滚的概率很低,主要发生在出现网络分区的场景下。

当发生回滚时,MongoDB会将回滚数据写成一个BSON文件,放到数据库目录下的一个rollback子目录下。文件名称格式如下:
 
  <database>.<collection>.<timestamp>.bson

DBA需要考虑是放弃这些数据,还是要重新应用这些数据。 如果DBA想应用这些数据,可以手工用bsondump 读取出dump中的内容,也可以 mongorestore 命令在新的主机上应用这些数据。

为避免复制集发生回滚,应用程序可以使用“写确认”方法保证写操作已经传播给复制集中的成员。

 

二、模拟

 

下面我们做个实验,模拟一下复制集的回滚。
我们构建一个具有主机、从机和仲裁者三个成员的复制集test。

在shell下执行下面命令启动三个mongod进程
 
./mongod --dbpath /mongodb/data/db_1 --port 27117 --replSet test --logpath /mongodb/log/mongod_1.log  --fork
./mongod --dbpath /mongodb/data/db_2 --port 27217 --replSet test --logpath /mongodb/log/mongod_2.log  --fork
./mongod --dbpath /mongodb/data/db_3 --port 27317 --replSet test --logpath /mongodb/log/mongod_3.log  --fork

 

使用mongo shell工具连接到端口号27117的mongod,执行rs.initiate()初始化复制集。

[root@localhost bin]# ./mongo --port  27117
> rs.initiate()
{
        "info2" : "no configuration explicitly specified -- making one",
        "me" : "localhost.localdomain:27117",
        "info" : "Config now saved locally.  Should come online in about a minute.",
        "ok" : 1
}

test:OTHER> rs.add("localhost.localdomain:27217")
{ "ok" : 1 }
test:PRIMARY> rs.addArb("localhost.localdomain:27317")
{ "ok" : 1 }

 

在设置客户端能够从从机读取数据,在每个成员的shell下都执行db.getMongo().setSlaveOk()

在主机上插入数据
test:PRIMARY> db.test.insert({empno:100,name:100})
test:PRIMARY> db.test.insert({empno:101,name:101})
test:PRIMARY> db.test.insert({empno:102,name:102})

在从机上读取,发现3条数据已经插入成功
test:SECONDARY> use test
switched to db test
test:SECONDARY> db.test.find()
{ "_id" : ObjectId("526aa7144ff959233867ff95"), "empno" : 100, "name" : 100 }
{ "_id" : ObjectId("526aa7194ff959233867ff96"), "empno" : 101, "name" : 101 }
{ "_id" : ObjectId("526aa7194ff959233867ff97"), "empno" : 102, "name" : 102 }
test:SECONDARY>

关闭从机进程,准备模拟从机未复制主机的写操作:
test:SECONDARY> use admin
switched to db admin
test:SECONDARY> db.shutdownServer()
2013-10-25T13:19:33.447-0400 DBClientCursor::init call() failed
server should be down...
2013-10-25T13:19:33.461-0400 trying reconnect to 127.0.0.1:27217
2013-10-25T13:19:33.464-0400 reconnect 127.0.0.1:27217 failed couldn't connect to server 127.0.0.1:27217
2013-10-25T13:19:33.470-0400 trying reconnect to 127.0.0.1:27217
2013-10-25T13:19:33.470-0400 reconnect 127.0.0.1:27217 failed couldn't connect to server 127.0.0.1:27217

在主机上查看复制集状态,确认从机已经不可达到
test:PRIMARY> rs.status()
{
        "set" : "test",
        "date" : ISODate("2013-10-25T17:20:50Z"),
        "myState" : 1,
         .......
                {
                        "_id" : 1,
                        "name" : "localhost.localdomain:27217",
                        "health" : 0,
                        "state" : 8,
                        "stateStr" : "(not reachable/healthy)",
                        "uptime" : 0,
                        "optime" : Timestamp(1382721305, 3),
                        "optimeDate" : ISODate("2013-10-25T17:15:05Z"),
                        "lastHeartbeat" : ISODate("2013-10-25T17:20:49Z"),
                        "lastHeartbeatRecv" : ISODate("2013-10-25T17:19:31Z"),
                        "pingMs" : 0,
                        "syncingTo" : "localhost.localdomain:27117"
                },
        .......
        ],
        "ok" : 1
}
test:PRIMARY>


主机继续插入数据,并查询确保插入成功
test:PRIMARY> use test
switched to db test
test:PRIMARY> db.test.insert({empno:103,name:103})
test:PRIMARY> db.test.insert({empno:104,name:104})
test:PRIMARY> db.test.insert({empno:105,name:105})
test:PRIMARY> db.test.find()
{ "_id" : ObjectId("526aa7144ff959233867ff95"), "empno" : 100, "name" : 100 }
{ "_id" : ObjectId("526aa7194ff959233867ff96"), "empno" : 101, "name" : 101 }
{ "_id" : ObjectId("526aa7194ff959233867ff97"), "empno" : 102, "name" : 102 }
{ "_id" : ObjectId("526aa9204ff959233867ff98"), "empno" : 103, "name" : 103 }
{ "_id" : ObjectId("526aa9294ff959233867ff99"), "empno" : 104, "name" : 104 }
{ "_id" : ObjectId("526aa9304ff959233867ff9a"), "empno" : 105, "name" : 105 }
test:PRIMARY>

kill掉主机进程,后启动原从机进程,即端口号为27217的mongod,该进程启动后将成为主机。我们查看test集合,发现empno为103、104、105的数据在新主机中没有
[root@localhost bin]#  ./mongod --dbpath /mongodb/data/db_2 --port 27217 --replSet test --logpath /mongodb/log/mongod_2.log  --fork
[root@localhost bin]# ./mongo --port 27217
test:PRIMARY> use test
switched to db test
test:PRIMARY> db.test.find()
{ "_id" : ObjectId("526aa7144ff959233867ff95"), "empno" : 100, "name" : 100 }
{ "_id" : ObjectId("526aa7194ff959233867ff96"), "empno" : 101, "name" : 101 }
{ "_id" : ObjectId("526aa7194ff959233867ff97"), "empno" : 102, "name" : 102 }
test:PRIMARY>

此时,启动老主机将发生回滚,老主机将对empno为103、104、105的数据进行删除。
[root@localhost bin]# ./mongod --dbpath /mongodb/data/db_1 --port 27117 --replSet test --logpath /mongodb/log/mongod_1.log  --fork
[root@localhost bin]# ./mongo --port 27117
test:SECONDARY> db.getMongo().setSlaveOk()
test:SECONDARY> use test
switched to db test
test:SECONDARY> db.test.find()
{ "_id" : ObjectId("526aa7144ff959233867ff95"), "empno" : 100, "name" : 100 }
{ "_id" : ObjectId("526aa7194ff959233867ff96"), "empno" : 101, "name" : 101 }
{ "_id" : ObjectId("526aa7194ff959233867ff97"), "empno" : 102, "name" : 102 }
test:SECONDARY>

我们在/mongodb/log/mongod_1.log中发现关于rollback的日志:
2013-10-25T13:33:48.171-0400 [rsBackgroundSync] replSet we are ahead of the sync source, will try to roll back
2013-10-25T13:33:48.209-0400 [rsBackgroundSync] replSet rollback 0
2013-10-25T13:33:48.209-0400 [rsBackgroundSync] replSet ROLLBACK
2013-10-25T13:33:48.209-0400 [rsBackgroundSync] replSet rollback 1
2013-10-25T13:33:48.209-0400 [rsBackgroundSync] replSet rollback 2 FindCommonPoint
2013-10-25T13:33:48.211-0400 [rsBackgroundSync] replSet info rollback our last optime:   Oct 25 13:24:00:1
2013-10-25T13:33:48.211-0400 [rsBackgroundSync] replSet info rollback their last optime: Oct 25 13:15:05:3
2013-10-25T13:33:48.211-0400 [rsBackgroundSync] replSet info rollback diff in end of log times: 535 seconds
2013-10-25T13:33:48.211-0400 [rsBackgroundSync] replSet rollback found matching events at Oct 25 13:15:05:3
2013-10-25T13:33:48.211-0400 [rsBackgroundSync] replSet rollback findcommonpoint scanned : 4
2013-10-25T13:33:48.211-0400 [rsBackgroundSync] replSet replSet rollback 3 fixup
2013-10-25T13:33:48.214-0400 [rsBackgroundSync] replSet rollback 3.5
2013-10-25T13:33:48.215-0400 [rsBackgroundSync] replSet rollback 4 n:3
2013-10-25T13:33:48.215-0400 [rsBackgroundSync] replSet minvalid=Oct 25 13:15:05 526aa719:3
2013-10-25T13:33:48.217-0400 [rsBackgroundSync] build index local.replset.minvalid { _id: 1 }
2013-10-25T13:33:48.219-0400 [rsBackgroundSync] build index done.  scanned 0 total records. 0.001 secs
2013-10-25T13:33:48.219-0400 [rsBackgroundSync] replSet rollback 4.6
2013-10-25T13:33:48.219-0400 [rsBackgroundSync] replSet rollback 4.7
2013-10-25T13:33:48.221-0400 [rsBackgroundSync] replSet rollback 5 d:6 u:0
2013-10-25T13:33:48.221-0400 [rsBackgroundSync] replSet rollback 6
2013-10-25T13:33:48.222-0400 [rsBackgroundSync] replSet rollback 7
2013-10-25T13:33:48.222-0400 [rsBackgroundSync] replSet rollback done


在/mongodb/data/db_1/rollback目录下看到bson文件:
[root@localhost bin]# ls -al /mongodb/data/db_1/rollback/
total 12
drwxr-xr-x 2 root root 4096 Oct 25 13:33 .
drwxr-xr-x 4 root root 4096 Oct 25 13:33 ..
-rw-r--r-- 1 root root  153 Oct 25 13:33 test.test.2013-10-25T17-33-48.0.bson
[root@localhost bin]#

我们使用工具bsondump将bson转换为json文件,就可以看到empno为103、104、105的回滚数据。
[root@localhost bin]# ./bsondump  /mongodb/data/db_1/rollback/test.test.2013-10-25T17-33-48.0.bson >test_rollback.json
3 objects found
[root@localhost bin]# more test_rollback.json
{ "_id" : ObjectId( "526aa9204ff959233867ff98" ), "empno" : 103, "name" : 103 }
{ "_id" : ObjectId( "526aa9294ff959233867ff99" ), "empno" : 104, "name" : 104 }
{ "_id" : ObjectId( "526aa9304ff959233867ff9a" ), "empno" : 105, "name" : 105 }
[root@localhost bin]#

三、小结

我们通过实验发现,只要在主机插入时,从机没有接收到的数据主从切换后都将被回滚。作为DBA,如果发生了主从切换,需要关注是否发生了回滚。如果有回滚发生,我们需要考虑如何处理回滚数据。

本实验中设置了一个仲裁器,主要是用于保证在老从机关闭时集群还能保证可用,且主机此后插入的数据没有被任何从机复制。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值