高并发下用户抢购问题简答

高并发下用户抢购问题简答

前言

面试题当中如何处理高并发用户抢购问题可以说是一个十分经典的问题,经常被提及,在这就这个问题写一个简要的解答;

思路

并发的最大瓶颈永远是数据库,MySQL的读写速度是制约并发的最大问题,而抢购之时真正需要写入的用户量实际上是很少的,等于抢购的商品总数.这就要求我们需要把无效的用户排除出.在前期放入有效用户量,一旦产品抢购结束,将活动页改为结束也的静态页面,最大程度的提升服务器相应速度.这里面放入限定数量的用户是最为关键的地方.
redis的高性能读写是现在最为主流的解决方案.下面就简单的介绍一下如何用redis来完成高并发抢购处理.

解决方案

将用户id写入redis列表当中,一旦列表长度达到商品总数,则拒掉后面的用户.

示例代码

Talk is cheap, show you my code.

<?php
/**
 * Created by PhpStorm.
 * User: mc
 * Date: 18/3/21
 * Time: 下午1:53
 */
$user_id = rand(100, 10000); // 模拟用户id
$key = 'user_list'; // 列表名
$redis = new Redis();
$redis->pconnect('localhost', 6379);
$len = $redis->lLen($key); // 获取列表长度
$count = 10; // 商品总数
if ($len >= $count) { // 达到商品总数则停止抢购
    echo '秒杀已结束';
    return '秒杀已结束';
}
$redis->lPush($key, $user_id);  // 将用户id推入列表
echo '恭喜你秒杀成功';
return '恭喜你秒杀成功';

ab测

ab -n 3000 -c 100 http://localhost:8001/     // 模拟3000个请求,100个并发

结果

列表当中的结果确实是10条,满足要求

 LRANGE user_list 0 -1
 1) "466"
 2) "5090"
 3) "8299"
 4) "6436"
 5) "4537"
 6) "9617"
 7) "9291"
 8) "2162"
 9) "1903"
10) "983"
Concurrency Level:      100
Time taken for tests:   4.558 seconds
Complete requests:      3000
Failed requests:        0
Total transferred:      540000 bytes
HTML transferred:       45000 bytes
Requests per second:    658.22 [#/sec] (mean)
Time per request:       151.924 [ms] (mean)
Time per request:       1.519 [ms] (mean, across all concurrent requests)
Transfer rate:          115.70 [Kbytes/sec] received

QPS能够达到658.

问题

上面的代码看似没问题,可是要是老司机一定不难发现代码当中存在一个极大的漏洞.当列表当中有9个值,两个用户同时取得$len,那么这两个用户就会被同时写入列表当中,这样就会出现超卖的问题.而且写列表的方式就需要取数据要在抢购完成之后,这显然不合理.
这需要我们将抢购判断和用户列表拆分开来,redis当中的string有一一自增的api具有原子性,哪怕并发情况下也一定能够保证自增.这能够很好的服务于我们的需求.

代码示例

$user_id = rand(100, 10000); // 模拟用户id
$key = 'user_list'; // 列表名
$redis = new Redis();
$redis->connect('localhost', 6379);
$len = $redis->incr('count');
$count = 10; // 商品总数
if ($len > $count) { // 达到商品总数则停止抢购
    echo '秒杀已结束';
    return '秒杀已结束';
}
$redis->lPush($key, $user_id);  // 将用户id推入列表
echo '恭喜你秒杀成功';
return '恭喜你秒杀成功';

这样就可以避免商品超售了,而且列表的生产和消费可以同步进行,提升业务体验.当然真正的业务当中复杂度远高于这里.

关于避免用户重复抢的简略方案

<?php
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$user_id = rand(1, 15);
if (!$redis->hSetNx('seckill', 'test:' . $user_id, 1)) { // hSetNx函数当key存在时会返回false,不存在时才会被设置成功,返回true
    echo '您已经参加过秒杀请勿重复参加';
    return '您已经参加过秒杀请勿重复参加';
}

$len = $redis->incr('count');
$count = 10; // 商品总数
if ($len > $count) { // 达到商品总数则停止抢购
    echo '秒杀已结束';
    return '秒杀已结束';
}
$redis->lPush('user_id', $user_id);
echo '恭喜您,秒杀成功';
return '恭喜您,秒杀成功';

上面的QPS才600+而原生的PHP在我的电脑下应该能到达3000+,这里面new Redis()创建redis连接耗费了过多的资源

连接复用

<?php
$redis = new Redis();
$redis->pconnect('127.0.0.1', 6379); // redis自带连接复用函数
$user_id = rand(1, 15);
if (!$redis->hSetNx('seckill', 'test:' . $user_id, 1)) { // hSetNx函数当key存在时会返回false,不存在时才会被设置成功,返回true
    echo '您已经参加过秒杀请勿重复参加';
    return '您已经参加过秒杀请勿重复参加';
}

$len = $redis->incr('count');
$count = 10; // 商品总数
if ($len > $count) { // 达到商品总数则停止抢购
    echo '秒杀已结束';
    return '秒杀已结束';
}
$redis->lPush('user_id', $user_id);
echo '恭喜您,秒杀成功';
return '恭喜您,秒杀成功';
Concurrency Level:      100
Time taken for tests:   0.428 seconds
Complete requests:      1000
Failed requests:        0
Total transferred:      180000 bytes
HTML transferred:       15000 bytes
Requests per second:    2337.77 [#/sec] (mean)
Time per request:       42.776 [ms] (mean)
Time per request:       0.428 [ms] (mean, across all concurrent requests)
Transfer rate:          410.94 [Kbytes/sec] received

结束

QPS能够高达2300+完全能够适应一般业务的并发需求了.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,386评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,142评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,704评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,702评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,716评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,573评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,314评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,230评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,680评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,873评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,991评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,706评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,329评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,910评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,038评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,158评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,941评论 2 355

推荐阅读更多精彩内容

  • 三年前当你们坐在夏衍中学的教室里时,你觉得三年的高中生涯好漫长,但一转眼,你今天却以毕业生的身份坐在了这里。三年光...
    呆小五阅读 119评论 0 0
  • 直到今天,2015年秋天的一幕我还是记得那么清楚,那年的凉风与阳光,落叶与花香;那年我决定了重新留头发;还有那年我...
    寒烟西斜阅读 341评论 9 9
  • (本文微小剧透) 上映多天的《非凡任务》口碑越来越趋于两极化,一端说该片是精彩的、弘扬了正能量、人物有情有义的好片...
    嗨右阅读 1,127评论 1 0
  • 该着色器提供类似卡通效果的渲染结果。卡通效果物体的特点是,其表面经常只使用包含两个层次的亮度同一种颜色。尺寸值可以...
    BLENDER阅读 2,334评论 0 8