欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

[日常] 高并发抢购方案的思考

程序员文章站 2022-10-04 11:53:59
经常在面试中被问到如何设计一个高并发环境下的抢购方案,虽然网上的资料已经很多了,但是都是很简单的说了一些用队列之类的套话,没有更详细的细节考虑.被问的实在是太多了,不得已我也仔细想想这些该怎么设计.抛开运维阶段的多层负载均衡,直接只说PHP的业务层面的逻辑. 整个流程如下:web界面点击抢购==>弹 ......

经常在面试中被问到如何设计一个高并发环境下的抢购方案,虽然网上的资料已经很多了,但是都是很简单的说了一些用队列之类的套话,没有更详细的细节考虑.被问的实在是太多了,不得已我也仔细想想这些该怎么设计.抛开运维阶段的多层负载均衡,直接只说php的业务层面的逻辑.

整个流程如下:
web界面
点击抢购==>弹出答题弹窗==>答对判定当前队列长度==>队列未满就进入队列,显示排队中(状态),使用wbsocker实时关注用户状态
            ==>答错再答基本就没戏了返回失败
                           ==>队列满了,返回失败


后端进程
从队列随机取部分用户==>修改他的状态为待支付状态===>用户点击支付进行判定库存量===>支付完成减库存

在整个过程中,用户点击支付的时候也要判定库存,如果没库存就显示失败;用户点击完支付,库存充足,如果隔了很长时间才输入密码支付,这个过程中如果库存没了,要给用户退款;

也就是要么冗余部分商品,要么给用户退款两种方案

 

商品的详细信息
$redis->hget('product', 'num','name');
商品数量设置的是10,其余字段留着存其他信息

用户的状态
1:答题状态
2:排队状态
3:支付状态
4:抢购成功!
5:抢购失败

抢购接口:panic_buy.php
1.判定当前用户哈希是否存在,如果不存在就设置一下
$redis->hsetnx('taoshihan', 'status', 1);
2.查看当前商品的库存,如果为0直接返回失败,更改用户状态为:5
$redis->hget('product', 'num');
3.查看以下队列长度,如果超过1000,直接更改用户状态为:5
$redis->llen('panic_buying');
3.用户进入队列排队,更改用户状态为:2
$redis->rpush('panic_buying', 'taoshihan');

查询状态:status.php
对于进入队列成功的用户才会调用到这个接口
1.判定当前用户状态
$redis->hget('taoshihan', 'status');


支付接口:pay.php
1.判定当前用户状态
$redis->hget('taoshihan', 'status');
2.判定当前商品数量,如果已经小于10个更改用户状态为:5
$redis->hget('product', 'num');
3.支付完成修改当前商品数量
$redis->hincrby('product', 'num',-1);


后端进程:
获取下商品数量,如果等于0,就把所有除去进入支付集合的队列成员更改用户状态为:5
$redis->hget('product', 'num');
$redis->sismember('pay', 'taoshihan');//是否存在于集合中

获取前100个用户,更改状态为:3,把该用户同时进入另一支付集合
$redis->lrange('panic_buying', 0, 99);//获取前100个
$redis->sadd('pay' , 'taoshihan');//插入支付集合


php-redis扩展的哈希结构函数

hdel-删除一个或多个哈希字段
hexists-确定哈希字段是否存在
hget-获取哈希字段的值
hgetall-获取哈希中的所有字段和值
hincrby-将哈希字段的整数值增加给定数字
hincrbyfloat-将哈希字段的浮点值增加给定数量
hkeys-获取哈希中的所有字段
hlen-获取哈希中的字段数
hmget-获取所有给定哈希字段的值
hmset-将多个哈希字段设置为多个值
hset-设置哈希字段的字符串值
hsetnx-设置哈希字段的值,仅当该字段不存在时
hvals-获取哈希中的所有值
hscan-扫描成员的哈希键
hstrlen-获取与哈希中的字段关联的值的字符串长度

php redis扩展的列表的函数

blpop,brpop-删除并获取列表中的第一个/最后一个元素
brpoplpush-从列表中弹出一个值,将其推到另一个列表中并返回
lindex,lget-通过列表从其索引中获取元素
linsert-在列表中的另一个元素之前或之后插入一个元素
llen,lsize-获取列表的长度/大小
lpop-删除并获取列表中的第一个元素
lpush-在列表前添加一个或多个值
lpushx-仅在列表存在时才在列表前添加值
lrange,lgetrange-从列表中获取一系列元素
lrem,lremove-从列表中删除元素
lset-通过其索引设置列表中元素的值
ltrim,listtrim-将列表修剪到指定范围
rpop-删除并获取列表中的最后一个元素
rpoplpush-删除列表中的最后一个元素,将其附加到另一个列表中并返回(redis> = 1.1)
rpush-将一个或多个值添加到列表
rpushx-仅在列表存在时将值附加到列表

php-redis扩展集合的操作方法

sadd 添加一个或多个成员到集合里面
scard, ssize 获取一下集合中成员的个数
sdiff 在n个集合中比较出差集
sdiffstore 和sdiff差不多,但是把差集结果存储在第一个key里面
sinter 返回多个集合的交集
sinterstore 和sinter类似,把结果存储在第一个key里面
sismember, scontains检查参数中的成员是否是集合中的一员
smembers, sgetmembers 获得集合中的所有成员
smove 把集合中的成员从一个集合移动到另一个集合
spop 在集合中随机删除一个并获取到这个成员
srandmember 在集合中随机获取一个成员,并不删除它
srem, sremove 在集合中删除指定成员
sunion 返回多个集合的并集
sunionstore 把多个集合的并集存储在第一个参数key里面