抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

Redis解决并发问题

用setnx替代set命令初始化计数器,这确保了一旦A初始化计数器成功,B就不会再去初始化计数器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 获取用户当前的排序
* @return int
*/
protected function getCurrentOrder()
{
$redis = new R("game");
$order = $redis->get(self::$hd_ename);
$number = point_model::getPointLogByTips(self::$hd_ename, 'luckNumber');
if(!$order){
if($number){
$number = $number[count($number) - 1]['point'];
}else{
$number = 0;
}
$redis->setex(self::$hd_ename,120,$number);
}else{
$number = $redis->incr(self::$hd_ename);
}
return $number;
}

我们都或多或少遇到过并发问题。家人因为看电视抢遥控器,这就是一种并发;两个孩子争着玩同一个玩具,这也是并发。在每一次“双11”购物节狂欢的背后,都有一群程序员在严阵以待,这不是一位数的并发,而是成千上万级别的并发。

说了什么是并发,接下来将向大家演示如何用Redis处理一个典型的并发问题。我们选择最常见的商品抢购场景,假定我们有100件商品,参与抢购的用户有成千上万,如何确保我们的商品不被多抢了?

聪明的你应该想到了,可以用计数器来控制,每卖出一件商品,计数器加1,当计数器到达100时,我们的商品就卖完了。程序的工作流程如下图。

img

看起来很完美,但需要通过高并发场景的检验。我们假定有A、B两个进程同时在运行这段程序。

问题1

初始化set计数器:A、B都发现计数器尚未初始化,在A执行“计数器加1”后,B去set计数器,此时计数器的值比正确值少1。(为什么时间差那么大?这在高并发场景中是完全可能存在的)

问题2

计数器加1:A、B都读到计数器的值为99,不满足>=100,两者都抢到了商品,但最终卖掉了101件,显然超卖了。

上面的流程存在两个问题,我们需要对程序流程做一点改进,新的流程如下图。

img

改进1

用setnx替代set命令初始化计数器,这确保了一旦A初始化计数器成功,B就不会再去初始化计数器。

改进2

先对计数器加1,再判断计数器是否>100,如果是,说明超卖了。这确保了即使A、B同时读到计数器的值为99,都去对计数器加1,两者至少有一个得到的结果>100,不会超卖。

从以上的内容我们学习到,如何用Redis处理一个常见的并发场景,这背后还有更多的技术细节值得我们深入了解,期待在下一篇文章中与大家共同学习。

评论