<meta charset="utf-8">
66_數(shù)據(jù)建模實戰(zhàn)_基于共享鎖和排他鎖實現(xiàn)悲觀鎖并發(fā)控制
1、共享鎖和排他鎖的說明
共享鎖:這份數(shù)據(jù)是共享的,然后多個線程過來,都可以獲取同一個數(shù)據(jù)的共享鎖,然后對這個數(shù)據(jù)執(zhí)行讀操作
排他鎖:是排他的操作,只能一個線程獲取排他鎖,然后執(zhí)行增刪改操作
讀寫鎖的分離
如果只是要讀取數(shù)據(jù)的話,那么任意個線程都可以同時進來然后讀取數(shù)據(jù),每個線程都可以上一個共享鎖
但是這個時候,如果有線程要過來修改數(shù)據(jù),那么會嘗試上排他鎖,排他鎖會跟共享鎖互斥,也就是說,如果有人已經(jīng)上了共享鎖了,那么排他鎖就不能上,就得等
如果有人在讀數(shù)據(jù),就不允許別人來修改數(shù)據(jù)
反之,也是一樣的
如果有人在修改數(shù)據(jù),就是加了排他鎖
那么其他線程過來要修改數(shù)據(jù),也會嘗試加排他鎖,此時會失敗,鎖沖突,必須等待,同時只能有一個線程修改數(shù)據(jù)
如果有人過來同時要讀取數(shù)據(jù),那么會嘗試加共享鎖,此時會失敗,因為共享鎖和排他鎖是沖突的
如果有在修改數(shù)據(jù),就不允許別人來修改數(shù)據(jù),也不允許別人來讀取數(shù)據(jù)
2、共享鎖和排他鎖的實驗
第一步:有人在讀數(shù)據(jù),其他人也能過來讀數(shù)據(jù)
創(chuàng)建一個groovy文件,寫入如下script語法,即排他鎖報錯,共享鎖+1
judge-lock-2.groovy: if (ctx._source.lock_type == 'exclusive') { assert false }; ctx._source.lock_count++

上一個共享鎖
POST /fs/lock/1/_update { "upsert": { "lock_type": "shared", "lock_count": 1 }, "script": { "lang": "groovy", "file": "judge-lock-2" } }
繼續(xù)加一個共享鎖,發(fā)現(xiàn)是可以讀的
POST /fs/lock/1/_update { "upsert": { "lock_type": "shared", "lock_count": 1 }, "script": { "lang": "groovy", "file": "judge-lock-2" } }
獲取鎖信息
GET /fs/lock/1
{
"_index": "fs",
"_type": "lock",
"_id": "1",
"_version": 3,
"found": true,
"_source": {
"lock_type": "shared",
"lock_count": 3
}
}
就給大家模擬了,有人上了共享鎖,你還是要上共享鎖,直接上就行了,沒問題,只是lock_count加1
第二步:已經(jīng)有人上了共享鎖,然后有人要上排他鎖
PUT /fs/lock/1/_create { "lock_type": "exclusive" }
排他鎖用的不是upsert語法,create語法,要求lock必須不能存在,直接自己是第一個上鎖的人,上的是排他鎖
{
"error": {
"root_cause": [
{
"type": "version_conflict_engine_exception",
"reason": "[lock][1]: version conflict, document already exists (current version [3])",
"index_uuid": "IYbj0OLGQHmMUpLfbhD4Hw",
"shard": "3",
"index": "fs"
}
],
"type": "version_conflict_engine_exception",
"reason": "[lock][1]: version conflict, document already exists (current version [3])",
"index_uuid": "IYbj0OLGQHmMUpLfbhD4Hw",
"shard": "3",
"index": "fs"
},
"status": 409
}
如果已經(jīng)有人上了共享鎖,明顯/fs/lock/1是存在的,create語法去上排他鎖,肯定會報錯
第三步: 對共享鎖進行解鎖
創(chuàng)建解鎖groovy文件


解鎖語法:
POST /fs/lock/1/_update { "script": { "lang": "groovy", "file": "unlock-shared" } }
連續(xù)解鎖3次,此時共享鎖就徹底沒了
每次解鎖一個共享鎖,就對lock_count先減1,如果減了1之后,是0,那么說明所有的共享鎖都解鎖完了,此時就就將/fs/lock/1刪除,就徹底解鎖所有的共享鎖
第四步: 上排他鎖,再上排他鎖
PUT /fs/lock/1/_create { "lock_type": "exclusive" }
其他線程
PUT /fs/lock/1/_create { "lock_type": "exclusive" }
肯定產(chǎn)生沖突,報錯
{
"error": {
"root_cause": [
{
"type": "version_conflict_engine_exception",
"reason": "[lock][1]: version conflict, document already exists (current version [7])",
"index_uuid": "IYbj0OLGQHmMUpLfbhD4Hw",
"shard": "3",
"index": "fs"
}
],
"type": "version_conflict_engine_exception",
"reason": "[lock][1]: version conflict, document already exists (current version [7])",
"index_uuid": "IYbj0OLGQHmMUpLfbhD4Hw",
"shard": "3",
"index": "fs"
},
"status": 409
}
第五步:、上排他鎖,上共享鎖
排他鎖之后再上共享鎖,報錯
POST /fs/lock/1/_update { "upsert": { "lock_type": "shared", "lock_count": 1 }, "script": { "lang": "groovy", "file": "judge-lock-2" } }
報錯信息如下:
{
"error": {
"root_cause": [
{
"type": "remote_transport_exception",
"reason": "[4onsTYV][127.0.0.1:9300][indices:data/write/update[s]]"
}
],
"type": "illegal_argument_exception",
"reason": "failed to execute script",
"caused_by": {
"type": "script_exception",
"reason": "error evaluating judge-lock-2",
"caused_by": {
"type": "power_assertion_error",
"reason": "assert false\n"
},
"script_stack": [],
"script": "",
"lang": "groovy"
}
},
"status": 400
}
第六步:解鎖排他鎖
DELETE /fs/lock/1