利用關係型數據庫特性:實現排它鎖(insert唯一約束)和樂觀鎖(update version一致性)。
排他鎖
表結構
獲取鎖
INSERT INTO method_lock (method_name, desc) VALUES ('methodName', 'methodName');
對method_name做了唯一性約束,這裏如果有多個請求同時提交到數據庫的話,數據庫會保證只有一個操作可以成功。
樂觀鎖
表結構
DROP TABLE IF EXISTS `method_lock`;
CREATE TABLE `method_lock` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`method_name` varchar(64) NOT NULL COMMENT '鎖定的方法名',
`state` tinyint NOT NULL COMMENT '1:未分配;2:已分配',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`version` int NOT NULL COMMENT '版本號',
`PRIMARY KEY (`id`),
UNIQUE KEY `uidx_method_name` (`method_name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='鎖定中的方法';
先獲取鎖的信息
select id, method_name, state,version from method_lock where state=1 and method_name='methodName';
佔有鎖
update t_resoure set state=2, version=2, update_time=now() where method_name='methodName' and state=1 and version=2;
如果沒有更新影響到一行數據,則說明這個資源已經被別人佔位了。
缺點:
1、這把鎖強依賴數據庫的可用性,數據庫是一個單點,一旦數據庫掛掉,會導致業務系統不可用。
2、這把鎖沒有失效時間,一旦解鎖操作失敗,就會導致鎖記錄一直在數據庫中,其他線程無法再獲得到鎖。
3、這把鎖只能是非阻塞的,因爲數據的insert操作,一旦插入失敗就會直接報錯。沒有獲得鎖的線程並不會進入排隊隊列,要想再次獲得鎖就要再次觸發獲得鎖操作。
4、這把鎖是非重入的,同一個線程在沒有釋放鎖之前無法再次獲得該鎖。因爲數據中數據已經存在了。
解決方案:
1、數據庫是單點?搞兩個數據庫,數據之前雙向同步。一旦掛掉快速切換到備庫上。
2、沒有失效時間?只要做一個定時任務,每隔一定時間把數據庫中的超時數據清理一遍。
3、非阻塞的?搞一個while循環,直到insert成功再返回成功。
4、非重入的?在數據庫表中加個字段,記錄當前獲得鎖的機器的主機信息和線程信息,那麼下次再獲取鎖的時候先查詢數據庫,如果當前機器的主機信息和線程信息在數據庫可以查到的話,直接把鎖分配給他就可以了。
————————————————
版權聲明:本文爲CSDN博主「1Vincent」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/wuzhiwei549/article/details/80692278