redis的并发问题处理

简单模拟下redis并发的场景:

建立2个文件1.php,2.php,内容如下:

<?php

$redis=new redis();
$redis->connect('127.0.0.1');
$price=$redis->get('price');
sleep(10);
$price=$price+10;

$redis->set('price',$price);

?>

price的初始值为10,分别运行这2个文件,发现最后price的值是20,而不是我们预想中的值30.这就是redis并发问题的一个简单的场景,那么要如何应对呢 ?有以下几种方法可以解决,用什么方法取决于业务需求。

1.使用redis的事务处理

<?php

$redis=new redis();

$redis->connect('127.0.0.1');


function doll(){
global $redis;
$redis->set('price',10);
}


$redis->watch('price');//监听 当监听的值发生变化的话就执行失败


$redis->get('price');


//模拟并发 模拟当其他线程也同时在对price进行写操作 此时事务将执行失败 放在事务开始之前
doll();


$redis->multi();//事务开始标志

$redis->set('price',20);

// sleep(5);

$res=$redis->exec();//执行事务

var_dump($res);exit;

?>

假设price的初始值为0,那么执行完该脚本后,price的值为10,$res的值为false,也就避免了并发操作price的问题。也就是说,当你监听了price之后,如果price的值发生了变化(其他并发操作),那么事务将失败。但是这个场景有个缺点就是,假如某个时刻同时有100个用户进行写操作,由于他们watch的price是同一个值,那么只要有一个人写入成功之后,由于price的值已经发生了变化,所以其他99个用户的操作都将失败。

2.加个自定义锁(排它锁),当有用户在对price进行写操作的时候,其他用户不能读写。

<?php 

/**
 * 实现Redis分布锁
 */
$redis=new redis();
$redis->connect('127.0.0.1');

$key        = 'price';       //要更新信息的缓存KEY
$lockName    = 'lock'.$key; //设置锁KEY
$lockExpire = 10;           //设置锁的有效期为5秒

//创建锁
$lock=$redis->setnx($lockName,time()+$lockExpire);
//如果锁不存在($lock为true) 说明锁创建成功 也就是说现在没人正在对price进行写操作 可以对price进行写操作
//如果锁存在 但是已经过期 也可以进行对price进行写操作 并重新设置过期时间
if ($lock ||  ($redis->get($lockName)<time() && $redis->getset($lockName,time()+$lockExpire)<time())) {
//给锁设置生存时间
    $redis->expire($lockName, $lockExpire);//当对price写入操作的这部分代码的执行时间大于缓存时间($lockExpire)时,依然有可能会产生并发。所以对price的操作要尽量放在前面(比如当sleep(10)的代码放在set操作之前依然会产生并发)实际线上环境应该测试这部分代码的执行时间,然后$lockExpire的值要大于这个执行时间
    //开始对price进行读写
$price=$redis->get('price');
$price=$price+10;
sleep(5);
$redis->set($key,$price);
//锁还没过期就删除 过期的没必要删除
if ($redis->ttl($lockName)) {
$redis->del($lockName);
}
}else{
//如果不是以上情况 说明有人正在对price进行写操作
echo 'please wait!';exit;

}

?>

参考文章:https://mp.weixin.qq.com/s/xVqlgOouFVjK_5vane6g8A

这种方法的缺点也很明显,当有人正在写操作时候,其他人都不能进行相关操作

3.可以使用队列

将用户的操作都存进一个队列中,然后用另一个线程去消费这个队列。这样的好处就是每个用户的操作都可以有效的操作。并且不会有并发操作的情况。


实际场景中应该视需求来决定要用什么办法。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值