redis锁实现
编辑时间:2018-01-12 作者:Linz
## redis锁实现 ### 前言 当今的站点,大部分都会使用缓存,无论是用Memcache又或Redis。 其中一种用法:去拿热数据时,我们一般会加一层缓存,并且设定过期时间。但是,这里如果不注意,容易导致一个问题的产生:当并发很大时,在缓存过期的瞬间,如果有大量的请求,那么这些请求会直接打到数据库,去拿数据库的数据,这就是所谓的"缓存雪崩"。 例如,本博客中的[php代码实现Memcache缓存的实例](https://www.3maio.com/w-detail/6)的代码,在高并发下,就会存在这种问题。 下面,就通过一些简单的代码来演示一下。 <?php /* 测试没有使用锁,高并发下会产生的问题 */ $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $value = $redis->get('a'); //判断有无,如果有,不保存数据。 //任何情况a都是1,才是正确的值。如果在多个客户端请求时,如果a不是1,说明出问题了 if (!$value) { //进到if,查询数据库 $id = $redis->incr('id');//模拟查询数据库 $redis->setEx('a', 100, $id); } 上面的代码,当并发200时,发现a键的值是10。这就说明,并发200时,其中有10个请求同时去查询了数据库。这就好像,同时200个人去抢一个厕所,结果又10个人抢到了。那结果就很悲剧了,因为我们不能让10个人同时上一个厕所。 ### 加锁 * 方式一 $result = $redis->setnx($key, $val);//1行 if ($result == 1) { $redis->expire($key, 100);//2行 } 但是这种方式也是有问题的,1行与2行是两次对Redis的操作,不具备原子性。有可能在expire时,redis挂了或者expire失败了,这样的结果就是$key没有成功设置时间。 * 方式二 $redis->set($key, $value, Array('nx', 'ex'=>10)); SET命令从2.6.12就包含了设置过期时间功能。 nx表示当$key不存在时我们才进行set操作,ex是有效期。10单位是秒 ### 正确写法(仅供参考) <?php $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $value = $redis->get('aa3'); if (!$value) { //这是模拟数据库查询,给数据库查询上锁,保证同时只能一个查询在进行 if (getLock($redis, 'test', uniqid(), 10)) { $id = $redis->incr('idid3');//这行代码模拟的是数据库查询 $redis->setEx('aa3', 10, $id);//将数据库中查询出的数据保存到Redis的aa3键 $redis->setTimeout('idid3', 10); } } //加锁 function getLock($redis, $lockName, $lockValue, $expire) { //px 为毫秒 return $redis->set('lock:'.$lockName, $lockValue, array('nx', 'px' => $expire)); } ### 参考 1. https://redis.io/topics/distlock
来说两句吧
最新评论