存储在Redis中的数据多大比较合适
背景
本文是工作中应对一个实际业务场景的调研总结过程。在使用 redis 作为数据缓存的时候,一个 key 里面需要存储许多个 map 结构。
缓存需要序列化后存储在redis,这时候就出现了两种方案:
-
将多个map结构分开序列化,N 个 map N个 key:map1:{“foo”:“bar”}; map2: {“foo”:“bar”}… mapN:{“foo”:“bar”},好处是可以对不同 map 增量更新到redis。
-
将所有map整体序列化为一个大key: cache: “{“map1”:{“foo”:“bar”}… “mapN”:{“foo”:“bar”}”,好处是从 redis 读取到数据的时候,反序列化要比方案1快。
过大的数据对 Redis 的影响
哪到底选哪个方案呢?这两个方案对 redis 的影响如何呢?万事不决先 google。
首先看到 Redis 官方的说法是:『A String value can be at max 512 Megabytes in length.』。当然我们不能这么任性的真的扔这么大的数据进去,还觉得没问题。过大的 key 和 value 有两个问题:
-
Redis 是一个内存数据库,如果容量过大的 key 和 value 首先会导致服务器中的内存碎片。这会影响 Redis 的内存分配的效率,进一步导致内存的使用率下降。
-
容量过大的 key 和 value 还有这样几个影响:
a. 这些过大的数据需要更多的时间去传输数据,对网络缓存、带宽都有影响
b. 过大的数据传输可能会导致其他的请求超时:
假设客户端设置的超时时间是 1 秒。如果应用同时请求 ‘A’ 和 ‘B’ 两个 key。通常我们会使用 pipelining 的方法,这时 A 和 B 会逐个等待服务器对它们的响应。如果 A 的响应数据过大,它可能会吃掉其他请求的超时时间。如下图例子,如果 A 的响应数据过大,它会吃掉其他请求的超时时间
c. 如果是经常访问的数据,以上的影响会更加经常发生。
所以当 key 和 value 过大的时候,可能更需要优化数据结构,将 key 和 value 的值都变小。
『过大』的定义是什么
然而到底多大的 key 和 value 算大?我们上面描述的两种方案到底哪个好些?
这个时候就需要使用一些性能测试工具来评估一下实际的影响,恰好 Redis 有官方的 redis-benchmark工具。它类似 Apache 的 ab测试。
redis-benchmark 使用示例:
redis-benchmark -n 1000000 -t set,get -P 16 -q
SET: 718390.81 requests per second
GET: 864304.25 requests per second
回到对应到上面说的两个方案,使用 SCRIPT LOAD 命令,利用 lua 脚本构造我们想要的数据。这里使用 10000 次操作,每100次一个pipeline 命令提交。
方案1,多个map分别序列化后存储:
redis-benchmark -n 10000 -P 100 -q SCRIPT LOAD "local bulk = {}; local s = '{\'foo\':\'bar\',\'foo1\':\'bar1\',\'foo2\':\'bar2\',\'foo3\':\'bar3\'}'; for i=0,10,1 do bulk[i] = s; end; redis.call('HMSET', 'foo', unpack(bulk));"
结果:> 370370.38 requests per second
方案2,一个大string
redis-benchmark -n 10000 -P 100 -q SCRIPT LOAD "local plain = '' local s = '{\'foo\':\'bar\',\'foo1\':\'bar1\',\'foo2\':\'bar2\',\'foo3\':\'bar3\'}'; for i=0,10,1 do plain= plain..s; end; redis.call('HMSET', 'foo', 'cache', plain);"
结果:> 208333.33 requests per second
可以看到,我们开头提到的业务场景下,方案1要快了不少。
那对于一般的场景结论是什么呢?到底什么时候需要拆分,什么时候用一个大的序列化 string 节约CPU资源呢?
结论
建议是大于 50k 的数据,就优先选择拆分吧。具体情况可以结合上面的流程,逐个分析:
- redis 本身剩余存储空间够吗?
- 这类数据频率大不大,带宽和超时会是问题吗?
- 用redis-benchmark跑一下实际业务场景,看性能是否满足需求。
本文是搬运我自己博客到 CSDN 的第一篇,原文点此。