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复制集中也存在回滚。这里的回滚是指当发生主从失败切换时,前主机重新加入复制集后,将回滚失败之前的写操作。回滚仅仅针对主机失败前,主机已经完成的写操作但从机没有复制的写操作。主机失败后,从机成为主机,由于没有复制刚刚主机的写操作,新的复制集中没有刚刚的数据。这时如果老主机作为新的从机重新加入到复制集,老主机将回滚刚刚未复制的写操作,以与其他成员保持数据一致性,对已经完成复制的写操作,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 复制集中存在的一个问题
- MongoDB复制集简介(四)--复制集的写关注
- MongoDB复制集简介(一)--初步认识
- MongoDB复制集简介(二)--内部信息
- MongoDB复制选举原理以及复制集的管理
- MongoDB(一):简介
- MongoDB基础之九 replication复制集
- mongodb 复制数据库和表
- mongodb笔记05(MongoDB 复制(副本集))
- 河北省地税数据上收集中及异地容灾应用(RealSync数据库同步复制)
- MongoDB 复制集
- Mongodb 分片 + 复制集
- MongoDB入门简介 java
- MongoDB基于复制集创建索引
- 复制(1)——SQLServer 复制简介
- MongoDB 修改器 简介
- mongodb 复制集
- MongoDB入门简介
- MongoDb复制-主从复制
- [Mongodb] Relica Set复制集集群简单搭建--持续更新