在之前的一篇文章中,介绍了balancer会声明使用分布式锁来协调分布式环境下的信息沟通并确保事务一致性,有关分布式锁的一些原理性信息可以参见这几篇文章:
http://wenku.baidu.com/view/19ce3085b9d528ea81c77982.html
http://wenku.baidu.com/view/d94ac11ffc4ffe473368ab27.html
下图是mongod使用sharding机制时的一个系统架构图(有关如果配置sharding并进行数据存储参见我的这篇文章 ):
这里Mongod实现的比较简单,它主要是通过在config服务器结点上创建并维护两个lock集合(lockpings和lock),并 mongos启动时会在这两个集合中各创建一条数据记录,然后通过维护该记录的状态(包括时间戳,state)来标识当前mongos结点的锁使用情况。 如果多台机器之间想使用一个锁,只要确保锁的名称一致就行,这里就会在configsvr结点的相应集合中共享这一条记录以确保只有一个mongos结点 获得了该锁。
首先mongod会在配置为configsvr结点上创建并维护两个集合,如下图:
config.lockpings:
_id: 存储mongos发送来的balancer的标识信息(格式=> "机器名称:时间戳:随机数")
ping: 以及发送ping指令时的时间(日期时间格式)
config.lock:
_id: 存储锁的名称(如balancer)
process: 关联config.lockpings中_id的字段(并与其保持一致)
state: 锁状态(是否有操作使用该锁)。0为未锁定,1为锁定。
ts: 锁转为使用状态时的时间戳
when :锁转为使用状态时的时间(日期时间格式)
who: 谁使用了该锁(格式为process + 线程名称 + 随时数)
why: 使用锁的原因(如:doing balancer round)
了解上面内容之后,我们看一下mongos分布式锁的类关系图:
其中DistributedLock类就是分布式锁的结构,它包括如下信息:
_name:锁名称,与config.lock中的_id相绑定
_conn:要链接到的mongod服务器地址
_ns: 使用的锁集合名称,构造函数将其初始化为"config.locks"
_takeoverMinutes: 强制接管的时间(当某个lock长时间不被解锁或无响应信息时),这时系统会回收该lock(通过将state置1)
lock_entry() : 当系统开始获取lock时所执行的操作
unlock():解除锁定
dist_lock_try类主要用于对上面的DistributedLock类操作进行封装,以便于使用.
其构造函数和析构函数与DistributedLock类的lock_entry()和unlock()方法对应。
下面我们就通过源码来进一步分析其底层的实现原理, 首先我们看一下DistributedLock的构造函数:
上面的构造方法主要是完成初始化conn,name,takeoverMinutes等相关属性,之后它会调用 distLockPinger.got()来向mongod发送ping指令,如下:
它使用线程方式来执行distLockPingThread()方法来发送ping指令,并最终执行下面方法:
上面的方法首先会更新config.lockpings集合中ping的时间信息,之后从config.lock集合中找出还在持有lock的 process信息,并从lockpings集合中去掉除process之外的且时间超过4天的lockping信息。最后为lockpings创建索 引,来为后面的更新操作提升效率(该集合中的数据会越来越多)。
我们可以将上面方法简单的理解为在获取使用lock之前的准备和维护工作。
到目前DistributedLock的初始化工作就完成了,下面看一下它的使用流程。在之前的文章中介绍过,当构造分布式锁之后,它会使用下面代码来尝试获取相应的锁:
上面的dist_lock_try()就是开始尝试获取当使用该锁(balanceLock)的代码,其构造函数如下:
它会持有传入的DistributedLock实例指针绑定到其自己的_lock(DistributedLock)属性上,然后使用该_lock来执行 DistributedLock的lock_try()方法,这里持有该_lock的主要目的还包括在其析构函数中调用该_lock的unlock()方 法,如下:
下面我们分别来看一下lock_try()和unlock()方法:
上面的方法首先要尝试在config.locks集合中找到相应的锁记录信息,如没有则会创建一条记录。
如有则判断记录的state状态(如果为1表示其正在被使用),如果使用期限未超过强制接管期限(elapsed <= _takeoverMinutes)或未超过100年则,则无法获取当前锁。
当可以修改锁状态时,则使用update去更新lock记录的相关信息(包括state, when,who,why,ts等),同时对异常情况(一个事务结束锁后,另一事务未及时获取该锁)也做了容错处理。
如果上面方法成功执行后,会更新config.locks中的相应数据记录,并返回true,表示当前进程已获取了该lock.
在成功执行锁定的代码段之后,就需要对当前代码段解锁,这是通过下面方法进行的:
mongos会在解锁出现问题(如远程服务器停止响应等)尝试执行三次解锁,解锁主要就是将远程服务器的config.locks中相应锁记录字段state置为0即可。
好了,今天的内容就先到这里了。
原文链接:http://www.cnblogs.com/daizhj/archive/2011/05/30/mongodb_source_distlock.html
作者: daizhj, 代震军
微博: http://t.sina.com.cn/daizhj
Tags: mongodb,c++,balance,DistributedLock, 分布式锁