背景
使用@SchedulerLock可以實現比較輕量級的簡單的定時任務,也可以實現分佈式鎖,那具體的原理是啥?
一探究竟
不熟悉的同學可以先看一下用法:
https://www.baeldung.com/shedlock-spring
大概就是定義一個數據表
注意這裏的name是primary key,是不可重複的。利用了primary key的這個特性,實現了鎖的搶佔。細節下面會介紹
然後加註解實現:
看源碼
直接從註解出發,看源碼
在註解所在的包內發現一個DefaultLockManager
一看有executeWithTask,應該就是這個代碼了
一直往裏看
doLock
insertRecord,看jdbc的實現
獲取鎖邏輯
其實很簡單,就是一個sql語句
INSERT INTO tableName (name, lock_until, locked_at, locked_by) VALUES(鎖名字, 當前時間+最多鎖多久, 當前時間, 主機名)
如果插入成功了,就說明搶到了鎖。否則認爲搶不到。
如果記錄已經存在了,就用更新記錄來搶鎖
UPDATE tableName SET lock_until = 當前時間+最多鎖多久, locked_at = 當前時間, locked_by = 主機名 WHERE name = 鎖名字 AND lock_until <= 當前時間
找到同一個name的鎖,如果發現lock_util小於等於當前時間(鎖已經無效),就可以去update;多個機器同時update,只有一個機器可以update成功(修改行數>0),實現了鎖的搶佔。
釋放鎖邏輯
注:lockTime = 最小鎖時間 和 當前時間的較大值
UPDATE tableName SET lock_until = lockTime WHERE name = 鎖名字
假設最小鎖時間30s,當前時間只過了15s,那麼就會最小鎖時間;否則用當前時間。
這個就是爲了保證最少也要鎖 最小鎖時間 (30s)
總結
SchedulerLock利用mysql實現了分佈式鎖。
搶鎖:
通過插入同一個name(primary key),或者更新同一個name來搶
釋放鎖:
通過設置lock_until來實現釋放,再次搶鎖的時候需要通過lock_util來判斷鎖失效了沒。