红包系统架构设计

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 红包系统架构设计

红包系统架构设计


软件系统设计,第一步,确定关键需求。

需求分析

比如红包这个系统,需要有如下:

  1. 包红包
  2. 发红包
  3. 抢红包
  4. 拆红包
  5. 不能抢超,也就是说红包个数,金额是有限的,不能超的。
  6. 支持高并发,例如1亿用户凌晨12点开始抢红包。

上面的 4 方面就是关键需求。

  • 包红包:系统为每个红包设置一个 id ,然后将红包发送个用户,这里需要设置 红包金额,红包个数,要发送的用户,存储这些信息。
  • 发红包,设置完红包参数后,微信支付,完成付款,然后收到付款成功通知,红包系统更新红包订单状态,更新为已支付,并写入红包发送记录表。这样用户可以将用户的红包信息和红包的收发记录发出,红包系统调用微信通知,将红包信息发送到微信群。
  • 抢红包,微信群用户收到红包后,点开,红包系统会校验红包是否被抢完,是否过期。
  • 拆红包,拆红包时,要先查询红包订单,判断是否可拆,计算本次拆的红包金额,记录抢红包流水。

流程如下:

image.png

几个问题


抢红包那一刻 时间短,瞬时用户量大

  1. 为啥有时候抢到了红包,点开后就是没有,说明抢和拆是分开的,抢只能说明交易被受理,扣减库存成功才能真的说被抢到了。
  2. 红包金额怎么算?
  3. 红包金额什么时候算?
  4. 如何计算红包被抢完了?
  5. 如何保证每秒8w的写入?
  6. 如何防止恶意请求?
  7. 领一个红包就更新数据么?
  8. 红包如何入账?


针对上面的问题,可以做出如下思考:


秒杀场景是读多写少的。

第一反应,会想到 Redis 计数,红包个数减到0 表示红包抢完。但是有个问题,Redis 有 decr 原子递减,redis 原子递减会变成负数,还是有超抢问题。

Redis  内嵌了lua 支持,解决了长久以来多个命令组合的问题。有点类似事务,有一定原子性,可以用完成一定事务性操作,可以将扣减操作写在lua脚本里,然后 Redis 去执行,扣减到0 返回 false。这样避免超抢超卖。

但是随之而来又有一个问题? Redis 并发很高么,上亿用户同时请求怎么办 ,QPS 怎么也要到10w。

那就得 对 Redis 做文章:

Redis 集群,主从同步,读写分离

假设这时候有黑客或黄牛怎么办,在不断的用脚本点击链接或者红包,怎么避免?

链接不能写死,用MD5 进行链接加密,后台验证之后才能通过。否则认为是攻击。

通过 Redis 或者链接加密的方式 已经把大部分交易拦住了,但是这短时间还是有大部分交易,比如凌晨12点10亿人抢5000w个红包,现在放了5000w个请求进来,并发还是很高, 怎么办?这5000w 个人说明抢到红包了,接下来就是拆红包了。

这个时候就需要流量消峰,会想到使用 MQ 队列来排队处理用户请求。阀值或者说库存以 Redis 为主,需要扣减库存。

流量削峰了,使得流量平缓,慢慢处理。

拆红包过程就是主要的业务逻辑,主要包含3步骤:

  1. 锁库存
  2. 插入秒杀记录
  3. 更新库存

拆红包过程:扣减库存和秒杀记录的操作,更新库存就是更新红包发送的订单,还有就是写入秒杀记录就是写入红包领取的信息流水。还需要以用户为中心记录用户整体领了多少红包。最后调用支付系统将拆红包后的金融转入用户余额,成功之后更新抢红包的订单状态为转账成功。

为提高性能,往往将库存信息放到内存 Cache 中, 内存Cache 操作成功后给Server 返回成功,然后异步落 DB 持久化。


基于上面的问题,考虑下微信红包怎么设计?


微信红包用户发一个红包时,有一个系统 ID ,作为这个红包的唯一标识,接着有 包,发 ,抢,拆 都与 ID 关联。红包系统根据 ID ,按照一定规则,垂直上下切分,切分后一个垂直链条上的逻辑 server 服务器,包含一个DB ,同一个红包 ID 的所有请求均路由到同一个 Set 内处理。这样系统之间相互独立,相互解耦。将海量事务操作化成小量。

image.png

同一个请求如何路由到同一个逻辑 Server ?

同个红包 ID 的所有请求,按照红包 ID  路由到同一个 Set ,一个逻辑 Server 会存在多个 Server ,但是 这些 Server 共享同一个 DB。

image.png

单点 Server 如何请求排队


当红包 ID 路由到同一台Server 上时,请求被受理,此时需要进入队列。串行的执行。

image.png

memcached 控制并发


memcached 是用来控制并发的,当队列过载的时候,拒绝服务。具体来说,利用memcached的CAS原子增操作,控制同时进入DB执行拆红包事务的请求数,超过预先设定数值则直接拒绝服务,用于DB负载升高时的降级体验。


双维度分库分表


根据红包ID 的hash 值的基础上做多库多表。在此基础之上,再根据,红包订单的时间进行分表,简单的来说,就是形成 db_xx.t_y_dd 的“库.表”的结构,其中xx|y是红包ID的hash值后三位,dd的取值范围在1~31,代表一个月天数最多31天。

server 还需要进行 包,发,抢,拆,几个操作。可以分别封装成单个微服务,进行分布式部署,提高处理能力。


总结


微信红包系统在解决高并发问题上的设计,主要采用了SET分治、请求排队、双维度分库表等方案,可以使得红包系统Server适应高并发,同时Server采用 Redis 库存扣减防止超卖,异步入 DB 等方式提高并发能力。


参考资料

相关文章
|
缓存 算法 NoSQL
红包场景的系统设计和实践
红包场景的系统设计和实践
461 0
红包场景的系统设计和实践
|
消息中间件 存储 Java
kafka零拷贝
kafka零拷贝
234 0
|
存储 设计模式 缓存
通用点赞设计思路
点赞作为一个高频率的操作,如果每次操作都读写数据库会增加数据库的压力,所以采用缓存+定时任务来实现。点赞数据是在redis中缓存半小时,同时定时任务是每隔5分钟执行一次,做持久化存储,这里的缓存时间和任务执行时间可根据项目情况而定。
2687 2
|
NoSQL Redis 数据库
阿里云Redis集群版简要介绍
产品简介 云数据库 Redis 提供集群版实例,轻松突破 Redis 自身单线程瓶颈,可极大满足对于 Redis 大容量或高性能的业务需求。 云数据库 Redis 集群版内置数据分片及读取算法,整体过程对用户透明,免去用户开发及运维 Redis 集群的烦恼。
13716 0
|
NoSQL 关系型数据库 MySQL
排行榜系统设计:高并发场景下的最佳实践
本文由技术分享者小米带来,详细介绍了如何设计一个高效、稳定且易扩展的排行榜系统。内容涵盖项目背景、技术选型、数据结构设计、基本操作实现、分页显示、持久化与数据恢复,以及高并发下的性能优化策略。通过Redis与MySQL的结合,确保了排行榜的实时性和可靠性。适合对排行榜设计感兴趣的技术人员参考学习。
1581 7
排行榜系统设计:高并发场景下的最佳实践
|
消息中间件 中间件 程序员
分布式事务大揭秘:使用MQ实现最终一致性
本文由小米分享,介绍分布式事务中的MQ最终一致性实现,以RocketMQ为例。RocketMQ的事务消息机制包括准备消息、本地事务执行、确认/回滚消息及事务状态检查四个步骤。这种机制通过消息队列协调多系统操作,确保数据最终一致。MQ最终一致性具有系统解耦、提高可用性和灵活事务管理等优点,广泛应用于分布式系统中。文章还讨论了RocketMQ的事务消息处理流程和失败情况下的处理策略,帮助读者理解如何在实际应用中解决分布式事务问题。
1165 6
|
NoSQL 算法 关系型数据库
Redis系列-16.腾讯经典面试题-如何做一个迷你版的微信抢红包呢?
Redis系列-16.腾讯经典面试题-如何做一个迷你版的微信抢红包呢?
301 0
|
存储 自然语言处理 Java
这些年背过的面试题——ES篇
本文是技术人面试系列ES篇,面试中关于ES都需要了解哪些基础?一文带你详细了解,欢迎收藏!
|
关系型数据库 MySQL 索引
MySQL InnoDB中的锁-插入意向锁(Insert Intention Lock)
MySQL InnoDB 插入意向锁 Insert Intention Lock
4082 0
MySQL InnoDB中的锁-插入意向锁(Insert Intention Lock)
|
存储 NoSQL Redis
redis zset底层数据结构
zset底层存储结构  zset底层的存储结构包括ziplist或skiplist,在同时满足以下两个条件的时候使用ziplist,其他时候使用skiplist,两个条件如下: 有序集合保存的元素数量小于128个 有序集合保存的所有元素的长度小于64字节  当ziplist作为zset的底层存储结构时候,每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员,第二个元素保存元素的分值。
15619 1