標籤:io os ar for 檔案 資料 2014 art 問題
分布式鎖的作用
一般來講,鎖的作用是在於解決不同的執行流之間對於同一個資源的競爭而產生的問題。分布式鎖的作用就在於解決分布式程式中,分布在不同機器上的執行流對於資源的競爭問題。在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次, 如果不成功則放在待解鎖列表中
mongodb叢集的分布式鎖的實現研究