php使用redis的锁机制解决缓存雪崩及商城订单超卖的问题

发布时间:2021/11/26 作者:天马行空 阅读(913)

redis有一个setnx方法,它是是set if not exists 的缩写,也就是只有不存在的时候才设置,设置成功时返回1,设置失败时返回0,可以利用这个特性来实现锁的效果。
因为用户量比较大,所以我们给某些业务加上了缓存。这样,就不会在用户每次访问的时候都直接查询到数据库,然而在缓存失效的那一瞬间,有大量的用户进来访问,就会造成数据库瞬间压力倍增。
这时,就需要想一个办法只让一个用户进来更新缓存,其他用户都乖乖的在外面等着。
我们就可以利用setnx方法来增加一个锁,只有拿到这个锁的用户才能进来,没拿到锁的用户就在外面等着进来的用户更新缓存给他们访问。

$cache = $redis->get($cachekey);
if(empty($cache)){
    //获取锁
    $rs = $redis->setNX($key, $value);
    if ($rs) {
        //设置一个缓存过期时间,避免业务卡死后所有用户都无法访问
        $redis->expire($key, 10);
        //处理更新缓存逻辑
        $redis->set($cachekey,.....);
        //删除锁
        $redis->del($key);
    }
}


上面的代码可以解决大部分问题,但setNX、expire不是原子操作,如果一个用户拿到了锁,而设置缓存时间失败了的话,同样会造成所有的用户都无法访问(因为都拿不到锁),那这个问题就大了。那么要怎么才能解决这个问题呢?其实,在Redis从2.6.12起SET就涵盖了 SETEX的功能,SET本身又包含了设置过期时间的功能,所以使用SET就可以解决上面遇到的问题。

$cache = $redis->get($cachekey);
if(empty($cache)){
    //获取锁,同时设置缓存过期时间
    $rs = $redis->set($key, $random, array('nx', 'ex' => 10));
    if ($rs) {
        //处理更新缓存逻辑
        $redis->set($cachekey,.....);
        //先判断随机数,是同一个则删除锁,避免删掉其他用户的锁
        if ($redis->get($key) == $random) {
            $redis->del($key);
        }
    }
}


同样,我们可以利用这个加锁的思路解决订单超卖的问题,用户在下单的时候,同一个时间我们就只允许一个用户进来减库存,其他的用户就先等着上一个用户下单完释放锁之后再进行减库存。为了提高用户的体验,可以在外层加一个循环减库存,这样就可以提高用户的抢购成功率。

关键字php redis