hi,已經忘記多久沒有寫博客,現在正式迴歸! 今天要分享的是分佈式鎖的原理與實現;在系統軟件開發過程中,很多時候我們會有一種這麼一種場景: 我們有很多需要同時處理的任務,而這些任務執行過程中都需要去訪問修改一個公共資源;如果每個線程都可以隨意的去修改資源,那我們的公共資源的最終結果可能和我們預期的會不一致(例如:扣庫存);此時我們就需要同一個時間,只能有一個線程訪問修改公共資源;要實現這個功能,我們就需要用到鎖;JDK 的JUC提供了很多相關的實現,如:synchronized,ReenreantLock等;但是,程序同時部署到多個服務器上時,我們就需要用到分佈式鎖。 針對分佈式鎖的實現,目前比較常用的有以下幾種方案:
- 1、數據庫實現分佈式鎖:原理簡單,性能較差
- 2、redis實現的分佈式鎖:性能最好
- 3、zookeeper實現的分佈式鎖:分佈式鎖:可靠性最好
一、基於數據庫表 方法1:通過創建單獨數據庫表biz_lock
CREATE TABLE `biz_lock` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`biz_name` varchar(64) NOT NULL DEFAULT '' COMMENT '鎖定的業務/資源',
`remarks` varchar(1024) NOT NULL DEFAULT '備註信息',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '保存數據時間,自動生成',
PRIMARY KEY (`id`),
UNIQUE KEY `uidx_biz_name` (`biz_name`) USING BTREE
)
ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='鎖定中的業務/資源';
使用方式:當我們要鎖住某個方法或資源的時候,我們就在該表中增加一條記錄,想要釋放鎖的時候就刪除這條記錄。
(1).獲取鎖
insert into biz_lock(biz_name,remarks) values ('resort_room_stock_operate','酒店房間庫存操作')
(2).釋放鎖
delete from methodLock where biz_name ='biz_name'
方法2:基於數據庫表做樂觀鎖
常用的方法是爲數據增加一個版本標識,每次修改記錄的時候需要比對版本號是否一致;
(1).查詢商品記錄下本次操作前的version
select (id,status,version) from t_goods_info where id=#{id}
(2).根據商品信息生成訂單
(3).修改商品status爲2
update t_goods_info
set status=2,version=version+1
where id=#{id} and version=#{version};
方法3:數據庫悲觀鎖 每次要操作前,先去數據庫獲取鎖,然後做操作完成提交
select * from t_goods_info where id = #{id} for update
二、redis實現的分佈式鎖(大概有三種方式)
(1).jredis自己實現
a.【setnx】+命令實現分佈式鎖(set if not exist)
b.lua腳本實現redis分佈式鎖
(2).Redisson實現
RedissonLock、RedissonRedLock
三、zookeeper
(1)、使用zookeeper API根據節點監聽和臨時有序節點功能實現
(2)、Apache Curator