千帆竞发 —— 分布式锁
一個操作要修改用戶的狀態,修改狀態需要先讀出用戶的狀態,在內存里進行修改,改完了再存回去。如果這樣的操作同時進行了,就會出現并發問題,因為讀取和保存狀態這兩個操作不是原子的。這個時候就要使用到分布式鎖來限制程序的并發執行。
同時操作一個context,存在并發問題分布式鎖
一般是使用 setnx(set if not exists) 指令占坑, 用完再調用 del 指令釋放茅坑。如果邏輯執行到中間出現異常了,可能會導致 del 指令沒有被調用,這樣就會陷入死鎖,鎖永遠得不到釋放。于是我們在拿到鎖之后,再給鎖加上一個過期時間,比如 5s,這樣即使中間出現異常也可以保證 5 秒之后鎖會自動釋放。
/*** @Auther: majx2* @Date: 2019-3-21 16:02* @Description:*/ public class DistributedLockTest {Jedis jedis = RedisDS.create().getJedis();final static String KEY = "KEY";@Testpublic void testLock() throws InterruptedException {new Thread(new Runnable() {@Overridepublic void run() {Assert.assertTrue(exec());}}).start();Thread.sleep(1000);Assert.assertFalse(exec());Thread.sleep(3000);}public boolean exec(){return new RedLock().trylock(KEY, new LockWrap() {@Overridepublic boolean invoke() {try {Thread.sleep(2000);} catch (InterruptedException e) {}return true;}});}public class RedLock{public boolean trylock(String key,LockWrap wrap){Long result = jedis.setnx(key, KEY);// 占坑if(result == 1L){ jedis.expire(key,5000); // 避免沒有刪除boolean invoke = wrap.invoke();jedis.del(key);return invoke;}return false;}}public interface LockWrap{boolean invoke();} }但是以上邏輯還有問題。如果在 setnx 和 expire 之間服務器進程突然掛掉了,可能是因為機器掉電或者是被人為殺掉的,就會導致 expire 得不到執行,也會造成死鎖。這種問題的根源就在于 setnx 和 expire 是兩條指令而不是原子指令。 解決這些問題,可以使用開源分布式組建redission。
超時問題
Redis 的分布式鎖不能解決超時問題,如果在加鎖和釋放鎖之間的邏輯執行的太長,以至于超出了鎖的超時限制,就會出現問題。因為這時候鎖過期了,第二個線程重新持有了這把鎖,第二個線程就會在第一個線程邏輯執行完之間拿到了鎖;緊接著第一個線程執行完了業務邏輯,就把鎖給釋放了,第三個線程就會在第二個線程邏輯執行完之間拿到了鎖。為了避免這個問題,Redis 分布式鎖不要用于較長時間的任務。如果真的偶爾出現了,數據出現的小波錯亂可能需要人工介入解決。
集群問題
在 Sentinel 集群中,主節點掛掉時,從節點會取而代之,客戶端上卻并沒有明顯感知。原先第一個客戶端在主節點中申請成功了一把鎖,但是這把鎖還沒有來得及同步到從節點,主節點突然掛掉了。然后從節點變成了主節點,這個新的節點內部沒有這個鎖,所以當另一個客戶端過來請求加鎖時,立即就批準了。這樣就會導致系統中同樣一把鎖被兩個客戶端同時持有,不安全性由此產生。
集群環境下,分布式鎖存在問題如果你很在乎高可用性,希望掛了一臺 redis 完全不受影響,那就應該考慮 redlock算法。不過代價也是有的,需要更多的 redis 實例,性能也下降了。
注: Redlock算法,需要提供多個 Redis 實例,這些實例之前相互獨立沒有主從關系。加鎖時,它會向過半節點發送 set(key, value, nx=True, ex=xxx) 指令,只要過半節點 set 成功,那就認為加鎖成功。釋放鎖時,需要向所有節點發送 del 指令。不過, Redlock 算法還需要考慮出錯重試、時鐘漂移等很多細節問題,同時因為 Redlock 需要向多個節點進行讀寫,意味著相比單實例 Redis 性能會下降一些。
本文基于《Redis深度歷險:核心原理和應用實踐》一文的JAVA實踐。更多文章請參考:高性能緩存中間件Redis應用實戰(JAVA)
轉載于:https://my.oschina.net/u/1404949/blog/3039545
總結
以上是生活随笔為你收集整理的千帆竞发 —— 分布式锁的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 软件工程导论——软件工程介绍
- 下一篇: Epicor 设置下拉菜单Epicomb