php 并发 使用redis锁

使用场景

稍微有点儿并发的项目会面临一个问题 就是数据不一致 可以实现单机锁 分布式锁
比如 根据name判断数据是否存在 如果不存在新增 如果存在则不新增
如果将name字段设为唯一索引 容易报错程序将不在执行 try catch又太乱套了

redis锁

redis中string类型 有两个参数 NX EN
redis->set(key, value,[‘nx’,‘ex’=>60]); 比如这种形式
NX的作用是 如果这个key存在 则等待 说白了就是只有当key不存在时 才可以进行赋值
EX是设置key的过期时间

redis类代码

这个是封装好的一个redis锁类 除了单机锁之外 还可以进行分布式锁设置
要注意 尽量要不要把锁的范围设置太大 我是根据ID当做锁的key
也要避免 当前进程删除了其他进程的锁

namespace app\Utils;
use Redis;
class RedisLock {
    public $redis;
    public $key;
    public $value;
    public function __construct()
    {
        $redis = new redis();
        $redis->connect("127.0.0.1",6379);
        $this->redis = $redis;
    }

    public function getLock($params){
        $key = $params['key'];
        $value = $params['value'];
        $number = $params['number'];
        $time = $params['time'];

        $redis = $this->redis;
        $this->key = $key;
        $this->value = $value;
        $lock = false;
        /*
			注意 这里是通过循环来获取锁资源 要不然即使第一次获取也会阻塞返回false
			没有获取到锁返回(没有设置key成功)false
			获取到锁(设置key成功)返回true
			比如第一个进程获取锁 在第一个进程没有释放锁之前 后面的进程是无法获取这把锁的 
		*/
        while ($number){
            $lock = $redis->set($key, $value,['nx','ex'=>$time]);
            if($lock){
                break;
            }
            $number--;
            usleep(500000);
        }
        return $lock;
    }
	/*
		当在获取锁的时候 会设置一个value 为了避免当前进程删除了其他进程的锁
		在删除锁的时候 要判断一下 redis中key对应的value 是不是当前进程设置的
		如果是的话 才可以删除
	*/
    public function delLock(){
        $delLock = false;
        $redis = $this->redis;
        if($this->value==$redis->get($this->key)){
            $delLock = $redis->del($this->key);
        }
        return $delLock;
    }
}

锁的调用

//这个HookBash类是我自己封装的 其实redis类直接new就可以 这一步可以忽略掉
$hook_base = HookBash::getinstance();
//生成一个value
$value = uniqid(microtime());
//添加锁 number是while循环最大次数 time是最长等待时间 如果超过返回false
$lock =  $hook_base::getLock(['obj'=>"RedisLock","params"=>["key"=>"lock_faLog_{$groupOrderId}","value"=>$value,"number"=>50,"time"=>25]]);
if(!$lock){
    $this->e("获取锁失败",602);
}
//执行你自己的业务逻辑
$this->makeOrder($groupOrderId);
//执行之后删除锁
$delLock = $hook_base::delLock(['obj'=>"RedisLock","params"=>[]]);
//我这里删除锁失败的话 是记录了一下日志 但是在运行中并没有出现删除所失败的情况
if(!$delLock){
	$this->e("删除锁失败",603);
} 

其他注意事项

在运行阶段redis中并不会出现大量的key(除非并发特别高)
在执行keys *的时候 比如出现了10个不一样儿的key
再次执行keys *的时候 上面内10个key就没了 而是出现10个新的key
这样也就意味着redis中长期存在的key是没有太多 因为进程执行完毕删除锁(业务逻辑复杂 耗时的单算)
如果redis中开启持久化 建议 不要开启AOF AOF会把你每一次命令进行存储 (除非redis中还存储了其他比较重要的数据 我是开启的RDB)

你可能感兴趣的:(php 并发 使用redis锁)