阿里云 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. 做个环保的程序员,小优化大效果。附图一张:

欢迎订阅我的公众号:关注Redis开发运维实战相关问题,干掉所有的坑。

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章