需要寫一個抽獎活動,並發量很大,抽獎的同時需要操作多個數據表,決定采用redis鎖.
網上找了一下,找到大牛的博客
http://www.cnblogs.com/yjf512/archive/2017/03/22/6597814.html
Q:很好奇解鎖的函數里為什么要用redis執行lua腳本,為什么不用php直接來操作呢?
A:群里問了下,大概明白了,redis是原子性,操作都是串行,但php是並發操作的,所以在高並發的時候可能存在臟讀的問題,所以使用eval函數在redis里串行的執行這段代碼,因為是串行的,不存在並發問題.
Q:如何實現阻塞鎖
A:我沒有在網上找到好的解決方案,都是自己寫循環執行
SET key value [EX seconds] [PX milliseconds] [NX|XX]
將字符串值 value 關聯到 key 。
如果 key 已經持有其他值, SET 就覆寫舊值,無視類型。
對於某個原本帶有生存時間(TTL)的鍵來說, 當 SET 命令成功在這個鍵上執行時, 這個鍵原有的 TTL 將被清除。
可選參數
從 Redis 2.6.12 版本開始, SET 命令的行為可以通過一系列參數來修改:
- EX second :設置鍵的過期時間為 second 秒。 SET key value EX second 效果等同於 SETEX key second value 。
- PX millisecond :設置鍵的過期時間為 millisecond 毫秒。 SET key value PX millisecond 效果等同於 PSETEX keymillisecond value 。
- NX :只在鍵不存在時,才對鍵進行設置操作。 SET key value NX 效果等同於 SETNX key value 。
- XX :只在鍵已經存在時,才對鍵進行設置操作。
因為 SET 命令可以通過參數來實現和 SETNX 、 SETEX 和 PSETEX 三個命令的效果,所以將來的 Redis 版本可能會廢棄並最終移除 SETNX 、 SETEX 和 PSETEX 這三個命令。
1 <?php 2 /** 3 * Created by PhpStorm. 4 * User: yiyz 5 * Date: 2017/7/6 6 * Time: 下午2:05 7 */ 8 9 namespace common\vendor; 10 11 12 use common\helper\TextHelper; 13 14 class RedisLock 15 { 16 private $key; 17 private $timeout; 18 private $token; 19 20 public function __construct($key, $timeout = 10) 21 { 22 $this->key = $key; 23 $this->timeout = $timeout; 24 $this->token = TextHelper::generateOrderNo(); 25 } 26 27 /** 28 * 阻塞加鎖 29 * @return bool 30 */ 31 public function lock() 32 { 33 $timeStart = microtime(true) * 1000; 34 while (true) { 35 $res = \Yii::$app->redis->set($this->key, $this->token, "nx", "ex", $this->timeout); 36 if ($res) { 37 break; 38 } 39 $timeEnd = microtime(true) * 1000; 40 if ($timeEnd - $timeStart > 10000) { 41 //add log error 42 return false;43 } 44 } 45 return true; 46 } 47 48 /** 49 * 解鎖 50 * @return mixed 51 */ 52 public function unlock() 53 { 54 $script = ' 55 if redis.call("get",KEYS[1]) == ARGV[1] 56 then 57 return redis.call("del",KEYS[1]) 58 else 59 return 0 60 end'; 61 return \Yii::$app->redis->eval($script, $this->key, $this->token); 62 } 63 }
隨便寫的,沒經過測試,大牛不要笑我...