如何利用Redis锁解决高并发问题详解
redis技术的使用:
redis真的是一个很好的技术,它可以很好的在一定程度上解决网站一瞬间的并发量,例如商品抢购秒杀等活动。。。
redis之所以能解决高并发的原因是它可以直接访问内存,而以往我们用的是数据库(硬盘),提高了访问效率,解决了数据库服务器压力。
为什么redis的地位越来越高,我们为何不选择memcache,这是因为memcache只能存储字符串,而redis存储类型很丰富(例如有字符串、LIST、SET等),memcache每个值最大只能存储1M,存储资源非常有限,十分消耗内存资源,而redis可以存储1G,最重要的是memcache它不如redis安全,当服务器发生故障或者意外关机等情况时,redsi会把内存中的数据备份到硬盘中,而memcache所存储的东西全部丢失;这也说明了memcache不适合做数据库来用,可以用来做缓存。
引言
这里我们主要利用Redis的setnx的命令来处理高并发。
setnx有两个参数。第一个参数表示键。第二个参数表示值。如果当前键不存在,那么会插入当前键,将第二个参数做为值。返回1。如果当前键存在,那么会返回0。
创建库存表
CREATETABLE`storage`( `id`int(11)unsignedNOTNULLAUTO_INCREMENT, `number`int(11)DEFAULTNULL, PRIMARYKEY(`id`) )ENGINE=InnoDBAUTO_INCREMENT=1DEFAULTCHARSET=latin1
设置初始库存为10
创建订单表
CREATETABLE`order`( `id`int(11)unsignedNOTNULLAUTO_INCREMENT, `number`int(11)DEFAULTNULL, PRIMARYKEY(`id`) )ENGINE=InnoDBAUTO_INCREMENT=1DEFAULTCHARSET=latin1
测试不用锁的时候
$pdo=newPDO('mysql:host=127.0.0.1;dbname=test','root','root'); $sql="select`number`fromstoragewhereid=1limit1"; $res=$pdo->query($sql)->fetch(); $number=$res['number']; if($number>0) { $sql="insertinto`order`VALUES(null,$number)"; $order_id=$pdo->query($sql); if($order_id) { $sql="updatestorageset`number`=`number`-1WHEREid=1"; $pdo->query($sql); } }
ab测试模拟并发,发现库存是正确的。
mysql>select*fromstorage; +----+--------+ |id|number| +----+--------+ |1|0| +----+--------+ 1rowinset(0.00sec)
在来看订单表
mysql>select*from`order`; +----+--------+ |id|number| +----+--------+ |1|10| |2|10| |3|9| |4|7| |5|6| |6|5| |7|5| |8|5| |9|4| |10|1| +----+--------+ 10rowsinset(0.00sec)
发现存在几个订单都是操作的同一个库存数据,这样就可能引起超卖的情况。
修改代码加入redis锁进行数据控制
_redis=newRedis(); $this->_redis->connect('127.0.0.1'); } publicstaticfunctiongetInstance() { if(self::$_instanceinstanceofself) { returnself::$_instance; } returnself::$_instance=newself(); } /** *@function加锁 *@param$key锁名称 *@param$expTime过期时间 */ publicfunctionset($key,$expTime) { //初步加锁 $isLock=$this->_redis->setnx($key,time()+$expTime); if($isLock) { returntrue; } else { //加锁失败的情况下。判断锁是否已经存在,如果锁存在切已经过期,那么删除锁。进行重新加锁 $val=$this->_redis->get($key); if($val&&$val
再次进行ab测试,查看测试结果
mysql>select*from`order`; +----+--------+ |id|number| +----+--------+ |1|10| |2|9| |3|8| |4|7| |5|6| |6|5| |7|4| |8|3| |9|2| |10|1| +----+--------+ 10rowsinset(0.00sec)
发现订单表没有操作同一个库存数据的情况。所以利用redis锁是可以有效的处理高并发的。
这里在加锁的时候其实是可以不需要判断过期时间的,这里我们为了避免造成死锁,所以加一个过期时间的判断。当过期的时候主动删除该锁。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。