日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

基于数据库的分布式锁实现

發布時間:2024/4/14 数据库 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于数据库的分布式锁实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、基于數據庫表

要實現分布式鎖,最簡單的方式可能就是直接創建一張鎖表,然后通過操作該表中的數據來實現了。當我們要鎖住某個方法或資源的時候,我們就在該表中增加一條記錄,想要釋放鎖的時候就刪除這條記錄。

創建這樣一張數據庫表:

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 NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE 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、非重入?可以在數據庫表中加一個字段,記錄當前獲得鎖的機器的主機信息和線程信息,那么下次再獲取鎖的時候先查詢數據庫,如果當前機器的主機信息和線程信息在數據庫中可以查到的話,就直接把鎖分配給它即可。

二、基于數據庫表做樂觀鎖

常用的方法是為數據增加一個版本標識,具體實現請參考http://blog.csdn.net/lmb55/article/details/78266667

三、基于數據庫表做悲觀鎖(排它鎖)

除了可以通過增刪操作數據庫表中的記錄以外,還可以借助數據庫中自帶的鎖來實現分布式鎖。

我們還用上面創建的數據庫表,可以通過數據庫的排它鎖來實現分布式鎖。基于MySQL的InnoDB引擎,可以使用以下方法來實現加鎖操作

public boolean lock(){Connection.setAutoCommit(false);while (true) {try {result = select * from MethodLock where methodName = 'xxxx' for update;if (result == null) {return false;}} catch (Exception e) {}sleep(1000);}returnType false;}

在查詢語句后面增加for update,數據庫會在查詢過程中給數據庫表增加排他鎖(這里再多提一句,InnoDB引擎在加鎖的時候,只有通過索引進行檢索的時候才會使用行級鎖,否則會使用表級鎖。這里我們希望使用行級鎖,就要給method_name添加索引,值得注意的是,這個索引一定要創建成唯一索引,否則會出現多個重載方法之間無法同時被訪問的問題。重載方法的話建議把參數類型也加上)。當某條記錄被加上排他鎖之后,其他線程無法再在該行記錄上增加排他鎖。

我們可以認為獲得排它鎖的線程即可獲得分布式鎖,當獲取到鎖之后,可以執行方法的業務邏輯,執行完方法之后,再通過以下方法解鎖:

public void unlock(){connection.commit(); }

通過connection.commit()操作來釋放鎖。

這里還可能存在另外一個問題,雖然我們對method_name 使用了唯一索引,并且顯示使用for update來使用行級鎖。但是,MySql會對查詢進行優化,即便在條件中使用了索引字段,但是否使用索引來檢索數據是由 MySQL 通過判斷不同執行計劃的代價來決定的,如果 MySQL 認為全表掃效率更高,比如對一些很小的表,它就不會使用索引,這種情況下 InnoDB 將使用表鎖,而不是行鎖。如果發生這種情況就悲劇了。

還有一個問題,就是我們要使用排他鎖來進行分布式鎖的lock,那么一個排他鎖長時間不提交,就會占用數據庫連接。一旦類似的連接變得多了,就可能把數據庫連接池撐爆。

這種方法可以有效的解決上面提到的無法釋放鎖和阻塞鎖的問題

1、阻塞鎖?for update語句會在執行成功后立即返回,在執行失敗時一直處于阻塞狀態,直到成功。
2、鎖定之后服務宕機,無法釋放?使用這種方式,服務宕機之后數據庫會自己把鎖釋放掉。

但是還是無法解決數據庫單點和可重入的問題

優點:直接借助數據庫容易理解;

缺點:
1、會有各種各樣的問題,在解決問題的過程中會使整個方案變的越來越復雜。
2、操作數據庫需要一定的開銷,性能問題需要考慮。

總結:
使用數據庫來實現分布式鎖,這三種方式都是依賴數據庫中的一張表,一種是通過表中的記錄存在情況確定當前是否有鎖存在,另外兩種是通過數據庫的樂觀鎖和排它鎖/悲觀鎖來實現分布式鎖。

總結

以上是生活随笔為你收集整理的基于数据库的分布式锁实现的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。