實現方式 功能要求 實現難度 學習成本 運維成本
MySQL 的方案藉助表鎖/行鎖實現 知足基本要求 不難 熟悉 小量OK、大量影響現有業務、1主多從架構,不方便擴容
經過 ZK 建立數據節點的方式實現 知足要求 熟悉 ZK API 便可 須要學習 重,須要堆機器,有跨機房請求
Redis 使用 setnxex 基本要求 不難 熟悉 擴容方便、現有服務html
MySQL 單主架構,寫都會到 master,有瓶頸。ZK 的方式須要本身搭建、運維,並且須要堆機器,利用率不高。最終採用了 Redis 來實現,流量/存儲均可以擴容,運維也不須要本身。java
Mysql實現分佈式鎖 主要是基於數據庫的排他鎖(也叫行級排他鎖), 採用樂觀鎖的方式去作。
咱們能夠經過一個update語句是否成功來判斷線程搶佔鎖是否成功,好比以下sql語句:mysql
CREATE TABLE `t_schedule_cluster` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '@cname:主鍵', `execute` int(1) NOT NULL COMMENT '@cname:執行狀態', `version` int(11) NOT NULL COMMENT '@cname:版本號 ', `task_name` varchar(128) NOT NULL COMMENT '@cname:任務名稱', `execute_ip` varchar(32) DEFAULT NULL COMMENT '@cname:執行ip ', `update_time` datetime DEFAULT NULL COMMENT '@cname:修改時間', PRIMARY KEY (`id`), KEY `Index_series_id` (`execute`) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 COMMENT='@cname:多機定時任務調度';
爭搶鎖的sql語句:
update t_schedule_cluster set execute = 1 version = ?, execute_ip = ?, update_time = ? where task_name = ? and version = ?
git
實現原理入下圖:
github
可是數據庫的性能有限,若是在高併發的狀況下會頻發的訪問數據庫,對數據庫會形成較大的壓力。redis
基於Redis實現的分佈式鎖其實很簡單,底層就是使用redis的setnx
指令來實現的加鎖,咱們來看看官方對setnx的定義:
SETNX key value
將 key 的值設爲 value ,當且僅當 key 不存在。
若給定的 key 已經存在,則 SETNX 不作任何動做。
SETNX 是『SET if Not eXists』(若是不存在,則 SET)的簡寫。
返回值:
設置成功,返回 1 。
設置失敗,返回 0 。sql
redis> EXISTS job # job 不存在 (integer) 0 redis> SETNX job "programmer" # job 設置成功 (integer) 1 redis> SETNX job "code-farmer" # 嘗試覆蓋 job ,失敗 (integer) 0 redis> GET job # 沒有被覆蓋 "programmer"
以上內容來自於:http://redisdoc.com/string/setnx.html數據庫
既然setnx這麼強大,那麼咱們是否是能夠高枕無憂直接使用了? 固然了,咱們還要考慮一些極端場景。架構
既然設置了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,具備惟一性。併發
例如咱們有兩個線程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 繼續努力加油學習更多知識,讓本身在技術這條道路上越走越遠!