mongodb集群的分布式鎖的實現研究

jopen 10年前發布 | 31K 次閱讀 NoSQL數據庫 MongoDB

分布式鎖的作用

一般來講,鎖的作用是在于解決不同的執行流之間對于同一個資源的競爭而產生的問題。分布式鎖的作用就在于解決分布式程序中,分布在不同機器上的執行流對于資源的競爭問題。在mongodb的cluster上, 多個mongos都會發起balance這個過程。而一個時期內,只能有一個balance過程的存在。因此,如何解決多個mongos進程都要發起balance這個過程,需要分布式鎖。

代碼位置

代碼版本采用的是2.4.11版本,這個比較接近我們線上運行的stable版本。

主要涉及的代碼文件:

mongo/client/distlock.cpp

mongo/s/type_locks.h

mongo/s/type_lockpings.h

鎖的實體

分布式鎖一定會有一個存儲鎖的位置。無論文件鎖或者是內核中的mutex或者自旋鎖、讀寫鎖,都有一個鎖的"實體“, 通過代碼對這個”實體“進行一些保護性質的運算,來實現鎖定或者是解鎖的功能。

mongodb采用的將數據庫作為鎖的”實體“。在mongodb sharding cluster中有一個重要的數據庫config,

在這個db中有兩個collection和分布式鎖有關。

一個是config.locks


mongos> db.locks.find().pretty()
{
        "_id" : "configUpgrade",
        "process" : "i-qikzt805:50000:1390191129:1804289383",
        "state" : 0,
        "ts" : ObjectId("52dca219420e5e3bb3e63ee9"),
        "when" : ISODate("2014-01-20T04:12:09.751Z"),
        "who" : "i-qikzt805:50000:1390191129:1804289383:mongosMain:846930886",
        "why" : "upgrading config database to new format v4"
}
{
        "_id" : "balancer",
        "process" : "qc24:50000:1399171433:1804289383",
        "state" : 2,
        "ts" : ObjectId("54115f46274b8459f178c927"),
        "when" : ISODate("2014-09-11T08:37:26.462Z"),
        "who" : "qc24:50000:1399171433:1804289383:Balancer:846930886",
        "why" : "doing balance round"
}
{
        "_id" : "user_data.user_data",
        "process" : "qc-clouddb1:30001:1409913195:236929073",
        "state" : 0,
        "ts" : ObjectId("5409c74dc3a03d987a4a2d88"),
        "when" : ISODate("2014-09-05T14:23:09.190Z"),
        "who" : "qc-clouddb1:30001:1409913195:236929073:conn40:1485371859",
        "why" : "migrate-{ _id: \"824cb5db-6cbd-c90c-95ad-915bd7880c31\" }"
}
{
        "_id" : "user_data.device_info",
        "process" : "qc24:30005:1399282507:1672775172",
        "state" : 0,
        "ts" : ObjectId("5406f9e738d2c06115a4e475"),
        "when" : ISODate("2014-09-03T11:22:15.837Z"),
        "who" : "qc24:30005:1399282507:1672775172:conn1498556:2012981408",
        "why" : "migrate-{ _id: \"40acae65-45de-ce42-daf6-63fe1b0e8052\" }"
}
mongos>


可以看到有4個鎖的存在, "_id" : "configUpgrade" 顧名思義吧。"_id" : "balancer"是整個balancer過程的鎖。"_id" : "user_data.user_data"和"_id" : "user_data.device_info"對應具體的進行sharding的collection的balance鎖。

另一個是config.lockpings

mongos> db.lockpings.find().pretty()
{
        "_id" : "i-qikzt805:50000:1390191129:1804289383",
        "ping" : ISODate("2014-01-20T09:26:20.903Z")
}
{
        "_id" : "qc14:50000:1398961193:1804289383",
        "ping" : ISODate("2014-09-11T08:41:07.546Z")
}
{
        "_id" : "qc24:50000:1399171433:1804289383",
        "ping" : ISODate("2014-09-11T08:40:56.833Z")
}
{
        "_id" : "qc23:50000:1399172957:1804289383",
        "ping" : ISODate("2014-09-11T08:40:55.083Z")
}
{
        "_id" : "qc15:50000:1399173835:1804289383",
        "ping" : ISODate("2014-09-11T08:40:54.947Z")
}
{
        "_id" : "qc16:50000:1399174043:1804289383",
        "ping" : ISODate("2014-09-11T08:41:04.574Z")
}
{
        "_id" : "qc24:30005:1399282507:1672775172",
        "ping" : ISODate("2014-09-05T08:50:18.879Z")
}
{
        "_id" : "qc-clouddb6:50000:1409730027:1804289383",
        "ping" : ISODate("2014-09-11T08:40:54.966Z")
}
{
        "_id" : "qc-clouddb7:50000:1409730657:1804289383",
        "ping" : ISODate("2014-09-11T08:40:54.868Z")
}
{
        "_id" : "qc-clouddb8:50000:1409730659:1804289383",
        "ping" : ISODate("2014-09-11T08:40:56.802Z")
}
{
        "_id" : "qc-clouddb8:30008:1409813212:1448386028",
        "ping" : ISODate("2014-09-11T08:40:54.989Z")
}
{
        "_id" : "qc-clouddb1:30001:1409913195:236929073",
        "ping" : ISODate("2014-09-11T08:40:54.947Z")
}
{
        "_id" : "qc-clouddb3:30003:1409918540:1296167705",
        "ping" : ISODate("2014-09-11T08:40:55.232Z")
}
{
        "_id" : "qc-clouddb7:30007:1409919636:1928209546",
        "ping" : ISODate("2014-09-11T08:40:54.762Z")
}
{
        "_id" : "qc-clouddb2:30002:1409919744:42373342",
        "ping" : ISODate("2014-09-11T08:40:52.771Z")
}
{
        "_id" : "qc-clouddb6:30006:1409920835:1027944352",
        "ping" : ISODate("2014-09-11T08:40:54.932Z")
}
{
        "_id" : "qc-clouddb5:30005:1409920983:1186461301",
        "ping" : ISODate("2014-09-11T08:40:54.949Z")
}
{
        "_id" : "qc-clouddb4:30004:1409921811:221589655",
        "ping" : ISODate("2014-09-11T08:40:55.083Z")
}
i-qikzt805:50000:1390191129:1804289383 是進程id, 由hostname:port:timestamp:random()組成。

集群中每個mongos和mongod會每隔一段時間修改自己進程id的ping時間。相當于保持heartbeat。


lockping會單獨起一個線程,每隔一段時間去更新config.lockpings。

LockPinger線程

LockPinger線程只是做3件事:

1 更新進程在config.lockpings中的時間。

2 檢查config.locks中所有鎖定時間超過4天的鎖,如果存在則釋放掉。

3 清除掉本進程之前沒有成功釋放掉的鎖。(待解鎖列表中的所有的鎖)

獲取鎖的步驟

獲取鎖的代碼主要在這個函數DistributedLock::lock_try

1 從locks collection中查詢鎖是否存在(_id==鎖名稱)如果不存在,則可以獲得鎖.

2 如果鎖存在, 分4種情況討論:

    A. 可重入,超時

    B. 可重入,不超時

    C. 不可重入, 超時

    D. 不可重入,不超時

 B可以獲得鎖, A/C/D都不可以獲得鎖

另外還涉及一個是否釋放鎖的問題。C情況下會釋放鎖, 其他情況下不會。而為什么C釋放,而A不釋放的原因我沒有想明白。誰可以指點一下?

另外在獲取鎖的時候會分成兩步提交: 第一步先把狀態設置成1, 如果成功則再將狀態設置成2。
   

鎖超時的判斷條件

鎖的擁有者進程的上一次ping抵達的時間已經超過15分鐘

解鎖的步驟

解鎖步驟比較簡單:

1 查詢鎖是否存在

2 如果存在將狀態改成2

3 失敗后重試n次, 如果不成功則放在待解鎖列表中


來自:http://my.oschina.net/costaxu/blog/312551


 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!