摘要:分布式鎖的作用在單機環(huán)境下,有個秒殺商品的活動,在短時間內(nèi),服務器壓力和流量會陡然上升。分布式集群業(yè)務業(yè)務場景下,每臺服務器是獨立存在的。這里就用到了分布式鎖這里簡單介紹一下,以的事務機制來延生。
Redis 分布式鎖的作用
在單機環(huán)境下,有個秒殺商品的活動,在短時間內(nèi),服務器壓力和流量會陡然上升。這個就會存在并發(fā)的問題。想要解決并發(fā)需要解決以下問題
1、提高系統(tǒng)吞吐率也就是qps 每秒處理的請求書
2、避免商品在高并發(fā)的情況下,出現(xiàn)資源爭搶導致的超買超買問題
解決問題一:采用內(nèi)存型數(shù)據(jù)庫提高系統(tǒng)的qps
解決問題二:就要用到經(jīng)常會遇到的鎖,例如MySQL 有讀鎖、寫鎖、排他鎖、悲觀鎖、樂觀鎖。不過這里只討論redis來實現(xiàn)鎖
$redis = new Redis(); $redis->connect("127.0.0.1", 6379); //連接Redis $expire = 10;//有效期10秒 $key = "lock";//key $value = time() + $expire;//鎖的值 = Unix時間戳 + 鎖的有效期 $lock = $redis->setnx($key, $value); //判斷是否上鎖成功,成功則執(zhí)行下步操作 if(!empty($lock)) { //下步操作... }
如果以這樣的簡單版設置鎖就能解決所有問題,未免也太小看鎖在程序中應用了。
按正常的操作示例基本上都是這樣寫的。但是這樣寫有一些問題
1、假如有10000 個請求訪問了redis 不存在的鍵,這樣請求就是指接到了MySQL數(shù)據(jù),造成CPU短時間內(nèi)達到100%甚至宕機。這樣場景俗稱緩存擊穿造成的緩存雪崩。
2、 假如CPU過高或者網(wǎng)絡延時問題,造成鎖沒有被刪除掉或者緩存鍵過期沒有被回收的情況,就會形成死鎖。
解決問題:引用reids setnx 方法的作用是,當設置的key 不存在時,設置新的值。這樣就避免了緩存擊穿的問題。檢測鍵的過期時間,避免產(chǎn)生死鎖
解決死鎖問題$expire = 10;//有效期10秒 $key = "lock";//key $value = time() + $expire;//鎖的值 = Unix時間戳 + 鎖的有效期 $status = true; while($status) { $lock = $redis->setnx($key, $value); if(empty($lock)) { $value = $redis->get($key); if($value < time()) { $redis->del($key); } }else{ $status = false; //下步操作.... } }
1、按理說這樣解決單機版鎖競爭問題已經(jīng)是沒有多大問題了。那么特殊情況來了,如果這個設置鎖的鍵由于意外情況沒有被刪除,這樣同樣會有死鎖的情況發(fā)生。2、分布式集群業(yè)務業(yè)務場景下,每臺服務器是獨立存在的。多臺服務器怎么通過一個標識來相互競爭鎖呢。這里就用到了分布式鎖
這里簡單介紹一下,以MYSQL 的事務機制來延生。事務四個特性ACID,有四種隔離級別:未提交讀、已提交讀、可重復讀、串行化。這些特性都只在單臺服務器上生效。到了分布式集群了,數(shù)據(jù)在不同的服務器上,緊靠事務很難保持數(shù)據(jù)的一致性及隔離性,事務的作用就意義不大了。Redis也是如此。
正確的分布式鎖的打開方式/** * 實現(xiàn)Redis分布鎖 */ $key = "demo"; //要更新信息的緩存KEY $lockKey = "lock:".$key; //設置鎖KEY $lockExpire = 10; //設置鎖的有效期為10秒 //獲取緩存信息 $result = $redis->get($key); //判斷緩存中是否有數(shù)據(jù) if(empty($result)) { $status = TRUE; while ($status) { //設置鎖值為當前時間戳 + 有效期 $lockValue = time() + $lockExpire; /** * 創(chuàng)建鎖 * 試圖以$lockKey為key創(chuàng)建一個緩存,value值為當前時間戳 * 由于setnx()函數(shù)只有在不存在當前key的緩存時才會創(chuàng)建成功 * 所以,用此函數(shù)就可以判斷當前執(zhí)行的操作是否已經(jīng)有其他進程在執(zhí)行了 * @var [type] */ $lock = $redis->setnx($lockKey, $lockValue); /** * 滿足兩個條件中的一個即可進行操作 * 1、上面一步創(chuàng)建鎖成功; * 2、 1)判斷鎖的值(時間戳)是否小于當前時間 $redis->get() * 2)同時給鎖設置新值成功 $redis->getset() */ if(!empty($lock) || ($redis->get($lockKey) < time() && $redis->getSet($lockKey, $lockValue) < time() )) { //給鎖設置生存時間 $redis->expire($lockKey, $lockExpire); //****************************** //此處執(zhí)行插入、更新緩存操作... //****************************** //以上程序走完刪除鎖 //檢測鎖是否過期,過期鎖沒必要刪除 if($redis->ttl($lockKey)) $redis->del($lockKey); $status = FALSE; }else{ /** * 如果存在有效鎖這里做相應處理 * 等待當前操作完成再執(zhí)行此次請求 * 直接返回 */ sleep(2);//等待2秒后再嘗試執(zhí)行操作 } } }結尾
文章從知識面的廣度(mysql)、示例代碼優(yōu)缺點的簡介及應用的場景,區(qū)別于其他博客文章。嘿嘿~
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://systransis.cn/yun/31258.html
摘要:由于執(zhí)行的原子性所以不要在中執(zhí)行過長開銷的程序,否則會驗證影響其它請求的執(zhí)行。同一個腳本生成的簽名都是相同的,所以簽名可以先在本地生成,然后在服務器上一次腳本,程序中只需保存和使用該簽名即可。同樣的腳本,是始終生成相同的簽名的。 Last-Modified: 2019年6月5日15:59:34 參考鏈接 PHP使用Redis+Lua腳本操作的注意事項 《Redis官方文檔》用Redi...
閱讀 2879·2021-10-14 09:43
閱讀 1673·2021-09-29 09:34
閱讀 1757·2021-07-28 00:16
閱讀 2972·2019-08-30 15:53
閱讀 2917·2019-08-30 13:59
閱讀 2972·2019-08-30 13:57
閱讀 1102·2019-08-26 13:38
閱讀 1906·2019-08-26 13:25