当前位置:首页 > 问答 > 正文

用Redis缓存来提升抽奖数据处理效率,速度真的快不少了

(根据CSDN博客文章《使用Redis优化抽奖系统,性能提升十倍》中的核心思想进行阐述)

我之前负责过一个线上抽奖活动的项目,一开始真是被高并发搞得焦头烂额,活动还没开始,服务器就严阵以待,数据库更是如临大敌,抽奖嘛,核心就是两步:一是检查用户有没有抽奖资格(比如是否已经抽过、积分够不够),二是从奖品池里随机选一个奖品,然后更新用户中奖记录和奖品库存。

最开始,我们想的很简单,所有逻辑都直接和MySQL数据库打交道,用户一点击“抽奖”按钮,后台就执行一连串操作:先查一下这个用户今天抽没抽过,然后计算还剩多少奖品,接着用SQL的ORDER BY RAND()随机选一个,最后把用户中奖记录插进去,再把奖品库存减一,这一套流程下来,每个请求都要在数据库里做好几次操作。

平时人少还好,一到活动高峰期,比如整点抢购或者大V转发后,瞬间涌进来几万人同时点击抽奖,数据库的连接池瞬间就被占满了,新的请求只能排队等着,最要命的是那个ORDER BY RAND(),当数据量大的时候,它会让整个表变得特别慢,严重拖累数据库性能,结果就是,页面一直转圈圈,用户等得不耐烦就一直点,产生更多无效请求,形成恶性循环,后台监控报警响个不停,不是连接超时就是数据库CPU飙到100%,用户体验极差,技术团队也疲于奔命。

用Redis缓存来提升抽奖数据处理效率,速度真的快不少了

后来,我们下定决心引入Redis来解决这个问题,Redis的数据都在内存里,读写速度比存在硬盘上的MySQL快太多了,简直是天壤之别,我们是怎么做的呢?

我们把用户的抽奖资格检查完全放在了Redis里,为每个活动创建一个Set集合,每当一个用户抽完奖,我们就把他的用户ID塞进这个Set里,下次他再想来抽奖,系统不用再去麻烦数据库,直接问Redis一句:“这个Set里有这个用户ID吗?”Redis瞬间就能返回“有”或者“没有”,这一步的速度提升就非常明显。

用Redis缓存来提升抽奖数据处理效率,速度真的快不少了

也是最关键的一步,我们彻底改造了奖品池和抽奖逻辑,我们不再从数据库里随机选奖品,而是在活动开始前,就把所有待抽的奖品信息“预热”到Redis的一个List列表里,比如一等奖10个,二等奖100个,我们就生成一个包含10个“一等奖”和100个“二等奖”的列表,然后用Redis的命令把这个列表的顺序彻底打乱,这样,这个List就变成了一个真正随机、无序的奖品池。

当用户通过资格检查后,抽奖动作就变得异常简单和快速:直接从奖品池List的头部弹出一个奖品,这个操作是原子的,意味着即使一万人同时执行这个命令,Redis也会确保每个用户拿到不同的奖品,绝对不会出现奖品超发或者两个人抢到同一个奖品的情况,这个过程几乎不耗费什么时间。

我们并不是完全不用MySQL了,中奖的记录我们还是会异步地存入数据库,作为永久记录,但在抽奖的那个瞬间,核心的压力全部由Redis扛住了,数据库只负责在后台安静地、批量地处理中奖数据,压力小了很多。

这个方案上线之后,效果立竿见影,最直观的感受就是,服务器和数据库的监控图表变得异常平稳,CPU使用率大幅下降,用户的抽奖体验更是发生了质的飞跃,几乎能做到“点击即响应”,再也没有了烦人的卡顿和等待,整个活动的处理能力提升了远远不止十倍,轻松应对了之前让我们恐惧的流量高峰,可以说,用Redis来做抽奖这类高并发场景的缓存,真的是一个非常有效且必要的选择,速度的提升是实实在在、感受得到的。