Redis分布式锁实现方式
分布式鎖一般有三種實現方式
1. 數據庫樂觀鎖;
2.?基于ZooKeeper的分布式鎖;
3. 基于Redis的分布式鎖;
?
三種方式的優缺點分析
1、數據庫樂觀鎖:
優點是實現簡單,只需要for update關鍵詞就可以實現,缺點是無法滿足高并發量以及數據庫讀寫頻繁的系統;
2、ZooKeeper分布式鎖:
無論是從性能以及實現的功能來說都是非常優秀,只是在開發起來需要一定的基礎,對新手可能不是很友好;
3、Redis實現分布式鎖
優點是開發相對簡單,能滿足一定并發量的系統,缺點是存在線程爭搶鎖的問題,當并發量到達一定級別,多個線程去爭搶同一個鎖,對性能的影響較大;
?
事務以及原子性
雖然Redis是單線程運行,但是在分布式的情況下對同一資源進行操作還是會出現問題,下圖是一個簡單的例子
所以一定要保證tomcat1以及tomcat2讀寫的原子性,既讀與寫要么都執行,要么都不執行。關于事務的原子性可以查詢這里
那么如何保證呢,redis在2.6中加入了lua腳本功能可以輕松的解決這個問題,下面是一個簡單的例子實現了上述的加100操作
Jedis jedis = jedisPool.getResource(); String script = "local a = redis.call('get', KEYS[0]) a = a + 100 redis.call('set', a)";jedis.eval(script, 1, rname+"Lock",RedisCacheFactory.FactoryUUID,"1000");分布式鎖的具體實現
先說下思路:首先加鎖的方式是向redis里存入一個KEY-VALUE,KEY存入的加鎖對象可以是方法、類、數據等等,VALUE存入持有鎖的節點(例如tomcat1)
大概整理了一下幾個問題:
問題:為什么VALUE存入持有鎖的節點?
D:為的是防止A加的鎖被B給解除,保證只有持有鎖的節點才能解鎖;
問題:怎么存入持有鎖的節點?
D:這里只是我的思路是在tomcat啟的時候生成一個uuid作為該tomcat的token存入到VALUE中;
問題:怎么防止死鎖?
D:利用Redis設置鍵的過期時間
?
下面貼出部分代碼,僅供參考
加鎖
JedisPool jedisPool = new JedisPool(new JedisPoolConfig(),RedisInstance.hostName,Integer.parseInt(RedisInstance.port),5000,password);
Jedis jedis = jedisPool.getResource(); // key1 : key值 argv1 :value值 argv2 :
過期時間 String script = "if redis.call('EXISTS',KEYS[1]) ==0 then redis.call('set',KEYS[1],ARGV[1]) redis.call('EXPIRE',KEYS[1],ARGV[2]) return 1 else return 0 end";
long result = (long) jedis.eval(script, 1, rname+"Lock",RedisCacheFactory.FactoryUUID,"1000");
jedis.close();
jedisPool.close();
?
解鎖
JedisPool jedisPool = new JedisPool(new JedisPoolConfig(),RedisInstance.hostName,Integer.parseInt(RedisInstance.port),5000,password);
Jedis jedis = jedisPool.getResource();
String script = "if redis.call('EXISTS',KEYS[1]) ==1 and redis.call('GET',KEYS[1])==ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
long result = (long) jedis.eval(script, 1, rname+"Lock",RedisCacheFactory.FactoryUUID);
jedis.close(); jedisPool.close();
?
?到這里就寫完了,有問題,大家可以一起交流下;
?
?
總結
以上是生活随笔為你收集整理的Redis分布式锁实现方式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JS同步和异步问题
- 下一篇: MySQL之性能优化解说