Redis开发规范解析(一)--键名设计
去年我写过一个《阿里云Redis开发规范》,在网上转载很多,但其实说心里话,我并不认为写的多好,受制一些客观因素和篇幅,有些不够细致和深入,所以想在公众号里详细解析下,希望对大家有帮助。
本篇是第一篇:由键名设计想到的SDS内存优化
原文
1. key名设计
◆(1)【建议】: 可读性和可管理性
以业务名(或数据库名)为前缀(防止key冲突),用冒号分隔,比如业务名:表名:id
◆(2)【建议】:简洁性
保证语义的前提下,控制key的长度,当key较多时,内存占用也不容忽视,例如:
◆(3)【强制】:不要包含特殊字符
反例:包含空格、换行、单双引号以及其他转义字符
解析
上面这些内容本来没什么好说的,但是这个就和做菜“放盐少许”一样,key多长才算最佳呢?我们从之前遇到的一个问题讨论下。
一. 1个问题
之前公司有个同事找我,说他申请两个集群,双写两个集群,但是写满后容量是这样的:
遇到此类问题,我还是习惯把老图翻出来:
此类问题有很多种可能:
◆自身内存:没多少,几百KB
◆Lua内存:没用
◆缓冲内存:AOF和复制缓冲配置一致,客户端缓冲不存在,客户端已经停了。
◆对象内存:
◆键值个数:个数相同
◆内部编码:都是字符串类型,而且ziplist,quicklist等配置一致
那么问题出现在哪里呢?比较好的是,我们这边Redis的集群ID有一定含义的,比如12xxx开头的是Redis 3.0.x,以13xxx开头的是Redis 3.2.x,以14xxxx开头的是Redis 4.0.x
这时候想到是了可能是SDS的问题,Redis 3.2开始SDS有个升级,https://raw.githubusercontent.com/antirez/redis/3.2/00-RELEASENOTES
二、一个实验
我做了一个简单试验,分别在Redis 3.0.7和Redis 4.0.12,写入10亿个44字节的key和value,代码如下:
Redis 3.0.7的内存消耗:
Redis 4.0.12的内存消耗:
可以看到Redis 4.0.12的内存消耗比Redis 3.0.7小了30G,几乎可以顶上一台小内存机器了。
三. Redis的SDS
下面来看看为什么选的是44字节:
1.内部编码
Redis中的字符串类型,有三种内部编码:raw、embstr、int。当值小于44字节(Redis 3.2+),使用embstr,否则使用raw(这里不讨论int),例如
下图展示了两者的区别,可以看到embstr将redisObject和SDS保存在连续的64字节空间内,这样可以只需要一次jemalloc分配,而对于raw来说,SDS和redisObject分离,需要两次jemalloc,而且占用更多的内存空间。
回头来看内存不一致的问题,实际不存在embstr和raw的区别,因为他们是双写,键值内容应该是完全一致的。那肯定就是SDS的变化:
可以看到embstr在3.2+中使用了叫sdshdr8的结构,在该结构下,元数据只需要3个字节,而Redis需要8个字节,所以总共64个字节,减去redisObject(16字节),再减去SDS的原信息,最后的实际内容就变成了44字节和39字节。
现在回过头来屡一下两个问题:
1.字符串多短为好:其实就是要尽量使用embstr。
2.Redis 3.0 和 Redis 3.2+的sds有很大不同,新版本的sds会根据字符串长度使用不同的原信息,下面来看一下
Redis 3.0
Redis 3.2+ (3.2 4.0 5.0):sdshdr5、sdshdr8、sdshdr16、sdshdr32、sdshdr64、
四、结论
1.SDS在Redis 3.2+有可能节省更多的空间,但3.2更像一个过渡版本,Redis 4更加适合(异步删除、psync2、碎片率整理),我已经在线上大量使用,“赶紧”去用吧。
2.embstr从Redis3 39字节->Redis3.2+ 44字节
3.做个环保的程序员,小优化大效果。