一、基於資料庫表
要實現分布式鎖,最簡單的方式可能就是直接建立一張鎖表,然後通過操作該表中的資料來實現了。當我們要鎖住某個方法或資源的時候,我們就在該表中增加一條記錄,想要釋放鎖的時候就刪除這條記錄。
建立這樣一張資料庫表:
create
table
`methodlock` (
`id`
int(11) not
null auto_increment comment '主鍵',
`method_name`
varchar(64) not
null
default
'' comment '鎖定的方法名',
`desc`
varchar(1024) not
null
default
'備註資訊',
`update_time`
timestamp
notnull
default
current_timestamp
onupdate
current_timestamp comment '儲存資料時間,自動生成',
primary
key (`id`),
unique
key`uidx_method_name` (`method_name `) using btree
) engine=innodb default charset=utf8 comment='鎖定中的方法';
當我們要鎖住某個方法時,執行以下sql:
insert
into methodlock(method_name,desc) values (『method_name』,『desc』)
因為我們對method_name做了唯一性約束,這裡如果有多個請求同時提交到資料庫的話,資料庫會保證只有乙個操作可以成功,那麼我們可以認為操作成功的那個執行緒獲得了該方法的鎖,可以執行具體內容。
當方法執行完畢之後,想要釋放鎖的話,需要執行以下sql:
delete
from methodlock where method_name ='method_name'
上面這種簡單的實現有以下幾個問題:
1、這把鎖依賴資料庫的可用性,資料庫是乙個單點,一旦資料庫掛掉,會導致業務系統不可用。
2、這把鎖沒有失效時間,一旦解決操作失敗,就會導致記錄一直在資料庫中,其他執行緒無法在獲得鎖。
3、這把鎖只能是非阻塞的,因為資料的insert操作,一旦插入失敗就會直接報錯。沒有獲得鎖的執行緒並不會進入排隊佇列,要想再次獲得鎖就要再次觸發獲得鎖的操作。
4、這把鎖是非重入的,同乙個執行緒在沒有釋放鎖之前無法再次獲得該鎖。因為資料庫表中資料已經存在了。
當然,我們也可以有其它方式解決上面的問題:
1、資料庫是單點?那就搞兩個資料庫,資料庫之前雙向同步,一旦掛掉快速切換到備庫上。
2、沒有失效時間?可以做乙個定時任務,每隔一定時間把資料庫中的超時資料清理一遍。
3、非阻塞?可以寫乙個while迴圈,直到insert成功再返回成功。
4、非重入?可以在資料庫表中加乙個字段,記錄當前獲得鎖的機器的主機資訊和執行緒資訊,那麼下次再獲取鎖的時候先查詢資料庫,如果當前機器的主機資訊和執行緒資訊在資料庫中可以查到的話,就直接把鎖分配給它即可。
二、基於資料庫表做樂觀鎖
常用的方法是為資料增加乙個版本標識,具體實現請參考
三、基於資料庫表做悲觀鎖(排它鎖)
除了可以通過增刪運算元據庫表中的記錄以外,還可以借助資料庫中自帶的鎖來實現分布式鎖。
我們還用上面建立的資料庫表,可以通過資料庫的排它鎖來實現分布式鎖。基於mysql的innodb引擎,可以使用以下方法來實現加鎖操作:
public boolean lock()
} catch (exception e)
sleep(1000);
}returntype false;
}
在查詢語句後面增加for update,資料庫會在查詢過程中給資料庫表增加排他鎖(這裡再多提一句,innodb引擎在加鎖的時候,只有通過索引進行檢索的時候才會使用行級鎖,否則會使用表級鎖。這裡我們希望使用行級鎖,就要給method_name新增索引,值得注意的是,這個索引一定要建立成唯一索引,否則會出現多個過載方法之間無法同時被訪問的問題。過載方法的話建議把引數型別也加上)。當某條記錄被加上排他鎖之後,其他執行緒無法再在該行記錄上增加排他鎖。
我們可以認為獲得排它鎖的執行緒即可獲得分布式鎖,當獲取到鎖之後,可以執行方法的業務邏輯,執行完方法之後,再通過以下方法解鎖:
public
void
unlock()
通過connection.commit()操作來釋放鎖。
這裡還可能存在另外乙個問題,雖然我們對method_name 使用了唯一索引,並且顯示使用for update來使用行級鎖。但是,mysql會對查詢進行優化,即便在條件中使用了索引字段,但是否使用索引來檢索資料是由 mysql 通過判斷不同執行計畫的代價來決定的,如果 mysql 認為全表掃效率更高,比如對一些很小的表,它就不會使用索引,這種情況下 innodb 將使用表鎖,而不是行鎖。如果發生這種情況就悲劇了。
還有乙個問題,就是我們要使用排他鎖來進行分布式鎖的lock,那麼乙個排他鎖長時間不提交,就會占用資料庫連線。一旦類似的連線變得多了,就可能把資料庫連線池撐爆。
這種方法可以有效的解決上面提到的無法釋放鎖和阻塞鎖的問題:
1、阻塞鎖?for update語句會在執行成功後立即返回,在執行失敗時一直處於阻塞狀態,直到成功。
2、鎖定之後服務宕機,無法釋放?使用這種方式,服務宕機之後資料庫會自己把鎖釋放掉。
但是還是無法解決資料庫單點和可重入的問題。
優點:直接借助資料庫容易理解;
缺點:
1、會有各種各樣的問題,在解決問題的過程中會使整個方案變的越來越複雜。
2、運算元據庫需要一定的開銷,效能問題需要考慮。
總結:
使用資料庫來實現分布式鎖,這三種方式都是依賴資料庫中的一張表,一種是通過表中的記錄存在情況確定當前是否有鎖存在,另外兩種是通過資料庫的樂觀鎖和排它鎖/悲觀鎖來實現分布式鎖。
基於資料庫實現分布式鎖
多個程序 多個執行緒訪問共同元件資料庫.通過selec.for update訪問同一條資料 for update鎖定資料,其他執行緒只能等待 此時只有乙個操作可以對資料進行修改,而其他人不能夠對該資料進行修改操作,但可以檢視 select from distribute lock where bus...
基於資料庫的分布式鎖實現
一 基於資料庫表 要實現分布式鎖,最簡單的方式可能就是直接建立一張鎖表,然後通過操作該表中的資料來實現了。當要鎖住某個方法或資源的時候,就在該表中增加一條記錄,想要釋放鎖的時候就刪除這條記錄。建立這樣一張資料庫表 create table methodlock id int 11 not null ...
基於資料庫的分布式鎖
使用場景 某大型 部署是分布式的,訂單系統有三颱伺服器響應使用者請求,生成訂單後統一存放到order info表 order info表要求訂單id order id 必須是唯一的,那麼三颱伺服器怎麼協同工作來確認order id的唯一性呢?這時候就要用到分布式鎖了。分布式鎖的要求 在了解了使用場景...