此篇文章是主要介绍Redis在数据存储方面的其中一种方式,压缩列表。本文会介绍1. 压缩列表(ziplist)的使用场景 2.如何达到节约内存的效果?3.压缩列表的存储格式 4. 连锁更新的问题 5. conf文件配置。在实践上的操做主要是对conf配置文件进行配置,具体上没有确切的一个值,更可能是经验值。也有的项目会直接使用本来的默认值。此篇对于更好地理解一个数据库底层的存储逻辑会有一点帮助。修学储能,既要博,也要渊。但愿这篇文章对一样也是在学习Redis的各位同伴有点用。node
1、压缩列表(ziplist)的使用场景:redis
Redis为了优化数据存储,节约内存,在列表、字典(哈希键)和有序集合的底层实现了使用压缩列表这一优化方案。数据库
例如,假如一个哈希键里面存储的字符串比较短,那么Redis就会将它用压缩列表的格式去存储,即转换为字节数组存储。而一个哈希键内部存储的整数值比较小,一样也会把它存储为压缩列表的一个节点。同理,列表键的对小数据的存储跟哈希键的操做相似。数组
如此说来,压缩列表并非开发者能够直接调用的Redis中的一种存储数据结构,而是Redis中为优化数据存储而在底层作的一项努力。理解好这点仍是比较重要的。服务器
2、如何达到节约内存的效果?数据结构
压缩列表是一种序列化的数据结构,这种数据结构的功能是将一系列数据与其编码信息存储在一块连续的内存区域,这块内存物理上是连续的。但逻辑上被分为多个组成部分,即节点。目的是为了在必定可控的时间复杂度条件下尽量的减小没必要要的内存开销,从而达到节省内存的效果。须要理解是怎么达到节约内存做用的,还须要去了解压缩列表的存储格式。性能
3、压缩列表的存储格式:学习
压缩列表(ziplist)是Redis列表键、哈希键和有序集合键的底层实现之一,其实质是一种序列化的数据存储结构。有别于普通状况下,Redis用双端链表表示列表,使用散列表表示哈希键,用散列表+跳跃表表示有序集合。当一个列表或者哈希字典/有序集合只包含不多内容,而且每个列表项或者哈希项/有序集合项若是是小整数,或者比较短的字符串。那么Redis就会用压缩列表来作底层的实现。测试
压缩列表由一系列经Redis特殊编码的连续内存块组成,每个内存块称为一个节点(entry),而一个压缩列表能够包含不少个节点。每一个节点存储的数据格式能够是字节数组(中文字符串等都会转换为字节数组)或者整数值。优化
字节数组的长度能够是如下的其中一种:
1. 长度小于等于63字节(2的6次方)
2. 长度小于等于16383字节(2的14次方)
3. 长度小于等于4294967295字节(2的32次方)
整数值多是如下六种中的其中一种:
1. 4位长,介于0-12之间的无符号整数
2. 1字节长的有符号整数
3. 3字节长的有符号整数
4. int16_t类型整数
5. int32_类型整数
6. int64_t类型整数
普通存储格式下和压缩列表存储格式下的不一样点:
列表存储结构典型的为双端链表,每个值都是用一个节点来表示,每一个节点都会有指向前一个节点和后一个节点的指针,以及指向节点包含的字符串值的指针。而字符串值又分为3个部分存储,第一部分存储字符串长度,第二部分存储字符串值中剩余可用的字节量,第三部分存储的则是字符串数据自己。因此一个节点每每都须要存储3个指针、2个记录字符串信息的整数、字符串本省和一个额外的字节。整体上额外的开销是很大的(21字节)。
压缩列表节点的格式:
每个节点都有previous_entry_length,encoding,content三个部分组成,在遍历压缩列表的时候是从后往前遍历的。
1. previous_entry_length记录了前一个节点的长度,只要用当前指针减去这个值就能够达到前一个节点的起始地址。
2. encoding记录了节点content属性所保存数据的类型和长度
3. content记录了一个节点的值
显然压缩列表这种方式节约了很多存储空间。但同时也会引起下面的问题。
4、连锁更新的问题:
通常而言若是前一个节点的总体长度小于254字节,previous_entry_length属性只须要1个字节的空间来保存这个长度值。而当前一个节点大于254字节的时候,previous_entry_length属性要用5个字节长的空间来记录长度值。
当长度为254字节左右的节点前插入一个新的节点的时候,须要增长previous_entry_length来记录这个节点到新节点的偏移量。这个时候,这个节点的长度确定就大于254字节了。因此这个节点的后一个节点就不能只用一个字节的previous_entry_length来记录这个节点的信息了,而是须要5个字节来记录。若是连续多个节点的长度都为254字节左右,在其中的某一个节点前/后发生节点的插入和删除(删除的推理与插入相反,本来用5字节记录前一节点的可能变为1字节),均可能引起连锁的更新,显然,这样对系统地运行效率是很不利的。不过,在实际应用中这种状况仍是比较少发生的。
而双端链表在节点的更新、增长和删除上显得就会“轻松”不少了。 由于每个节点存储的信息都是相对独立的。
实践意义:
要预估一个节点大概占据多少字节的存储空间,适当地调整字段的存储格式而不要使存储的字段值占据存储空间落在254字节(除去encoding属性和previous_entry_length属性)左右。
Redis中查看字符串和哈希键值的长度相关命令:
1. 查询字符串键对应的值长度
命令:
Strlen
例如:
127.0.0.1:6379> strlen m_name
(integer) 8
2. 查询哈希键某一个域长度
命令:
Hstrlen
例如:
127.0.0.1:6379> hstrlen good_list good_list1
(integer) 226
5、Conf文件配置:
经过修改配置文件,能够控制是否使用压缩列表存储相关键的最大元素个数和最大元素的大小
Conf文件中的配置:
1.
[] -max-ziplist-entries : 表示对于键的最大元素个数,即一个键中在该指定值下的数量的节点个数都会用压缩列表来储存
[] -max-ziplist-value :表示压缩列表中每一个节点的最大致积是多少字节
实际使用中,一个列表键/哈希键的某一个元素每每存储着比较大的信息量,会大于64字节,因此配置时颇有可能会比64大,同时考虑到实际存储数据的容量大小以及上面谈到的previous_entry_length的大小问题,对[] -max-ziplist-value进行合理的配置。
配置文件内容:
############## ADVANCED CONFIG ########################## 哈希键 # Hashes are encoded using a memory efficient data structure when they have a # small number of entries, and the biggest entry does not exceed a given # threshold. These thresholds can be configured using the following directives. hash-max-ziplist-entries 512 hash-max-ziplist-value 64 有序集合键 # Similarly to hashes and lists, sorted sets are also specially encoded in # order to save a lot of space. This encoding is only used when the length and # elements of a sorted set are below the following limits: zset-max-ziplist-entries 128 zset-max-ziplist-value 64 列表键,比较特殊,直接使用制定大小kb字节数表示(有些conf文件的列表键与hash键的表达式没太大区别) # Lists are also encoded in a special way to save a lot of space. # The number of entries allowed per internal list node can be specified # as a fixed maximum size or a maximum number of elements. # For a fixed maximum size, use -5 through -1, meaning: # -5: max size: 64 Kb <-- not recommended for normal workloads # -4: max size: 32 Kb <-- not recommended # -3: max size: 16 Kb <-- probably not recommended # -2: max size: 8 Kb <-- good # -1: max size: 4 Kb <-- good # Positive numbers mean store up to _exactly_ that number of elements # per list node. # The highest performing option is usually -2 (8 Kb size) or -1 (4 Kb size), # but if your use case is unique, adjust the settings as necessary. list-max-ziplist-size -2
案例:
修改配置前使用默认配置:
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
127.0.0.1:6379> hstrlen good_list good_list1
(integer) 226
127.0.0.1:6379> object encoding good_list
"hashtable"
修改配置:
hash-max-ziplist-entries 512
hash-max-ziplist-value 254
注意:修改配置后须要重启服务器
127.0.0.1:6379> hstrlen good_list good_list1
(integer) 226
127.0.0.1:6379> object encoding good_list
"ziplist"
能够看到存储方式已将变为ziplist
较官方的压力测试和指导建议:
当一个压缩列表的元素数量上升到几千(实际使用可能远小于这个值)的时候,压缩列表的性能可能会降低,由于Redis在操做这种结构的时候,编解码会出现必定的压力。
压缩列表的长度限制在500-2000以内,每一个元素体积限制在128字节或如下,压缩列表的的性能都会处于合理范围以内。
参考资料:
《redis设计与实现》