订单

276 阅读6分钟

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)体验较差,付款后抢购失败

   c21里面选2

3)预扣库存——库存锁定

 a)将12结合起来,并且预扣除

 b)反作弊——经常不付款的用户数据打上标记

 c)库存数量采用无符号整数,更新为负数则报错

 dupdate t_user_balance 

         set balance = balance-? 

         where openid =? and type = ? And balance-? >=0

   sqlwhen then else跟上述sql语义相同

4)表模型设计

  a)库存扣出流水——生成库存预占流水记录(关键字段:orderNo,skuId,quantity,state(0-预占;1-已扣减;2-已释放)

      先预抢占库存(redis),然后创建订单

      流水表中增加时间戳,有效时间;库存流水表不会太大,毕竟秒杀

      支付成功扣出库存

      取消订单,回退库存

5.redis的库存 incrby和decrby是否存在问题?


1)

6.抢红包


1)

1.读性能


1)缓存

 a)更新缓存时,先写db,后更新缓存。整个方法事务保证

23

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限流