首页 > 编程知识 正文

redis sortedset,redis存储list集合

时间:2023-05-06 16:30:54 阅读:128965 作者:418

在以前的博客Redis数据结构的基础skiplist中,了解了Redis的跳转表。 在本博客中,您将在Redis中学习重要的数据结构—— ziplist (压缩链表)。

版本: 3.0源地址: 3.0/src/ziplist.c (这次位于. c文件而不是. h文件的注释中)。

我先告诉你ziplist在做什么:

/* theziplistisaspeciallyencodedduallylinkedlistthatisdesigned * tobeverymemoryefficient.itstoresbothstringsandintegervalueeervaluent whereintegersareencodedasactualintegersinsteadofaseriesof * characters.* /在简单翻译中,压缩链表是专门为节省内存而设计的可以存储字符串值和整数值。 整数值不是编码为一系列字符,而是根据正整数编码存储。 (参考了ziplist结构的详细情况) ) ) ) ) ) ) ) )。

特性有什么特性?

/* itallowspushandpopoperationsoneithersideofthelist * ino (1) time. */在表的两端提供复杂度为o )1)的推送和pop操作。

现在,让我们来看看上述特性是如何实现的。

结构/* ziplistoveralllayout : * thegenerallayoutoftheziplistisasfollows 3360 * zlbyteszltailzlenentryentryentryzlend * * zlbytesisanunsignedintegertoholdthenumberofbytesthattheziplistoccupies.* thisvalueneedstobestoredtobeabletoresizetheentirestructurewithouttheneedtotraverseitfirst.* * zltailistheofffsetotthelastettthelateratteratetetttteratteteterather thisallowsapopoperationonthefarsideofthelistwithouttheneedforfulltraversal.* * zllenisthenumberofentries.whenthisvalueislalaraved weneedtotraversetheentirelisttoknowhowmanyitemsitholds.* * zlendisasinglebytespecialvalue,equal to 255,whichindicatesthestheeenthenathenathenal

zlbytes:32bit记录了当前ziplist占用的存储器区域的大小(可变),无需横穿整个ziplist结构取得占用区域的大小,就可以容易地实现存储器的再配置。 ZL tail:32位记录了相对于当前ziplist表中最后一个节点的压缩链表起始地址的偏移量,使您可以确定ziplist末尾元素的地址而无需遍历。 zll en:16位,记录当前ziplist数据项(entry )的数量,如果值大于2162 )2^{16}-22162,则需要跨整个结构获取ziplist中的数据项数量。 zlend :恒等为255,表示ziplist的末尾。 上述大小请参阅第3.0/src/ziplist.c 141行。

/* utility macros */#defineziplist _ bytes (ZL ) ) () ZL ) ) ) # define

ZIPLIST_TAIL_OFFSET(zl) (*((uint32_t*)((zl)+sizeof(uint32_t))))#define ZIPLIST_LENGTH(zl) (*((uint16_t*)((zl)+sizeof(uint32_t)*2)))#define ZIPLIST_HEADER_SIZE (sizeof(uint32_t)*2+sizeof(uint16_t))#define ZIPLIST_ENTRY_HEAD(zl) ((zl)+ZIPLIST_HEADER_SIZE)#define ZIPLIST_ENTRY_TAIL(zl) ((zl)+intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl)))#define ZIPLIST_ENTRY_END(zl) ((zl)+intrev32ifbe(ZIPLIST_BYTES(zl))-1)

再来具体说下 entry:

ZIPLIST ENTRIES: * Every entry in the ziplist is prefixed by a header that contains two pieces * of information. First, the length of the previous entry is stored to be * able to traverse the list from back to front. Second, the encoding with an * optional string length of the entry itself is stored. * * The length of the previous entry is encoded in the following way: * If this length is smaller than 254 bytes, it will only consume a single * byte that takes the length as value. When the length is greater than or * equal to 254, it will consume 5 bytes. The first byte is set to 254 to * indicate a larger value is following. The remaining 4 bytes take the * length of the previous entry as value. * * The other header field of the entry itself depends on the contents of the * entry. When the entry is a string, the first 2 bits of this header will hold * the type of encoding used to store the length of the string, followed by the * actual length of the string. When the entry is an integer the first 2 bits * are both set to 1. The following 2 bits are used to specify what kind of * integer will be stored after this header. An overview of the different * types and encodings is as follows: * * |00pppppp| - 1 byte * String value with length less than or equal to 63 bytes (6 bits). * |01pppppp|qqqqqqqq| - 2 bytes * String value with length less than or equal to 16383 bytes (14 bits). * |10______|qqqqqqqq|rrrrrrrr|ssssssss|tttttttt| - 5 bytes * String value with length greater than or equal to 16384 bytes. * |11000000| - 1 byte * Integer encoded as int16_t (2 bytes). * |11010000| - 1 byte * Integer encoded as int32_t (4 bytes). * |11100000| - 1 byte * Integer encoded as int64_t (8 bytes). * |11110000| - 1 byte * Integer encoded as 24 bit signed (3 bytes). * |11111110| - 1 byte * Integer encoded as 8 bit signed (1 byte). * |1111xxxx| - (with xxxx between 0000 and 1101) immediate 4 bit integer. * Unsigned integer from 0 to 12. The encoded value is actually from * 1 to 13 because 0000 and 1111 can not be used, so 1 should be * subtracted from the encoded 4 bit value to obtain the right value. * |11111111| - End of ziplist.

每个结点前面都有一个 header,这个 header 包含了两类信息:

1、上一个数据项的长度(大小),从后向前遍历时使用(从后一项位置向前移动该长度,就找到了前一项)

如果上一个数据项占用字节数小于 254,则用 1 个字节来保存,字节值就是上一个数据项的占用字节数。如果上一个数据项占用字节数大于等于 254,则用 5 个字节表示。为了表示这种情况,第一个字节的值是 254,后面的 4 个字节组成一个数,存储前一个数据项的占用字节大小。

不是 255 的原因是 255 已经被用来表示 ziplist 尾端了。

2、当前数据项本身的数据长度,具体内容和数据项保存的值有关

如果保存的是字符串,则头 2 位将保存编码字符串长度(大小)使用的类型,之后是字符串真正的长度;

1)|00pppppp| - 1 byte:字符串长度小于等于 63 字节( 2 6 − 1 2^6-1 26−1)
2)|01pppppp|qqqqqqqq| - 2 bytes:字符串长度小于等于 16383 字节( 2 14 − 1 2^{14}-1 214−1)
3)|10______|qqqqqqqq|rrrrrrrr|ssssssss|tttttttt| - 5 bytes:字符串长度大于等于 16384 字节( 2 32 − 1 2^{32}-1 232−1)

如果保存的是整数,那么头 2 位都会被设置为 1,后面两字节用来标识结点保存整数的类型。

1)|11000000| - 1 byte:2 个字节的 int16_t 类型整数
2)|11010000| - 1 byte:4 个字节的 int32_t 类型整数
3)|11100000| - 1 byte:8 个字节的 int64_t 类型整数
4)|11110000| - 1 byte:3 个字节长的整数
5)|11111110| - 1 byte:1 个字节长的整数
6)|1111xxxx| - (with xxxx between 0000 and 1101) immediate 4 bit integer:从 1 到 13 一共 13 个值,用 13 个值来保存真正的数据(数据而非数据长度)

参考 Redis内部数据结构详解,:

3、 看下结构:

typedef struct zlentry {// 编码上一个 entry 长度用的字节大小,上一个 entry 的长度 unsigned int prevrawlensize, prevrawlen; // 编码当前 entry 长度用的字节大小,当前 entry 的长度 unsigned int lensize, len; // header 部分的大小,prevrawlensize + lensize unsigned int headersize; // 当前 entry 的编码方式 unsigned char encoding; // 指向 entry 的指针,即 prev-entry-len 字段。 unsigned char *p;} zlentry;

具体的图就不画了,自己有几个地方捋顺不清,暂时先按照 Redis内部数据结构详解 此篇博客中的来理解吧(找了好久资料,都是把 entry 划分为 3 个部分来解释的,自己 C 的知识几乎没有,就先这样吧)

大概的

version:6.0

简单看了下 6.0/src/ziplist.c,和 3.0 的差别不是特别大,自己看 3.0 的时候也参照了 6.0 中的大量注释。

缺点

ziplist 有个缺点不得不提一下:连锁更新。

连锁更新,就是指当一个元素插入后,会引起当前位置元素新增 prevlensize 的空间。而当前位置元素的空间增加后,又会进一步引起该元素的后续元素,其 prevlensize 所需空间的增加

关联阅读

Redis 基础

Redis 数据结构 SDS

Redis 数据结构 linkedlist

Redis 数据结构 hashtable

Redis 数据结构 skiplist

Redis 数据结构 ziplist

Redis 数据结构 intset

Redis 数据结构 quicklist

Redis 对象 RedisObject

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。