String 提供了 “键 - 单值” 的存储模式,可以使用与很多场景。而且可以保存二进制字节流。但是 String 存在保存的数据内存消耗比较多的问题。
String 类型内存开销大
除了实际存储的数据,String 类型还需要额外的内存空间记录字符长度,空空间使用等原数据信息。
数据存储方式
- int编码 :在保存64位的有符号整数时,String 会保存为一个 8 字节的 Long类型整数
- SDS:String 保存包含字符的数据时,就会使用SDS
SDS(Simple Dynamic String)

- buf : 字节数组保存实际的数据
- len : 4个字节,表示 buf 已用长度
- alloc : 也占4个字节,表示 buf 实际使用长度
len 和 alloc 就是String一个内存额外消耗
RedisObject 的结构消耗
Redis 有多种数据类型,不同的数据类型有相同的元素要记录,Redis 使用RedisObject 结构来同一记录这些元数据,同时指向实际数据; RedisObject 包含一个8字节的元数据和一个8字节的指针

- 如果保存的是Long类型的整数时,RedisObject 中的指针就直接被赋值为整数数值。
- embstr : 如果为是字符串,并且字符串小于,等于 44 字节时,RedisObject 中元数据、指针、SDS 是一个连续的内存空间
- raw : 如果字符串,大于 44 字节时, RedisObject 给SDS配置独立的内存空间并让指针指向SDS

Redis 结构
Redis 使用一个全局的哈希表保存所有的键值对,哈希表的每一项都是一个dictEntity,dictEntity 结构中有三个 8 字节的指针,指向 key、value、dictEntity, 因为Redis 采用 jemalloc 分配内存空间,会根据申请的字节数 N ,找到一个比N大,但是接近N的2次幂整数作为分配空间的大小,所以一个 dictEntity 占用 32 个字节
压缩列表
Redis 中的压缩列表非常省空间,表头有三个字段,zlbytes : 列表长度; zltail : 列表尾的偏移量;zlen : 列表中 entry 的个数。还有一个zlend : 表示列表结束。压缩列表省内存主要是它使用一系列连续的entry保存,连续存放不需要额外的指针,节省了指针空间。

压缩类表的Entry
- prev_len : 表示前一个 entry 长度,当一个 entry 长度小于 254 为 1 否则为 5;
- encoding : 表示编码方式 1 字节
- len : 自身的长度 4 字节
- key : 保存实际数据
如何使用集合保存单值键值对
在保存单值键值对时,可以采取基于 Hash 类型的二级编码方式;把一个单值的数据拆成两部分,前一部分作为 Hash 的键,后一个部分作为 Hash 的值
二级编码的拆分
因为哈希有两种数据结构压缩表和哈希表;如果超过下面设置的阈值就会从压缩列表转为哈希表,而且不可逆转
- hash-max-zlist-entries : 表示压缩链表保存时最大的元素个数
- hash-max-zlist-values : 表示压缩链表保存时单个元素的最大长度
基于以上特性,使用二级编码是确保哈希集合中的元素个数
