当前位置: 首页 > 后端技术 > PHP

使用Redis锁解决高并发问题

时间:2023-03-30 01:17:32 PHP

这里主要使用Redis的setnx命令来处理高并发。setnx有两个参数。第一个参数代表密钥。第二个参数表示值。如果当前键不存在,则以第二个参数为值插入当前键。返回1。如果当前键存在,则返回0。CREATETABLE`storage`(`id`int(11)unsignedNOTNULLAUTO_INCREMENT,`number`int(11)DEFAULTNULL,PRIMARYKEY(`id`))ENGINE=InnoDBAUTO_INCREMENT=1DEFAULTCHARSET=latin1settingCREATETABLE`order`(`id`int(11)unsignedNOTNULLAUTO_INCREMENT,`number`int(11)DEFAULTNULL,PRIMARYKEY(`id`))ENGINE=InnoDBAUTO_INCREMENT=1DEFAULTCHARSET=latin1testwithoutlock$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="更新存储集`number`=`number`-1WHEREid=1";$pdo->查询($sql);}}ab测试模拟并发,发现库存是正确的。mysql>从存储中选择*;+----+------+|编号|数字|+----+--------+|1|0|+----+--------+1rowinset(0.00sec)Lookingattheordertablemysql>select*from`order`;+----+-------+|编号|数字|+----+--------+|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();}/***@functionlock*@param$key锁名*@param$expTime过期时间*/publicfunctionset($key,$expTime){//初步锁$isLock=$this->_redis->setnx($key,time()+$expTime);如果($isLock){返回真;}else{//在锁定失败的情况下。判断锁是否已经存在,如果锁已经过期,则删除锁。进行重新加锁$val=$this->_redis->get($key);如果($val&&$valdel($key);返回$this->_redis->setnx($key,time()+$expTime);}返回假;}}/***@param$key解锁*/publicfunctiondel($key){$this->_redis->del($key);}}$pdo=newPDO('mysql:host=127.0.0.1;dbname=test','root','root');$lockObj=Lock::getInstance();//判断是能加锁成功if($lock=$lockObj->set('storage',10)){$sql="select`number`fromstoragewhereid=1limit1";$res=$pdo->query($sql)->fetch();$number=$res['number'];if($number>0){$sql="插入`order`VALUES(null,$number)";$order_id=$pdo->query($sql);if($order_id){$sql="更新存储集`number`=`number`-1WHEREid=1";$pdo->查询($sql);}}//解锁$lockobj->del('storage');}else{//加锁失败,执行其他操作}再次执行ab测试,查看测试结果mysql>select*from`order`;+----+--------+|编号|数字|+----+--------+|1|10||2|9||3|8||4|7||5|6||6|5||7|4||8|3||9|2||10|1|+----+--------+10rowsinset(0.00sec)orderfound该表没有操作相同的库存数据。所以使用redis锁可以有效的处理高并发。这里在加锁的时候其实是不需要判断过期时间的。这里为了避免死锁,加入了过期时间的判断。过期主动删除锁。