一、场景
对于查库较慢,导致列表加载较慢的应用场景,可以使用缓存来加速列表查询。方案:10页数据做一次缓存,并设置过期。查询单页数据时,如果在缓存的10页中,则从缓存中读取页;如果不在,则从库中读取10页,并加载到缓存。
二、方案
1、用 string 来批量缓存 100 条数据
(1)缓存全部数据,1次网络io写
mset user_query_1 json({}) \
user_query_2 json({}) \
...
user_query_100 json({})
(2)获取一页数据,1次网络io读
mget user_query_1 user_query_2 ... user_query_10
(3)设置过期时间,100次网络io写
expire user_query_1 60
expire user_query_2 60
...
expire user_query_100 60
(4)更新单条数据,1次网络io写
set user_query_1 json({})
2、用 hash 来逐条缓存 100 条数据
(1)缓存全部数据,100次网络io写
hmset user_query_1 id 1 name tom1
hmset user_query_2 id 2 name tom2
...
hmset user_query_100 id 5 name tom3
(2)读取一页数据,10次网络io读
hvals user_query_1
hvals user_query_2
...
hvals user_query_10
(3)设置过期时间,100次网络io写
expire user_query_1 60
expire user_query_2 60
...
expire user_query_100 60
(4)更新单条数据,1次网络io写
hmset user_query_1 id 1 name tom1
3、用 hash 来分页缓存 100 条数据
(1)缓存全部数据,1次网络io写
hmset user_query_1_100 page_1_10 json({id:1, name:tom1},{id:2, name:tom1},...,{id:10, name:tom10}) \
page_2_10 json({id:11, name:tom11},{id:12, name:tom11},...,{id:20, name:tom20}) \
...
page_10_10 json({id:91, name:tom91},{id:92, name:tom92},...,{id:100, name:tom100}) \
(2)读取一页数据,1次网络io读
hget user_query_1_100 page_1_10
(3)设置过期时间,1次网络io写
expire user_query_1_100 60
(4)更新单条数据,即更新单页数据,1次网络读单页,1次网络写单页
hget user_query_1_100 page_1_10
hset user_query_1_100 page_1_10 json({})
4、用 list 来批量缓存 100 条数据
(1)缓存全部数据,1次网络io写
rpush user_query_1_100 json({id:1, name:tom1}) \
json({id:2, name:tom2}) \
...
json({id:100, name:tom100})
(2)读取一页数据,1次网络io读
lrange user_query_1_100 0 9
(3)设置过期时间,1次网络io写
expire user_query_1_100 60
(4)更新单条数据,1次网络io写
lset user_query_1_100 0 json({})
三、总结
1、不更新/删除缓存,仅设置过期
(1)网络io次数要尽量少。首先读性能要高,一次批量读;其次是写性能,一次批量写,一次设置过期时间。相比之下,hash 分页和 list 比其它的好一些。
(2)读数据时,索引效率要高。相比之下,表面上 hash 分页要比 list 好一些,因为 hash 查询是 o(1),list 是双向链表,查询是o(n)。但是由于 list 长度是 100,因此相差不大。
(3)页大小变化。相比之下,list 比 hash分页 要更方便方便一些,因为可以取到任意范围。但是常见的页大小为10、20、50等,一般都为 10 的倍数,所以 hash分页 也可胜任。
2、考虑更新/删除缓存
(1)更新处理。list 比 hash分页 更方便一些,不过影响不大,因为这里只多了一次网络读io,而且一般对更新的及时性要求也不会太高。
(2)删除数据。如果考虑到这一点,可能会比较麻烦,没有特别好的方案。相比之下,list 要好一些,在 100 条数据内,即便删除了几条数据,仍可以范围索引10条数据,只是可能后面页不足10条。
当后面页数据不足时,可以考虑从下一个缓存块(后一个10页)补数据到前一个缓存块。
最好是能在删除数据时,就将后面的块的数据补充到前面的块,这样在查询时,能够保证每个块内的数据都是完整的。