利用redis實現分佈式鎖

分佈式鎖通常有三種實現方式:

1. 數據庫樂觀鎖;redis

2. 基於ZooKeeper的分佈式鎖;數據庫

3. 基於Redis的分佈式鎖;緩存

這裏大概說一下三種方式的優缺點,數據庫樂觀鎖優勢是實現簡單,只須要for update關鍵詞就能夠實現,缺點是沒法知足高併發量以及數據庫讀寫頻繁的系統tomcat

ZooKeeper分佈式鎖不管是從性能以及實現的功能來講都是很是優秀,只是在開發起來須要必定的基礎,對新手可能不是很友好併發

而本文主要講第三種利用redis實現分佈式鎖,優勢是開發相對簡單,能知足必定併發量的系統,缺點是存在線程爭搶鎖的問題,當併發量到達必定級別,多個線程去爭搶同一個鎖,對性能的影響較大分佈式


事務以及原子性

雖然Redis是單線程運行,可是在分佈式的狀況下對同一資源進行操做仍是會出現問題,下圖是一個簡單的例子高併發

因此必定要保證tomcat1以及tomcat2讀寫的原子性,既讀與寫要麼都執行,要麼都不執行。關於事務的原子性能夠查詢這裏性能

那麼如何保證呢,redis在2.6中加入了lua腳本功能能夠輕鬆的解決這個問題,下面是一個簡單的例子實現了上述的加100操做ui

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)lua

大概整理了一下幾個問題:

Q:爲何VALUE存入持有鎖的節點

A:爲的是防止A加的鎖被B給解除,保證只有持有鎖的節點才能解鎖

Q:怎麼存入持有鎖的節點

A:這裏只是個人思路是在tomcat啓的時候生成一個uuid做爲該tomcat的token存入到VALUE中

 

Q:怎麼防止死鎖

A:利用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();

 以上僅我的意見,若有錯誤的地方,還請各位海涵。

後面可能會整合此次緩存改造的全部環節發出來給你們參考 一下

補充說明一下:lua操做redis時若是操做多個key不在同一節點下會出錯,緣由是由於Cluster會將數據自動分佈到不一樣的節點(虛擬的16384個slot,具體看這裏)。

解決辦法 後面會貼出詳細教程

相關文章
相關標籤/搜索