1.主流程
1)创建订单(返回回调地址)
2)app内置支付sdk付款
3)回调通知
2.订单创建
1)参数校验,优惠券的合法性;专辑id是否存在,用户已购和专辑id是否冲突
合理使用校验器(validator+注解)
2)价格校验,使用虚拟币是否余额不足;这个过程未做虚拟币锁定
3)已支付完成,未创建的订单
订单号校验,paystatus=1和orderstatus!=3,同一个用户同一个产品存在(4小时内的数据)
4)同一个商品已创建,未支付订单。可以重复运用订单号,保证一定程度的幂等(并发/重复提交时无法保证,此处无分布式锁)
5)返回参数:appId,交易金额,订单号,回调地址
余额,expireTime,签名(根据支付给定key)
6)电商项目,创建订单时会锁定库存;i音乐属于虚拟产品,无限库存
3.回调通知
验证签名!!
1)订单id+openid => 订单,订单表内订单状态为待支付
2)更新订单状态为支付成功。并回填支付侧交易号,支付时间
3)订购产品——幂等操作
a)orderId+openid => 分布式锁(自旋) 保证一个线程执行
分布式锁前置——创建订单时,单个线程,保证调用一次支付扣费(可能存在用户扣款失败,在需要抢占库存的问题业务上)
openid+orderid 唯一键
分布式锁后置——回调时创建,先扣费用,然后再保证幂等
b)退款 查询退款流水表
c)余额管理
虚拟币的充值(充值有账户表+充值流水表,幂等保证,事务保证)
虚拟币的消费(消费有账户表+消费流水表,幂等保证,事务保证)
注:扣减sql防止超扣(未使用乐观锁)
update t_user_balance
set balance = balance-?
where openid =? and type = ? And balance-? >=0
openid和type 联合索引,上述sql走索引
优惠券调用dubbo接口幂等保证
4)通知第三方——第三方下单(幂等保证)
5)异常记录
订单状态异常——>后台监控
订单异常表——> 定时任务重试
6)成功后记录用户已购表,刷新缓存
4.订单退款——逆向操作
1)t_payment_refund_record
order_id 业务订单号
payment_order_no 支付侧业务订单号
refoundId(自生成uuid)
payment_refound_no 支付退款id(待回填)
2)调用支付侧http接口
退款回调后,返回退款id写入退款记录表
4.连续付费
1)t_payment_refund_record
order_id 业务订单号
5.库存问题
1)下单时扣除库存
a)下单后存储最精准,但可能下单后不付款
b)秒杀时,不建议
2)支付回调时扣除库存
a)并发较高时,库存没了
b)体验较差,付款后抢购失败
c)2和1里面选2
3)预扣库存——库存锁定
a)将1和2结合起来,并且预扣除
b)反作弊——经常不付款的用户数据打上标记
c)库存数量采用无符号整数,更新为负数则报错
d) update t_user_balance
set balance = balance-?
where openid =? and type = ? And balance-? >=0
sql,when then else跟上述sql语义相同
4)表模型设计
a)库存扣出流水——生成库存预占流水记录(关键字段:orderNo,skuId,quantity,state(0-预占;1-已扣减;2-已释放)
先预抢占库存(redis),然后创建订单
流水表中增加时间戳,有效时间;库存流水表不会太大,毕竟秒杀
支付成功扣出库存
取消订单,回退库存
5.redis的库存 incrby和decrby是否存在问题?
1)
6.抢红包
1)
1.读性能
1)缓存
a)更新缓存时,先写db,后更新缓存。整个方法事务保证
2)
3)
2.写性能——几十万的请求抢夺几百个库存
1)热点数据迁移至单独的库
2)分库分表,无锁化
db层面承接最后的压力
3)库存的扣减
a)简单的库存扣减可以使用redis
不存在锁定,sku库存的联动,不需要事务
b)较为复杂的场景仍然需要mysql
4)mysql 事务(整个过程行锁占用,时延较长,连接数不够用)
a) 同一个连接
更新库存
网络通信
订单状态修改
网络通信
commit/roback
注:先执行 B,再执行A,性能优化
5) mysql的死锁检测——对4的延伸和补充,说明行锁并发度较高时性能较低
并发系统中出现资源的循环依赖,陷入无限等待
a)innodb_lock_wait_timeout 超时,默认50s
b) innodb_deadlock_detect ,死锁检测 默认on
Wait-for-graph 算法,检测在同一个锁等待的事物是否形成环
行锁获取前会进行死锁检测,时间复杂度o(N),CPU消耗较高,每秒执行事务很少
c)回滚,根据事务的权重(更新/插入的行数,锁的数量)
表锁用双向列表管理
行锁通过哈希管理
6)mysql的性能优化
a)应用层排队。
按照商品维度排队,也就是按照库存id来做排队,加锁。控制单个商品占用连接的数量;
b)mysql层做排队
阿里直接mysql二次开发,mysql侧争夺行锁前串行或者update后可以自动提交;
c)存储过程,减少网络通信 ,事务开启/提交的时间
d)全链路的压测
3.流量漏斗
1)基于时间片的削峰-增加答题功能,防止秒杀器
2)静态数据部署cdn——动静分离
3)前台系统读redis——缓存
4)无效请求的过滤——ip,同一个用户秒杀次数,商品库存
5)后台写分库分表——数据库
6)保证不超买
a)有效请求
b)数据库更新 库存>0
如何设计一个系统
整体思路
1)梳理技术架构前,先梳理业务架构(产品架构)
2)数据结构ER图,实体关联图
3)技术预研,比如单点登录现有的开源方案
4)架构设计——为业务架构和业务量级服务
根据核心链路的梳理/核心模块/数据体量/并发程度;技术选型/模块拆分
以学生账户系统为例
a)性能
10w人,每天同时访问的人数在100人,web服务器使用nginx
b)可扩展性
学生系统功能稳定
c)高可用
宕机2小时,对学生的管理影响不大;(负载均衡/服务降级)
数据丢失后难以恢复,需要学生逐条写入;(同机房主备;机房故障,跨机房同步方案)
d)安全性
Nginx的访问控制/账号的密码管理
架构设计
1)架构基础
2)高性能架构模式
3)高可用架构
降级/削峰
a)限流。前端答题,动画限制;后端集群限流
b)降级(开关)。牺牲一致性,保证流量无损
C)容灾。建立多套相同的系统,提供7*24。同城双活是主流,成本低,难度低
4)可扩展性
秒杀
1)纯mysql性能低,分库分表解决一部分问题
2)库存直接扣减redis(lua脚本)
3)限流,保护系统;排队人数较多,请重试
事务(流水表+redis),异步同步mysql的库存表
insert写入快(顺序写入),update写入慢
4)分布式锁需要加吗。如果是
5)秒杀是热点数据问题;
a)读热点。增加热点数据的副本数量/本地缓存;
B)写热点。单sku限流