分布式锁的几种实现原理
分布式鎖主流有三種模式:
實現方式 功能要求 實現難度 學習成本 運維成本
MySQL 的方案借助表鎖/行鎖實現 滿足基本要求 不難 熟悉 小量OK、大量影響現有業務、1主多從架構,不方便擴容
通過 ZK 創建數據節點的方式實現 滿足要求 熟悉 ZK API 即可 需要學習 重,需要堆機器,有跨機房請求
Redis 使用 setnxex 基本要求 不難 熟悉 擴容方便、現有服務
MySQL 單主架構,寫都會到 master,有瓶頸。ZK 的方式需要自己搭建、運維,而且需要堆機器,利用率不高。最終采用了 Redis 來實現,流量/存儲都可以擴容,運維也不需要自己。
一、基于Mysql實現分布式鎖 (樂觀鎖)
Mysql實現分布式鎖 主要是基于數據庫的排他鎖(也叫行級排他鎖), 采用樂觀鎖的方式去做。
我們可以通過一個update語句是否成功來判斷線程搶占鎖是否成功,比如如下sql語句:
爭搶鎖的sql語句:
update t_schedule_cluster set execute = 1 version = ?, execute_ip = ?, update_time = ? where task_name = ? and version = ?
實現原理入下圖:
但是數據庫的性能有限,如果在高并發的情況下會頻發的訪問數據庫,對數據庫會造成較大的壓力。
二,基于redis的分布式鎖實現
基于Redis實現的分布式鎖其實很簡單,底層就是使用redis的setnx指令來實現的加鎖,我們來看看官方對setnx的定義:
SETNX key value
將 key 的值設為 value ,當且僅當 key 不存在。
若給定的 key 已經存在,則 SETNX 不做任何動作。
SETNX 是『SET if Not eXists』(如果不存在,則 SET)的簡寫。
返回值:
設置成功,返回 1 。
設置失敗,返回 0 。
以上內容來自于:http://redisdoc.com/string/setnx.html
既然setnx這么強大,那么我們是不是可以高枕無憂直接使用了? 當然了,我們還要考慮一些極端場景。
2.1 死鎖問題
既然設置了value值,那么我們肯定會想到過期時間,那么就需要再使用setnx指令后繼續使用expire指令。但是這兩部操作必定不是原子性的,如果執行expire失敗怎么辦?
其實Redis官方也考慮到了這個問題,在Redis2.8 之后,官方執行setnx 和 expire命令一起使用了。如下:
SET lock_key lock_value NX PX 30000
其中:
1.lock_key:即鎖名稱,這個名稱應是公開的,在分布式環境中,對于某一確定的公共資源,所有爭用方(客戶端)都應該知道對應鎖的名字。對于 Redis 而言,lock_name 就是 key-value 中的 key,具有唯一性。
具體操作如下圖:
2.2 鎖自動過期存在的隱患
例如我們有兩個線程A、B,此時線程A搶到了鎖,且設置自動過期時間為10s鐘,因為系統其他原因導致系統A發生阻塞。而此刻10s鐘后鎖自動過期,線程C獲取到了同一個資源的鎖,線程A從阻塞中恢復,認為自己仍然持有鎖,繼續操作同一資源。這樣就使得加鎖的互斥性失效了。
解決方案:
我們在上面set lock_key lock_value 時講過,lock_value是一個隨機生成的字符串,在每次獲取鎖的時候都會重新生成。那么我們在執行真正的業務邏輯(類似于和db進行交互的操作,同一時刻只能一個線程操作的情況)時,判斷當前生成的隨機字符串和lock_value是否一致,如果不一致則說明redis中的lock_value被修改過,也就說明此刻鎖已經被其他線程所占有。
具體操作流程如下圖:
主要使用的就是這兩種方案,在這里只是做個簡單總結,其實還有其他一些可以實現分布式鎖,根據自己項目本身情況選擇最合適的。
另外 已經Redis也有開源的框架可以很好地支持基于Redis的分布式鎖,這里推薦一個:Redission https://github.com/redisson/redisson
PS:2019 繼續努力加油學習更多知識,讓自己在技術這條道路上越走越遠!
轉載于:https://www.cnblogs.com/wang-meng/p/10226618.html
總結
以上是生活随笔為你收集整理的分布式锁的几种实现原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Xv6 Page Table
- 下一篇: 编程软件python是什么意思_程序员P