1 需求概述
近期在业务中接手开发类似积分兑换的功能。
需求简单描述如下: 用户可以在app中完成指定任务后,以抽奖的形式领取随机数额的积分。
比如说10个积分,每次领取到的积分是有过期时间的,只能在一定时间内使用该积分。用户可以拿着领取到的积分按照一定比例兑换优惠券(or下载券),比如说用了5个积分去做兑换。
2 需求关键点明确
整个需求的简化场景如上描述,核心的问题点是2个
- 积分如何拆分
- 如何优先使用快过期的积分
- 积分兑换明细
3 方案对比
3.1 方案一 - 目前线上方案
- 方案描述:
- 将用户的积分按最小值1拆分,用户有N个积分,表中就有N行记录。
- 涉及存储表
- 积分表
- user_id
- num 这里的num始终为1
- status
- start_time
- end_time
- 用户领取积分记录表
- user_id
- nums 当次获取的积分数
- 创建时间
- 用户兑换记录表
- user_id
- consume_num 消耗的值
- exchange_num 兑换后的值
- 兑换时间
- 积分表
- 核心接口逻辑梳理
- 获取积分逻辑
- insert into 用户领取积分记录表
- 批量insert into 积分表
- 兑换积分逻辑
- select count(num) from 积分表 where status = 0 and end_time > now 获取用户剩余的积分
- 根据用户输入积分计算可兑换的券量
- 异步发送下游业务进行剩余积分和消耗积分的再次对比,接着对积分表order后进行update,确保优先利用最快过期的豆
- 写入用户兑换记录表
- 获取积分逻辑
- 方案优劣势
- 缺点:数据量太大,整个数据的波动依赖于发放积分的多少,整体感觉这个场景下的这种设计思路不太合理,随着积分的翻倍,促销活动的多发,这个数据量简直是爆炸性增长
- 优点:个人感觉没有特别明显的优点,最大的优点是省去了解决分拆的复杂度
3.2 方案二 - 单笔领取存储维度
- 方案描述
- 通过存储每个积分使用的量,对用户的多笔进行分拆计算
- 涉及存储表
- 用户积分详情表
- user_id
- nums 积分数
- used 使用积分数
- start_time
- end_time
- 用户兑换记录表
- user_id
- consume_num 消耗的值
- exchange_num 兑换后的值
- 兑换时间
- 用户流水明细表
- user_id
- type 增加积分 or 消耗积分 可扩展对应场景
- nums
- detail 记录详情 包括消耗的是哪个id下的积分?增加的是哪个id
- 创建时间
- redis
- 存储用户总的积分值 可离线运行 日级别更新
- 用户积分详情表
- 核心接口逻辑梳理
- 获取积分逻辑
- INSERT into 用户积分详情表 user_id, nums, used=0, end_time=now() + 7days
- 兑换积分逻辑
- 先获取所有可用积分,并按过期日期排序:SELECT xx from 用户积分详情表 where user_id and end_time > now(), nums > used ORDER_BY end_time
- 判断sum( nums - used ) 看可用积分够不够
- 如果可用积分是够的,就按照order好的依次更新各条记录中的 used 的值,直到本次使用积分扣除完毕
- 获取积分逻辑
- 方案优劣势
- 缺点:在涉及分拆时代码处理会稍复杂一些
- 优点:库表的容量可控,不会存在极速增长的情况
4 说明
上述文档没有说到以下几点,但是需要关注
- 缓存
- 事务
缓存的话,看业务的量,如果并发不大,个人认为数据库完全可以抗住 事务的话,可以采用 柔性事务或者是补偿性的来,也可以直接使用日常的大事务,只要保证并发量和操作稳定性。