一、副本集的读write理论
站在应用客户端的角度,无论一个MongoDB的实例是一个单独的服务或是一个集群,都是透明的。
默认情况下,在MongoDB中,副本集的读操作由primary返回结果。
用户可以对每一个连接配置read优先(read preference),使read操作从secondary成员返回结果。如果客户端配置了优先读来允许secondary读,读操作可以从secondary成员(没有复制最新的write操作)返回数据。
这种行为在有时能反映最终一致性因为secondary成员的状态最终反映了primary的状态,并且,MongDB不能保证从secondary成员上读的严格一致性(write操作具有复制延迟,且可能会回退)。
· write关注
· read优先
· read优先的过程
1、write关注
站在应用客户端的角度,无论一个MongoDB的实例是一个单独的服务或是一个集群,都是透明的。然则,副本集提供了一些write的配置。
a.检查副本集的write操作
对于一个副本集,默认的write关注能保证primary上的write操作。然而,你可以覆盖这个默认的write关注,如通过指定副本集的成员的数量来保证。
为了覆盖默认的write关注,为每一个write操作指定一个write关注。例如,下面的write关注,指定了write传播到primary和至少1个secondary,或者方法超时5s。
db.products.insert(
{ item: "envelopes", qty : 100, type: "Clasp" },
{ writeConcern: { w: 2, wtimeout: 5000 } }
)
你可以对一个write 关注设置一个超时阀值,这能避免write操作未成功导致的无限期锁定。例如,write关注要求副本集的4个成员的反馈但是副本集只有3个可用成员,那么,这个操作就会阻塞直至这些成员变得可用。
b.修改默认的write关注
你可以通过设置副本集配置的getLastErrorDefault方法来修改副本集默认的write关注。下述命令创建了一个配置,在大多数投票成员write操作完成前等待:
cfg = rs.conf()
cfg.settings = {}
cfg.settings.getLastErrorDefaults = { w: "majority", wtimeout: 5000 }
rs.reconfig(cfg)
如果你设定一个write关注一个特定的write关注,write操作就会使用自己特性的write关注来替换默认的write关注。
2、read优先
默认时,一个应用的读操作直接在副本集的primary成员上执行。因为写操作能够保证在唯一的primary上,从primary上能返回最新的文档数据。
对于一个不需要完全最新数据的应用,你可以将一些或者全部的读操作分布到副本集的secondary成员上,来提升吞吐、减少延迟。
a. MongoDB 驱动提供了五种read 优先模式.
Read Preference Mode | Description |
primary | Default mode. All operations read from the current replica set primary. |
primary Preferred | In most situations, operations read from the primary but if it is unavailable, operations read from secondary members. |
secondary | All operations read from the secondary members of the replica set. |
secondary Preferred | In most situations, operations read from secondary members but if no secondary members are available, operations read from the primary. |
nearest | Operations read from member of the replica set with the least network latency,irrespective of the member’s type. |
b. Tag设置
Tag设置允许你将read操作导向特定的副本集成员。
自定义的read优先和write关注,从不同的方面评估了Tag设置。Read优先考虑到选择读取成员的Tag值,write关注则忽略了选择成员的Tag值,除非考虑这个值是否是唯一性的。
你可以指定Tag设置,在以下的read优先模式:
· primary Preferred
· secondary
· secondary Preferred
· nearest
Tag和primary模式并不兼容,总的来说,仅仅支持从副本集的secondary成员进行读操作。然而,nearest 优先模式下,选择最低网络延迟的匹配成员,这个成员可能是primary或secondary。
所有使用相同的成员选择逻辑的接口,用来选择一个成员来执行read操作,给予read优先模式和tag设置。
3、read优先过程
MongoDB使用一下的过程来引导操作到副本集和分片集群中。为了决定如何路由他们的操作,应用程序周期性的更新他们的副本集的状态视图,进而识别出那些成员上线或者下线,那些成员是primary,并且验证每个mongod实例之间的延迟。
a.成员选择
客户端,通过他们的驱动,以及分片集群的mongos实例,周期性地更新副本集的状态视图。
当你选择一个非primary默认是read优先,驱动将决定哪一个成员将作为以下进程的目标:
· 集中一个可用成员的列表,包含成员的类型。
· 排除掉不符合Tag设置的成员。
· 按照绝对值,决定距离client最近的成。
· 创建一个成员列表,明确绝对最近成员的ping延迟,
· 从这些host中选择一个随机的成员,该成员将接受read操作。
驱动然后可以将线程或者链接同被选中的成员结合。这个请求协议由应用程序配置生成。查看你的驱动文档,关于需求协议的配置和默认方法。
b.请求协议
因为一个副本集的secondary成员可能滞后于当前的primary一定的时间,从这个secondary成员上读取的数据可能映射出不同的时间点。为了防止从其他的成员上产生的跳跃性顺序读,驱动将应用线程联系到第一次读取的特定成员上,从而避免从其他成员上读取。该线程将持续从相同的成员上读取数据直到:
· 应用程序执行了一个不同的read优先
· 该线程终止
· 客户端接受到了一个socket异常,如网络错误或者mongos关闭连接,这将触发一个透明的retry重连
当使用请求协议时,如果客户端查出副本集选举出一个新的primary,驱动将丢弃所有的线程-成员的连接。
c.自动重连
MongoDB驱动和Mongod实例之间的连接必须平衡两个关注:
· 客户端尝试获取当前的结果,任何的连接应该尽可能地从相同的副本集成员上读取数据。请求优先使用请求协议。
· 客户端需要最小化数据库不可达(连接、网络问题、失效)时候的响应时间
· 一旦同一个实例建立好了连接,尽可能重用
· 如果之前的mongod连接丢失,尝试重连到一个新的成员,遵守当前的read优先模式。重连对于应用程序本身是透明的。如果连接允许从secondary读取数据,重连后,应用程序可以接收到从不同secondary上的2个顺序读。依赖于每个secondary成员的副本状态,文档可以映射你的数据库在不同时刻的状态。
· 仅仅在尝试连接到副本集的满足read优先模式和Tag设置的3个成员后返回错误,如果副本集有少于3个成员, 客户端在连接到所有的现存成员后会报错。报错后,驱动会使用特定的read优先模式来选择一个新的成员。不存在特定的read优先模式时,驱动会使用primary。
· 当检测到失效的情景时,驱动尽可能快地尝试刷新副本集的状态。
在version3.0的改变:mongos实例在不同的连接中有细微的差别,mongos实例在每次请求后返回secondary连接到连接池中,结果是,mongos会重新评估每一个操作的read优先。
d. 分片集群中的read优先
在大多数的分片集群中,每一个分片都是由副本集组成。如此,read优先也被使用。关于read优先,分片集群中的read操作于非分片副本集的一样。
与简单的副本集相似,在分片集群中,客户端与mongos在分片间的所有的交互,实际上都是连接到了副本成员。mongos然后负责read优先的应用,对应用透明。
直到version2.2,实现完全分片环境下的read优先没有配置上的修改来。每个mongos维护他们自己的副本成员连接池。
· 一个没有指定read优先的请求,将使用默认的primary,除非,mongos重用了一个现存的不同模式的连接。为避免混淆,通常明确地设置你的read优先模式。
· 所有的nearest和延迟计算,反映了mongos和mongd实例间的连接,而不是客户端和mongod实例间的连接。这产生了预期结果,因为所有的结果在返回到客户端时必须通过mongos。