凭证卷发放流程优化方案

129 阅读2分钟

背景

上周线上有多个大促活动,创建了多个凭证卷模版,并导入了大量劵码,其中有多个卷模版下的劵码超过50w+, 在发放劵的时候,会先查询一次卷码库存,其中劵模版ID是分库分表键,所以在统一模版下的数据量过多了以后,就会导致慢sql

select * from t_coupon_code from templateId = #{templateId} and status = "未发放" limit 20;

Q&A

Q1: 为什么sql 要写limit 20,直接查一个不行吗?

因为发放过程存在并发问题,玩法系统的RT有要求,不能超过100ms,所以营销获取可用劵码的时候多拿了几条,然后随机返回一条,减缓并发过程中的冲突,就算两个用户拿到了同一个劵码了,在后续的更新流程当中,一定会有一方更新失败,所以不存在两个用户抢到同一张劵码的情况

Q2: 为什么同一模版下,劵码量大了会导致慢SQL?

虽然sql,能够命中索引,但是后面的字段区分度不大,会扫描整个索引树,如果还不懂,建议回归校园

优化方案

引入Redis的set结构

为什么是set不是list?

List: 也是能够实现这样的效果的,但是需要防止出现重复元素,因为劵码是不会重复的

Set: 不仅能够去重,而且也能随机弹出集合内的元素(时间复杂度O(1))

引入redis以后如何发放劵码

这里只是简单给个思路,我很懒不想画流程图了,在公司天天敲,😡

  1. 从redis中pop出
  2. 如果pop的元素不是空,查询数据库 并返回结果
  3. 如果是空,判断当前劵码是否没有库存了(redis中的一个标记位)
  4. 如果有库存 加载DB中的数据到缓存,如果没有加载到,说明没有库存了,缓存中记录标记

引入redis以后数据一致性如何保证

会出现短暂的不一致场景:运营删除劵码库存的时候

但是不会有影响,应为pop,出一个已经被删除的元素以后,后续走正常的发放动作,这个过程中更新DB会出现问题,从而避免了并发问题