您的位置:首页 > 数据库 > Mongodb

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

2013-10-25 18:09 706 查看
一、复制集中回滚介绍

 

事务回滚在关系型数据库中是常见的,在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 [rsBackgro
a6b6
undSync] 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,如果发生了主从切换,需要关注是否发生了回滚。如果有回滚发生,我们需要考虑如何处理回滚数据。

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

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  mongodb rollback