redis(千帆竞发--分布式锁)
比如一個操作要修改的用戶的狀態,修改狀態需要先讀出用戶的狀態,在內存里進行修改,改完了再存回去。
如果這樣的操作同時進行了,就會出現并發問題,因為讀取和保存狀態這兩個操作不是原子的。
分布式鎖:
分布式鎖本質上要實現的目標就是在Redis里面占“坑”,當別的進程也要來占時,發現已經有人蹲在那里了,就
只好放棄或者稍后再試。
占坑一般是使用setnx(set if not exists)指令,只允許被一個客戶端占坑。先來先到,用完了在調用del指令釋放。
>setnx lock:codehole true OK ...do something critical... >del lock:codehole (integer) 1但有個問題,如果邏輯執行到中間出現異常,可能會導致del指令沒有被調用,這樣就會陷入死鎖,鎖就得不到釋放了。
這樣我們可以拿到鎖之后,再給鎖加上一個過期時間,for example:5s
>setnx lock:codehole true OK >expire lock:codehole 5 ...do something critical... >del lock:codehole (integer) 1如果在setnx和expire之間服務器進程突然掛掉的,就會導致expire得不到執行,也會造成死鎖。這個問題根源在于setnx和expire是兩條指令而不是原子指令。如果這兩條指令可以一起執行就不會出現問題。
>set lock:codehole true ex 5 nx OK ...do something critical... >del lock:codehole上面這個指令就是setnx和expire組合一起的原子指令,它就是分布式鎖奧義的所在。
?
?
超時問題:
Redis的分布式鎖解決超時問題,如果在加鎖和釋放鎖之間的邏輯執行的太長,以至于超出了鎖的超時限制,就會出現問題。因為
這時候第一個線程持有的鎖過期了,臨界區的邏輯還沒有執行完,這個時候第二個線程就提前重新持有了這把鎖,導致臨界區代碼不能得到嚴格的串行執行。
lua 腳本if redis.call("get",keys[1] == argv[1] then return redis.call("del",keys[1]))else return 0 end可重入性:
可重入性是指線程在持有鎖的情況下再次請求加鎖,如果一個鎖支持同一個線程的多次加鎖,那么這個鎖就是可重入的。
public class RedisWithReentrantLock{private ThreadLocal<Map<String,Integer>> lockers=new ThreadLocal<>();private Jedis jedis;public RedisWithReentrantLock(Jedis jedis){this.jedis=jedis;}private boolean lock(String key){return jedis.set(key,"","nx","ex",5L)}private void unlock(){jedis.del(key);}private Map<String,Integer> currentLockers(){Map<String,Integer> refs=lockers.get();if(refs!=null){return refs;}lockers.set(new HashMap<>());return lockers.get();}public boolean lock(String key){Map<String,Integer> refs=currentLockers();Integer refCnt=refs.get(key);if(refCnt!=null){refs.put(key,refCnt+1);return true;}boolean ok=this.lock(key);if(!ok){return false;}refs.put(key,1);return true; }public boolean unlock(String key){Map<String,Integer> refs=currentLockers(); Integer refCnt=refs.get(key);if(refCnt ==null){return false;}refCnt-=1;if(refCnt>0){refs.put(key,refCnt);}else{refs.remove(key);this.unlock(key);}return true;} public static void main(String[] args){Jedis jedis=new Jedis();RedisWithReentrantLock redis=new RedisWithReentrantLock(jedis);System.out.println(redis.lock("codehole"));System.out.println(redis.lock("codehole"));System.out.println(redis.unlock("codehole"));System.out.println(redis.unlock("codehole"));} }
?
總結
以上是生活随笔為你收集整理的redis(千帆竞发--分布式锁)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Epicor客制化 - 在VS中进行开发
- 下一篇: 支付常识