社区点赞业务缓存设计优化探索

背景

演进探索

v1.0版本

功能需求

实现方案

缓存结构如下:

cId => [uid1,uid2,uid3...]

流程图如下:

主要问题

这个版本的方案存在比较多待优化点。

第二、缓存存储数据结构上为Key/Value结构,每次使用时需先从Redis查询,再反序列化成PHP数组,in_array()和count()方法都有比较大的开销,尤其是查询热门动态时,对服务器的CPU和MEM资源都有一定浪费,对Redis也产生了比较大的网络带宽开销。

v2.0版本

功能需求

1、解决热点内容缓存击穿的风险。

2、优化代码层面对缓存数据序列化和反序列化导致的服务器资源消耗。

实现方案

缓存结构如下:

cid => [uid1,uid2,uid3...]

流程图如下:

主要问题

v3.0版本

功能需求

1、解决V2.0版本中缓存大Key风险。

实现方案

缓存结构如下:

cid_slice1 => [uid1,uid11,uid111...] 
cid_slice2 => [uid2,uid22,uid222...] 
cid_slice3 => [uid3,uid33,uid333...] 
...

流程图如下:

主要问题

... ...

总结一下,较高的服务器负载、Redis请求量、DB请求量。非常大的Redis资源使用(几十GB)。

所以我们需要一个更优的方案,解决优化以下现象:

1、Feed流场景下批量查询内容任务放大导致的服务器负载,Redis请求,DB请求放大现象。

2、缓存更高效的存储和使用,降低缓存整体的使用量。

3、缓存更高的命中率。

4、区分冷热数据。

实际Feed场景下的实现逻辑:

V4.0版本

功能需求

1、能解决Feed流场景下批量查询流量放大现象。

3、缓存结构要简单易维护,使业务实现要清晰明了。

实现方案

设计思路:

1、批量查询任务之所以放大是因为之前的缓存是以内容为维度进行设计,新方案要以用户为维度进行设计。

3、旧方案在维护缓存过期时间和延长过期时间的设计中,每次操作缓存都会进行ttl接口操作,QPS直接x2。新方案要避免ttl操作,但同时又可以维护缓存过期时间。

4、缓存操作和维护要简单,期望一个Redis接口操作能达到目的。

缓存结构如下:

在实际业务场景流程如下:

通过流程图,我们可以清晰看到, 上游Feed流,一次批量查询请求,没有了循环逻辑,最优情况下,只有一次Redis操作,业务实现也非常简单明了。

优化结果

优化前后Redis查询量QPS日常峰值下降了20倍。

优化前后接口平均RT下降了10倍。

优化前后DB查询量QPS日常峰值下降了6倍。

优化前后缓存节省了16G左右存储空间。

后续

优化不会结束,技术不会停止,技术方案会随着业务的演进而演进。

总结

并且随着业务的不断发展迭代,会涌现出更多的场景和困难,我们一直在优化探索的路上。

文|慎之

经验分享 程序员 微信小程序 职场和发展