发布时间:2021/11/26 作者:天马行空 阅读(1270)
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); } } }
同样,我们可以利用这个加锁的思路解决订单超卖的问题,用户在下单的时候,同一个时间我们就只允许一个用户进来减库存,其他的用户就先等着上一个用户下单完释放锁之后再进行减库存。为了提高用户的体验,可以在外层加一个循环减库存,这样就可以提高用户的抢购成功率。