初学Redis,于是便想写一个高并发的项目,最开始只能达到并发量1,后来增加并发,出现各种问题,我又采取各种办法来增大并发量,在慢慢增大并发量的过程中,我也在慢慢成长,在追求成功的过程中,越来越兴奋,成长的感觉,也是人生中最美妙的事情!
需求:一个抢课系统,使用Redis,course_1为课程的人数,user_1为哪些用户选择了课程,采用数组形式,由于ab压测,我不知道怎么传动态参数,于是我变用的随机数代替。
首先先来看代码:
$redis = my_redis();
if($redis->get('course_1') <= 0){
$this->apiReturn(303,'课程已被抢完');
}
$redis->decr('course_1');
$data = $redis->get('user_1');//获取key [value]
$data = json_decode($data,JSON_UNESCAPED_UNICODE);
array_push($data,mt_rand(1,100));
$data = json_encode($data);
$redis->getset('user_1',$data);
抢课人数结构
课程人数结构
第一次测试,总请求量100,同时并发量为1,课程人数100:
ab -n1000 -c50 http://127.0.0.1/lession_admin/public/index.php/api/test
-n一共多少次请求,-c瞬间的并发数
注意,localhost要改成127.0.0.1,不然会被积极拒绝(到底有多积极???)。
ab -n100 -c1 http://127.0.0.1/lession_admin/public/index.php/api/test
结果:课程数为0(正常)
[94,76,85,79,87...].length = 100(正常)
第二次测试,总请求量100,同时并发量为5,课程人数100:
结果:课程数为0(正常)
[94,76,85,79,87,32,64...].length = 95(异常)
我们发现少了5个人,那么会不会跟总请求量有关,于是我们改一下请求量
第三次测试,总请求量300,同时并发量为5,课程人数100:
结果:课程数为0(正常)
[94,76,85,79,87,32,64...].length = 89(异常)
很惊讶,每秒钟同时5的并发量,功能就已经产生异常了,这就得考虑代码问题了。
我们看到,每秒钟5个人访问,就出了问题,那么肯定是代码问题,我们看到,每次-1,这种思路不可取,非常容易出问题,于是我换一种思路,判断已经选课的人数,有没有超过总人数,如果超过了,就不能再选课,实验后发现,我这种做法是正确的,并且取得了很好的效果,Redis里的字符串(我刚开始因为看到可以自减,所以选择字符串结构,现在发现自己是错的),也改为了set结构,因为它有去重的功能:
$courseId = 1;
$id = I('id');
$id = (int)$id;
$redis = my_redis();
这里的if是重点,判断人数100,有没有小于抢的人数,如果小于,那么就返回失败,这里是精华,之前自减1,真的太多漏洞了。
if($redis->smembers('course_' . $courseId)[1] <= count($redis->smembers('user_' . $courseId))){
$this->apiReturn(303,'课程已被抢完',["num"=>count($redis->smembers('user_1'))]);
}
$result = $redis->sismember('user_' . $courseId, $id);
if ($result) {
$this->apiReturn(304,'您不能重复抢课');
}
$result_Add = $redis->sadd('user_' . $courseId, $id);
if ($result_Add) {
$this->apiReturn(200,'抢课成功');
}
$this->apiReturn(305,'抢课失败');
课程信息
线程,一秒请求十万
随机变量1-500
http请求带参数
采用set结构,可以去重,在这个业务场景下,比list好用
第四次测试得出:
10000并发数据正常,也是100条数据,并没重复。
50000并发数据异常,超出3条数据,并没重复。
100000并发数据异常,超出1条数据,并没重复。
从并发量5异常,到并发量一万正常,就几行代码解决了这个问题,代码真的是很奇妙的东西。
搭建分布式:
redis-trib.rb create --replicas 1 127.0.0.1:6379 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 127.0.0.1:6385
检测
redis-trib.rb check 127.0.0.1:6379
如果说第二次启动报错的话,
错误信息为:
[ERR] Node 127.0.0.1:6379 is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0.
下面我开始用分布式锁,来解决并发的问题。