Hyperf方案 Redis分布式锁
?php// 案例Redis分布式锁// 说明用SET NX EX命令实现分布式锁防止多个进程同时执行同一段代码// 需要安装composer require hyperf/redisdeclare(strict_types1);namespaceApp\Lock;useHyperf\Redis\Redis;useHyperf\Di\Annotation\Inject;classRedisLock{#[Inject]protectedRedis$redis;// 加锁返回true表示抢到了锁false表示别人已经占着publicfunctionlock(string$key,int$ttl10,string$owner):bool{$owner$owner?:uniqid(lock_,true);// 锁的持有者标识用来解锁时验证$lockKeylock:.$key;// 加个前缀和其他key区分开// SET key value NX EX ttl — NX表示不存在才设置EX表示过期时间// 这一条命令是原子的不会出现设置了但没设过期时间的情况$result$this-redis-set($lockKey,$owner,[NX,EX$ttl]);return$result!false;// 设置成功说明抢到锁了}// 解锁只能解自己加的锁用Lua脚本保证原子性publicfunctionunlock(string$key,string$owner):bool{$lockKeylock:.$key;// 用Lua脚本先检查是不是自己的锁是才删不是就不动// 这两步必须用Lua保证原子否则check和delete之间可能被别人抢走$luaLUAif redis.call(GET, KEYS[1]) ARGV[1] then return redis.call(DEL, KEYS[1]) else return 0 endLUA;$result$this-redis-eval($lua,[$lockKey,$owner],1);return$result1;// 返回1说明删掉了0说明锁不是你的}// 尝试加锁加不上就等一会儿再试最多等$waitMs毫秒publicfunctiontryLock(string$key,int$ttl10,int$waitMs3000):?string{$owneruniqid(lock_,true);// 生成唯一标识$lockKeylock:.$key;$deadlinemicrotime(true)*1000$waitMs;// 截止时间while(microtime(true)*1000$deadline){$result$this-redis-set($lockKey,$owner,[NX,EX$ttl]);if($result!false){return$owner;// 抢到了返回owner后面解锁要用}usleep(50000);// 没抢到等50毫秒再试}returnnull;// 等了这么久还没抢到放弃}// 续期快到期了还没处理完把过期时间续一下publicfunctionrenewLock(string$key,string$owner,int$ttl10):bool{$lockKeylock:.$key;$luaLUAifredis.call(GET,KEYS[1])ARGV[1]thenreturnredis.call(EXPIRE,KEYS[1],ARGV[2])elsereturn0endLUA;$result$this-redis-eval($lua,[$lockKey,$owner,$ttl],1);return$result1;}}// 业务层怎么用namespaceApp\Service;useApp\Lock\RedisLock;useHyperf\Di\Annotation\Inject;classOrderService{#[Inject]protectedRedisLock$lock;// 下单防止同一用户同时提交多个订单publicfunctioncreateOrder(int$userId,array$items):array{$lockKeyorder_create_{$userId};// 按用户维度加锁$owneruniqid(lock_,true);// 先尝试加锁等3秒$owner$this-lock-tryLock($lockKey,ttl:10,waitMs:3000);if($ownernull){return[code429,msg操作太频繁请稍后再试];}try{// 这里处理真正的下单逻辑$orderId$this-doCreateOrder($userId,$items);return[code0,data[order_id$orderId]];}finally{$this-lock-unlock($lockKey,$owner);// 不管成功失败都要解锁}}// 秒杀扣库存并发高的场景publicfunctiondeductStock(int$productId,int$qty):bool{$lockKeystock_{$productId};$owneruniqid(lock_,true);if(!$this-lock-lock($lockKey,ttl:5,owner:$owner)){returnfalse;// 没抢到锁直接返回失败}try{// 查库存$stock$this-getStock($productId);if($stock$qty){returnfalse;// 库存不足}// 扣库存$this-updateStock($productId,$stock-$qty);returntrue;}finally{$this-lock-unlock($lockKey,$owner);// 完事解锁}}privatefunctiondoCreateOrder(int$userId,array$items):int{returnrandom_int(100000,999999);// 模拟返回订单ID}privatefunctiongetStock(int$productId):int{return100;// 模拟查库存}privatefunctionupdateStock(int$productId,int$newStock):void{// 模拟更新库存}}