MongoDB复制集简介(四)--复制集的写关注

来源:互联网 发布:9wifi九维网络登入不了 编辑:程序博客网 时间:2024/04/30 04:11

 一、概念介绍

“Write Concern(写关注)”描述了当系统反馈写成功后MongoDB提供数据持久性保证程度,写关注越强,MongoDB提供的保证程度越高。当写操作具有弱写关注时,写操作会迅速返回,但是写操作可能不具有持久性;具有强写关注的客户端在发出写操作后需要等待MongoDB确认写操作成功。在单机环境下,Journaled为最高写关注级别,表示MongoDB只在写操作已经成功且日志已经写到磁盘后,才确认写成功;Acknowledged为次一等级的写关注,表示MongoDB已经确认收到了正确的写操作,应用可以排除网络故障或者重复键值等错误,这是MongoDB默认的写关注。

在复制集环境下,基本级别的写关注只能影响一台机器上的写操作。应用程序可以通过在getLastError提供w参数的不同配置,将复制集的写关注级别设置为Replica Acknowledged。通过Replica Acknowledged,我们可以保证写操作已经传播到复制集的成员中。

如下图所示,在一个3节点的复制集中,客户端在发出写请求后使用带w:2的getLastError进行写操作确认。w:2表示至少写操作已经传播到了复制集中的两个成员(包括主机),图中在第二成员已经收到主机传播来的日志且已经获得应用后对getLastError做出反馈,不需要等待日志传播到第三成员。如果我们将w的值设置超过了当前拥有数据拷贝的复制集成员个数,会导致getLastError被阻塞,直到其他数据成员变得可用满足了w的设置要求。当然,我们可以对getLastError设置一个超时阈值,以防止getLastError一直被阻塞。

 

 

 

在复制集配置中,有两个设置和复制集的写关注有很大关系,一个为成员的tags,另一个为复制集的getLastErrorModes设置。通过修改这两个设置,我们可以定制自己的复制集写关注,当然tags也可以用于设置读喜好,这里就不涉及了。当我们定制写关注模式时,需要注意的是该模式需要指定一个域名和一个值,如果该值为n,则要求至少有n个成员反馈接受了写操作且这n个成员对应tag的名字与模式指定的域名相同,但是每个tag的值不同,这时才能确认写成功。

二、应用举例

1、假设一个具有5个成员的复制集,每个复制成员的tags依次如下:

cfg=rs.conf()

cfg.members[0].tags={ "use": "reporting" }
cfg.members[1].tags={ "use": "backup" }
cfg.members[2].tags={ "use": "application" }
cfg.members[3].tags={ "use": "application" }
cfg.members[4].tags={ "use": "application" }

cfg.settings = { getLastErrorModes: { use2: { "use": 2 } } }

上面的getLastErrorModes要求至少有两个不同值的“use” tag成员反馈,才给应用反馈写结果,比如第一、第二个成员反馈写操作,主机就可以给应用反馈写结果。如果第三、第四个成员反馈写操作,还不能给应用反馈,因为他们两个的"use“tag值相同,必须要等到第一或者第二个成员反馈写操作后,才能给应用反馈。

2、假设一个具有三个成员的复制集,每个复制成员的tags依次如下:

  { "disk": "ssd" }
  { "disk": "ssd" }
  { "disk": "ssd" }

我们可以将getLastErrorModes设置如下:

cfg.settings = { getLastErrorModes: { disk2: { "disk": 2 } } }

getLastErrorModes配置要求至少具有两个不同的disk tag值的成员反馈才向应用反馈写结果,而实际成员的tag配置完全相同,不可能具有两个不同值的成员反馈,因此这种配置将会失败,如下所示。


test:PRIMARY> rs.reconfig(cfg)
{
        "errmsg" : "exception: mode { disk: 2.0 } requires 2 tagged with disk, but only 1 with this tag were found",
        "code" : 14831,
        "ok" : 0
}

3、假设一个具有三个成员的复制集,每个复制成员的tags依次如下:

  { "disk": "ssd" }
  { "disk": "san" }
  { "disk": "spinning" }

根据上面的tags,如果我们想配置成在写操作传播到SAN后才反馈成功,这种配置方案在现有tags下无法实现。如果我们想实现上述写关注策略,需要额外的tags才能实现,如下:

cfg=rs.conf()
cfg.members[0].tags={ "disk": "ssd" }
cfg.members[1].tags={ "disk": "san","disksan":"san" }
cfg.members[2].tags={ "disk": "spinning" }
cfg.settings = { getLastErrorModes: { disksan1: { "disksan": 1 } } }

rs.reconfig(cfg)

 

上述配置成功后,我们可以看到复制集的配置如下,端口号27217对应的成员具有两个tag,而其他成员只有一个tag,我们可以使用disksan的tag来控制写操作必须传播到27217对应的成员

test:PRIMARY> rs.conf()
{
        "_id" : "test",
        "version" : 19,
        "members" : [
                {
                        "_id" : 0,
                        "host" : "localhost.localdomain:27117",
                        "tags" : {
                                "disk" : "ssd"
                        }
                },
                {
                        "_id" : 1,
                        "host" : "localhost.localdomain:27217",
                        "tags" : {
                                "disk" : "san",
                                "disksan" : "san"
                        }
                },
                {
                        "_id" : 2,
                        "host" : "localhost.localdomain:27317",
                        "tags" : {
                                "disk" : "spinning"
                        }
                }
        ],
        "settings" : {
                "getLastErrorModes" : {
                        "disksan1" : {
                                "disksan" : 1
                        }
                }
        }
}

 

在端口号为27217的成员可用的情况下,我们使用上述写关注模式,得到以下结果,表示写已经成功。

test:PRIMARY> use test

test:PRIMARY> db.test.insert({empno:100,name:100})
test:PRIMARY> db.runCommand( { getLastError: 1, w: "disksan1" } )
{
        "n" : 0,
        "lastOp" : Timestamp(1382978081, 1),
        "connectionId" : 957,
        "writtenTo" : [
                {
                        "_id" : 1,
                        "host" : "localhost.localdomain:27217",
                        "tags" : {
                                "disk" : "san",
                                "disksan" : "san"
                        }
                },
                {
                        "_id" : 0,
                        "host" : "localhost.localdomain:27117",
                        "tags" : {
                                "dis" : "ssd"
                        }
                },
                {
                        "_id" : 2,
                        "host" : "localhost.localdomain:27317",
                        "tags" : {
                                "disk" : "spinning"
                        }
                }
        ],
        "wtime" : 0,
        "err" : null,
        "ok" : 1
}

如果端口号为27217的成员不可用了,此事还使用该写关注模式,会发生什么?

我们通过kill进程的方法模拟端口号为27217的成员不可用,当该mongd进程被kill掉以后,复制集的状态如下:

test:SECONDARY> rs.status()
{
        "set" : "test",
        "date" : ISODate("2013-10-28T16:40:08Z"),
        "myState" : 2,
        "syncingTo" : "localhost.localdomain:27317",
        "members" : [
                {
                        "_id" : 0,
                        "name" : "localhost.localdomain:27117",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        "uptime" : 1274,
                        "optime" : Timestamp(1382978081, 1),
                        "optimeDate" : ISODate("2013-10-28T16:34:41Z"),
                        "errmsg" : "syncing to: localhost.localdomain:27317",
                        "self" : true
                },
                {
                        "_id" : 1,
                        "name" : "localhost.localdomain:27217",
                        "health" : 0,
                        "state" : 8,
                        "stateStr" : "(not reachable/healthy)",
                        "uptime" : 0,
                        "optime" : Timestamp(1382978081, 1),
                        "optimeDate" : ISODate("2013-10-28T16:34:41Z"),
                        "lastHeartbeat" : ISODate("2013-10-28T16:40:06Z"),
                        "lastHeartbeatRecv" : ISODate("2013-10-28T16:39:56Z"),
                        "pingMs" : 0
                },
                {
                        "_id" : 2,
                        "name" : "localhost.localdomain:27317",
                        "health" : 1,
                        "state" : 1,
                        "stateStr" : "PRIMARY",
                        "uptime" : 1017,
                        "optime" : Timestamp(1382978081, 1),
                        "optimeDate" : ISODate("2013-10-28T16:34:41Z"),
                        "lastHeartbeat" : ISODate("2013-10-28T16:40:07Z"),
                        "lastHeartbeatRecv" : ISODate("2013-10-28T16:40:08Z"),
                        "pingMs" : 0
                }
        ],
        "ok" : 1
}

我们使用mongo工具连接到主机,执行插入操作后再调用getLastError,并增加超时10秒参数,以防止getLastError被阻塞。执行结果显示getLastError发生超时,表示设置的写关注模式未成功,这是由于具有disksan的tag成员已经不可用,写操作不可能传播到该成员上。但是这时在主机上查询empno为109的文档时,还是能够显示查询成功的,表明主机上已经写入该数据。

test:PRIMARY> db.test.insert({empno:109,name:109})
test:PRIMARY> db.runCommand( { getLastError: 1, w: "disksan1",wtimeout:10000 } )
{
        "n" : 0,
        "lastOp" : Timestamp(1382978744, 1),
        "connectionId" : 964,
        "wtimeout" : true,
        "waited" : 10001,
        "writtenTo" : [
                {
                        "_id" : 2,
                        "host" : "localhost.localdomain:27317",
                        "tags" : {
                                "disk" : "spinning"
                        }
                },
                {
                        "_id" : 0,
                        "host" : "localhost.localdomain:27117",
                        "tags" : {
                                "disk" : "ssd"
                        }
                }
        ],
        "err" : "timeout",
        "ok" : 1
}
test:PRIMARY> db.test.find({empno:109}))
{ "_id" : ObjectId("526e94b82e59202b33f85752"), "empno" : 109, "name" : 109 }

 

 

 

 

原创粉丝点击