工作中无意发现线上redis内存占用过高,每天差不多30W的hset操作吧,占用了将近200Mredis内存,于是编写测试代码,简单测试了一下,发现还是有地方可以优化的
1、SPRING-DATA-REDIS: SERIALIZER实例
spring-data-redis提供了多种serializer策略,这对使用jedis的开发者而言,实在是非常便捷。sdr提供了4种内置的serializer:
- JdkSerializationRedisSerializer:使用JDK的序列化手段(serializable接口,ObjectInputStrean,ObjectOutputStream),数据以字节流存储
- StringRedisSerializer:字符串编码,数据以string存储
- JacksonJsonRedisSerializer:json格式存储
- OxmSerializer:xml格式存储
其中JdkSerializationRedisSerializer和StringRedisSerializer是最基础的序列化策略,其中“JacksonJsonRedisSerializer”与“OxmSerializer”都是基于stirng存储,因此它们是较为“高级”的序列化(最终还是使用string解析以及构建java对象)。
RedisTemplate中需要声明4种serializer,默认为“JdkSerializationRedisSerializer”:
- keySerializer :对于普通K-V操作时,key采取的序列化策略
- valueSerializer:value采取的序列化策略
- hashKeySerializer: 在hash数据结构中,hash-key的序列化策略
- hashValueSerializer:hash-value的序列化策略
通过对int、string、object进行序列化测试,发现默认的JdkSerializationRedisSerializer序列化后的字节流占用空间是最大的, GenericJackson2JsonRedisSerializer次之,StringRedisSerializer最小,但StringRedisSerializer只可以对string进行序列化
JdkSerializationRedisSerializer
int >>>>>>>>>> 81
string >>>>>>>>>> 10
object >>>>>>>>>> 113
GenericJackson2JsonRedisSerializer
int >>>>>>>>>> 3
string >>>>>>>>>> 5
object >>>>>>>>>> 27
StringRedisSerializer
string >>>>>>>>>> 3
参考:
2、HASH存储什么数据节省内存
通过测试发现数字要比存储字符串要节省内存占用,随意能用数字的尽量使用数字
2.1、Jedis的hset重载方法
java操作redis的底层实现,保存hash到redis是有两个方法的:
2.1.1、JedisCommands接口下的
- Long hset(String key, String field, String value);
2.1.2、BinaryJedisCommands接口下的
- Long hset(byte[] key, byte[] field, byte[] value);
2.2、测试
分别对应字符串和字符流,spring data redis封装的RedisTemplate,只提供了字符流的实现,但是通过调用底层Jedis的实现发现,在插入30W条相同数据,测试结果如下:
HASH subKey 存储一个code = 123 的枚举类型,调用实现BinaryJedisCommands接口
>>>>>>>>>>>>>>>>begin
# Memory
used_memory:713224
used_memory_human:696.51K
used_memory_rss:655416
used_memory_peak:72374824
used_memory_peak_human:69.02M
used_memory_lua:36864
mem_fragmentation_ratio:0.92
mem_allocator:jemalloc-3.6.0
>>>>>>>>>>>>>>>>after
# Memory
used_memory:162700776
used_memory_human:155.16M
used_memory_rss:162599336
used_memory_peak:162700776
used_memory_peak_human:155.16M
used_memory_lua:36864
mem_fragmentation_ratio:1.00
mem_allocator:jemalloc-3.6.0
执行时间: time = 25609 ms
HASH subKey 存储字符串"123",调用实现BinaryJedisCommands接口的方法
>>>>>>>>>>>>>>>>begin
# Memory
used_memory:713224
used_memory_human:696.51K
used_memory_rss:655416
used_memory_peak:72374824
used_memory_peak_human:69.02M
used_memory_lua:36864
mem_fragmentation_ratio:0.92
mem_allocator:jemalloc-3.6.0
>>>>>>>>>>>>>>>>after
# Memory
used_memory:71500776
used_memory_human:68.19M
used_memory_rss:71265080
used_memory_peak:72374824
used_memory_peak_human:69.02M
used_memory_lua:36864
mem_fragmentation_ratio:1.00
mem_allocator:jemalloc-3.6.0
执行时间: time = 23076 ms
HASH subKey 存储字符串"123",调用实现JedisCommands接口的方法
>>>>>>>>>>>>>>>>begin
# Memory
used_memory:713224
used_memory_human:696.51K
used_memory_rss:655416
used_memory_peak:72374824
used_memory_peak_human:69.02M
used_memory_lua:36864
mem_fragmentation_ratio:0.92
mem_allocator:jemalloc-3.6.0
>>>>>>>>>>>>>>>>after
# Memory
used_memory:52300776
used_memory_human:49.88M
used_memory_rss:52273928
used_memory_peak:72374824
used_memory_peak_human:69.02M
used_memory_lua:36864
mem_fragmentation_ratio:1.00
mem_allocator:jemalloc-3.6.0
执行时间: time = 24877 ms
占用内存从155.16M减小到49.88M,节省的内存还是挺多的。
结论
在保存数据的时候尽量调用实现JedisCommands接口的hset方法,不进行序列化操作。