<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    jinfeng_wang

    G-G-S,D-D-U!

    BlogJava 首頁 新隨筆 聯系 聚合 管理
      400 Posts :: 0 Stories :: 296 Comments :: 0 Trackbacks
    http://huoding.com/2015/09/14/463 



    在 Redis 里,所謂 SETNX,是「SET if Not eXists」的縮寫,也就是只有不存在的時候才設置,可以利用它來實現鎖的效果,不過很多人沒有意識到 SETNX 有陷阱!

    比如說:某個查詢數據庫的接口,因為調用量比較大,所以加了緩存,并設定緩存過期后刷新,問題是當并發量比較大的時候,如果沒有鎖機制,那么緩存過期的瞬間,大量并發請求會穿透緩存直接查詢數據庫,造成雪崩效應,如果有鎖機制,那么就可以控制只有一個請求去更新緩存,其它的請求視情況要么等待,要么使用過期的緩存。

    下面以目前 PHP 社區里最流行的 PHPRedis 擴展為例,實現一段演示代碼:

    <?php  $ok = $redis->setNX($key, $value);  if ($ok) {     $cache->update();     $redis->del($key); }  ?>

    緩存過期時,通過 SetNX  獲取鎖,如果成功了,那么更新緩存,然后刪除鎖。看上去邏輯非常簡單,可惜有問題:如果請求執行因為某些原因意外退出了,導致創建了鎖但是沒有刪除鎖,那么這個鎖將一直存在,以至于以后緩存再也得不到更新。于是乎我們需要給鎖加一個過期時間以防不測:

    <?php  $redis->multi(); $redis->setNX($key, $value); $redis->expire($key, $ttl); $redis->exec();  ?>

    因為 SetNX 不具備設置過期時間的功能,所以我們需要借助 Expire 來設置,同時我們需要把兩者用 Multi/Exec 包裹起來以確保請求的原子性,以免 SetNX 成功了 Expire 卻失敗了。 可惜還有問題:當多個請求到達時,雖然只有一個請求的 SetNX 可以成功,但是任何一個請求的 Expire 卻都可以成功,如此就意味著即便獲取不到鎖,也可以刷新過期時間,如果請求比較密集的話,那么過期時間會一直被刷新,導致鎖一直有效。于是乎我們需要在保證原子性的同時,有條件的執行 Expire,接著便有了如下 Lua 代碼:

    local key   = KEYS[1] local value = KEYS[2] local ttl   = KEYS[3]  local ok = redis.call('setnx', key, value)   if ok == 1 then   redis.call('expire', key, ttl) end   return ok

    沒想到實現一個看起來很簡單的功能還要用到 Lua 腳本,著實有些麻煩。其實 Redis 已經考慮到了大家的疾苦,從 2.6.12 起,SET 涵蓋了 SETEX 的功能,并且 SET 本身已經包含了設置過期時間的功能,也就是說,我們前面需要的功能只用 SET 就可以實現。

    <?php  $ok = $redis->set($key, $value, array('nx', 'ex' => $ttl));  if ($ok) {     $cache->update();     $redis->del($key); }  ?>

    如上代碼是完美的嗎?答案是還差一點!設想一下,如果一個請求更新緩存的時間比較長,甚至比鎖的有效期還要長,導致在緩存更新過程中,鎖就失效了,此時另一個請求會獲取鎖,但前一個請求在緩存更新完畢的時候,如果不加以判斷直接刪除鎖,就會出現誤刪除其它請求創建的鎖的情況,所以我們在創建鎖的時候需要引入一個隨機值:

    <?php  $ok = $redis->set($key, $random, array('nx', 'ex' => $ttl));  if ($ok) {     $cache->update();      if ($redis->get($key) == $random) {         $redis->del($key);     } }  ?>

    如此基本實現了單機鎖,假如要實現分布鎖,請參考:Distributed locks with Redis,不過分布式鎖需要注意的地方更多:How to do distributed lockingIs Redlock safe

    posted on 2016-12-14 21:30 jinfeng_wang 閱讀(169) 評論(0)  編輯  收藏 所屬分類: 2016-REDIS
    主站蜘蛛池模板: 亚洲乱码一二三四五六区| 国产成人精品久久亚洲高清不卡| 国产成人1024精品免费| 久久亚洲国产成人影院网站| 亚洲精品免费视频| 亚洲av无码一区二区三区在线播放| 国产免费人成在线视频| 日本免费中文视频| 国产精品亚洲综合网站| 亚洲短视频男人的影院| 在线播放高清国语自产拍免费| 日韩免费码中文在线观看| 久久久无码精品亚洲日韩蜜臀浪潮 | 亚洲桃色AV无码| 最近高清中文字幕免费| 黄网站色成年片大免费高清| 亚洲黄色免费观看| 亚洲成a人片在线观看老师| 在线观看免费av网站| 一级毛片免费播放视频| 亚洲一区动漫卡通在线播放| 亚洲人成无码网站| 日韩高清在线免费看| 99re免费在线视频| 国产高潮久久免费观看| 亚洲另类自拍丝袜第五页| 亚洲福利在线观看| 亚洲中文久久精品无码| 暖暖在线日本免费中文| 91九色视频无限观看免费| 本免费AV无码专区一区| 亚洲第一se情网站| 亚洲一级特黄特黄的大片| 亚洲AV永久无码精品水牛影视| 在线免费观看色片| 国产h肉在线视频免费观看| 最好免费观看高清在线| 一级人做人爰a全过程免费视频| 亚洲1区1区3区4区产品乱码芒果| 亚洲中文字幕无码一区二区三区| 免费观看的av毛片的网站|