Redis学习之空间节省

我们知道Redis是一个基于内存的数据库,所有的数据都存储在内存中,所以我们需要学习如何优化存储,用最小的代价得到最好的性能。

在使用Redis的过程中,我们可以使用下面的方式来节省空间:

一.精简键名和键值

这是一种很直观的减少内存占用的方式,这种方式的目标是:用最简洁的名称表达最好的效果。切忌为了单纯的节省空间而使用难以理解的键名。

二.内部编码优化

1.概念了解

这种方式是根据Redis内部编码规则来节省空间,Redis为每种数据类型都提供了两种内部编码方式,我们以散列类型为例:

散列类型是通过散列表实现的,它的优势是在数据很多的时候以优越的时间复杂度查找指定数据。

但是当键中的元素很少的时候,它的性能并不比普通的查找方式高所以这种情况下Redis会采用一种更为紧凑但性能较差的普通内部编码方式。

内部编码方式的选择对于开发者来说是透明的,Redis会根据实际情况自动调整,当键中元素变多时Redis会自动将该键的内部编码方式转换为散列表。
如果想查看一个键的内部编码方式我们可以使用OBJECT ENCODING命令来执行:
在这里插入图片描述
2.基本知识了解

Redis的每个键值都是使用一个redisObject结构体保存的,redisObject的定义如下:

type struct redisObject{
    unsigned type:4; /表示键值的数据类型
    unsigned notused:2; /
    unsigned encoding:4; /表示Redis键值的内部编码方式
    unsigned lru:22;/
    int refcount;/该键被引用的数量
    void *ptr;
}robj;

其中type字段表示的键值的数据类型取值可以是如下内容:

#define REDIS_STRING 0
#define REDIS_LIST 1
#define REDIS_SET 2
#define REDIS_ZSET 3
#define REDIS_HASH 4

encoding字段表示的就是Redis键值的内部编码方式。

3.下面我们针对每种数据类型分别学习其内部编码规则及优化方式:

1)字符串类型
Redis使用一个sdshdr类型的变量来存储字符串,而redisObject的ptr子段指向的是改变量的地址,

sdshdr定义如下:
struct sdshdr{
  int len;
  int free;
  char buf[];
}

其中len表示字符串长度,free表示buf中的剩余空间,buf存储字符串内容。

所以当执行SET key foobar时,存储键值需要的空间是:
sizeof(redisObject)+sizeof(sdshdr)+strlen(“foobar”)=30字节

而当键值内容可以用一个64位有符号整数表示时,Redis会将键值转换成long类型来存储,例如:SET key 123456, 它所占用的空间比前者要小。

由此可见,使用字符串类型存储数字比存储字符串要省空间。 int 比 raw编码方式省空间。

Redis 3.0中新加了一中编码方式,embstr方式,这种方式与raw类似,都是基于sdshdr方式实现,只不过sdshdr的结构体与其对应的分配在同一块连续的内存空间中。这种方式便于分配和释放内存,当键值内容不超过39字节时,Redis会采用这种方式编码,但当对这种编码方式的键值进行任何修改操作时,Redis会将其转换为raw编码方式。

2)散列类型
散列类型的内部编码方式可能是 :

REDIS_ENCODING_HT(基于hash存储,适合元素量较多的集合)
REDIS_ENCODING_ZIPLIST(基于链表存储,适合元素较少的集合)

我们可以在配置文件中定义使用REDIS_ENCODING_ZIPLIST方式编码散列类型的时机,在配置文件中配置下面属性:
hash-max-ziplist-entries 512
hash-max-ziplist-value 64 时,
当散列类型键的字段数少于hash-max-ziplist-entries参数值且每个字段名和字段值的长度都小于 hash-max-ziplist-value参数值(单位为字节)时,Redis就会使用REDIS_ENCODING_ZIPLIST来存储该键,否则使用REDIS_ENCODING_HT. 每当键值变更后Redis都会自动判断是否满足条件来完成转换。

3)列表类型
列表类型的内部编码方式:
REDIS_ENCODING_LINKEDLIST(基于双向链表)
REDIS_ENCODING_ZIPLIST(和散列类型的一样)
它们之间的转换方式和散列类型的一样,同理。

REDIS_ENCODING_QUICKLIST:它是前两者的结合,其原理是将一个长列表分成若干个以链表形式组织的ziplist,从而达到减少空间占用的同时提升了REDIS_ENCODING_ZIPLIST编码的性能。(最新的Redis默认使用它)

4)集合类型
集合类型的内部编码方式为:
REDIS_ENCODING_HT(无序)
REDIS_ENCODING_INTSET(有序)

当集合中的所有元素都是整数且元素的个数小于配置文件中的set-max-intset-entries(默认512)时Redis会使用REDIS_ENCODING_INTSET编码方式存储该集合,否则使用REDIS_ENCODING_HT。

REDIS_ENCODING_INTSET编码存储结构体inset的定义如下:

typedef struct intset{
  uint32_t encoding;
  uint32_t length;
  int8_t contents[];
}intset;

其中contents存储的就是集合中的元素值,根据encooding的不同,每个元素占用的字节数不同,默认的encoding是INTSET_ENC_INT16(两个字节),当新增加的整数元素无法用两个字节表示时,Redis会用INTSET_ENC_INT32(4个字节),再者就用INTSET_ENC_INT64(8个字节)

这种编码方式以有序的的方式存储元素。

5)有序集合类型
有序集合类型的内部编码方式可能是:
REDIS_ENCODING_SKIPLIST
REDIS_ENCODING_ZIPLIST

同样我们可以在配置文件中定义使用:
zset-max-ziplist-entries 128
zset-max-ziplist-value 64

具体是转换和散列类型一样,不赘述。

当编码方式是REDIS_ENCODING_SKIPLIST时,Redis使用散列表和跳跃列表两种数据结构来存储有序集合类型的键值。

其中散列表用来存储元素值于元素分数的映射关系以实现O(1)时间复杂度的ZSCORE等命令。
跳跃表用来存储元素的分数及其到元素值的映射以实现排序的功能,其中Redis中对跳跃列表的实现进行了几点修改,包括:允许跳跃表中的元素(分数)相同,为跳跃链表每个节点增加了指向前一个元素的指针以实现倒序查找。

总结:我们除了优化键值的应用格式外,还可以通过编码方式对存储空间进行优化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员小牧之

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值