日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

應用場景

分布式系統中,面對高并發場景,又對數據一致性有一定要求的情況下,使用分布式鎖。例如商城中下單扣庫存這種情況。

解決方案

基于數據庫

例如:

select * from mall_spu where id=111 for update

例如:專門建一張表用來實現。例如以類名、方法名、數據ID作為唯一主鍵,org.leo.mall.order.OrderServer.addOrder.skuId.111,方法執行的時候,如果能插入成功,代表拿到鎖,如果報主鍵沖突,則拿鎖失敗。


基于Zookeeper

以類、方法、數據ID作為目錄,請求取順序節點&節點列表,如果自己的節點最小,說明拿鎖成功。而且還可以通過watch,在鎖釋放的時候重新拿鎖。因為是臨時鎖,所以主動釋放,或者session失效都可以釋放鎖,避免死鎖產生。

性能差點,因為Zookeeper的操作都在主節點上。


基于redis

本文主要講講應用的一些變革。

1、加鎖。

原來的做法是:

public static boolean getLock(String key,int expireTime){
 Long result=RedisClient.setnx(key,"");
 if(result!=1){return false}
 RedisClient.expire(key,expireTime);
 return true;
}

setnx加鎖,成功后用expire加上超時時間。

問題在于:sennx和expire不是原子操作,萬一expire的時候崩了,這條命令永遠不過期了。

所以后來基于Redis的升級,有了下面正確的加鎖方法:

public static boolean getLock(String key,String requestId,int expireTime){
 String result=RedisClient.set(key,requestId,"NX","PK",expireTime);
 if(result.equals("OK")){return true;}
 return false;
}

其實就是用Redis提供的一條set命令,替代了前面的setnx、expire兩條命令,保證了原子性。

NX是指Key不存在就新增。PX是指設置超時時間。

requestId是為了后面解鎖用。

2、解鎖

解鎖看著最簡單,其實蠻復雜。

腦子里第一想法就是:

public static void releaseLock(String key){
 RedisClient.del(key);
}

這個危險性在于任何人都可以解鎖!比如A請求加了鎖:spu_id_111。B請求也要對111進行操作,一看鎖被占了,直接del,然后自己拿鎖——雖然在程序開發上講,沒有哪個傻子會這么干!!

所以這才有了第二種做法:

A請求加鎖的時候,通過UUID、Random等方法生成隨機數requestId。

public static void releaseLock(String key,String requestId){
  String result=RedisClient.get(key);//步驟1
  if(result.equals(requestId)){//步驟2
   //二者相等,說明加解鎖的請求是同一個
   RedisClient.del(key);//步驟3
 }
}

看似很嚴謹,但是問題出在哪呢?還是出在操作不是原子性上。

A請求執行步驟1、2完畢,還未執行步驟2時,鎖過期了,自動解鎖!這時B請求加鎖必然成功,而A請求繼續執行步驟3,把B請求的鎖給刪了。

正確的做法如下:

public static boolean releaseLock(String key,String requestId){
 String luaCommand="if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
 List<String> keyList=Lists.newArrayList(key);//這里我用的是Guava
List<String> valList=Lists.newArrayList(requestId);
 Object result=RedisClient.eval(luaCommand,keyList,valList);
 if(result.equals(1L)){return true;}
 return false;
}

利用的就是Redis通過eval命令執行LUA腳本是原子性的特性。


再然后就是使用Redisson實現了,這個適用于集群部署的Redis。

我在實際使用Redis分布式鎖的時候遇到過一種情況。使用分布式鎖后,要調用第三方接口,從而導致整個流程時間偏長,鎖過期的情況下還沒有執行完,當時的處理方式是加大了過期時間。

如果使用Redisson,因為有看門狗機制,就很好地解決了這個問題。看門狗會定時去檢查,如果請求實例還在則自動去延長超時時間。不過這帶來的問題一定是性能的下降,所以當時我們還是采用了粗暴的延長設置過期時間來解決此類問題。

Redisson也是個可重入鎖,因為鎖的內容除了key、實例ID之外還有數字Value,這樣一來同樣的實例多次拿鎖,Value+1,釋放鎖,Value-1即可。

分享到:
標簽:Redis
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定