Redis源码-List:Redis List存储原理、Redis List命令、 Redis List存储底层编码quicklist、Redis List应用场景

  • Redis数据类型
  • 1.List存储原理
  • 2.Redis-List数据类型:操作命令
  • 左侧放入队列元素单个或多个
  • 右侧放入队列元素单个或多个
  • 左侧弹出首元素
  • 右侧弹出首元素
  • 根据下标获取元素
  • 根据下标范围获取元素
  • 阻塞的移出并获取列表的第一个元素弹出元素
  • 查看Redis内部的数据结构类型
  • 3.存储原理(底层编码)
  • Redis List一种存储底层编码类型:quicklist
  • quicklist.h源码文件
  • quicklist
  • quicklist中的参数:list-max-ziplist-size(fill)
  • quicklist中的参数:list-compress-depth(compress)
  • quicklistNode
  • ziplist.c源码文件:
  • ziplist是什么:特殊编码的,由连续内存块组成的双向链表
  • ziplist的几种编码方式
  • ziplist.c文件的zlentry的源码截取
  • 4.应用场景
  • 作为分布环境的队列、栈来使用
  • Redis各数据结构命令、源码分析、应用场景


Redis数据类型

Redis数据类型不等同与数据结构,数据结构是Redis该数据类型存储结构的存储原理,也是该数据类型的底层编码。

1.List存储原理

redisutil list 泛型 redis list 原理_list

Redis List存储的是有序的字符串(String和Hash是无序的),从左到右的队列,元素可以重复。
最大存储数量2^32-1(40亿左右元素数量)。

2.Redis-List数据类型:操作命令

Redis List存储的是有序的字符串(String和Hash是无序的),从左到右的队列,因此往队列中追加和弹出元素,也是需要区分左右的。

redisutil list 泛型 redis list 原理_list_02

左侧放入队列元素单个或多个

# lpush key value [value ...]:队列左边放入元素,可以放多个(空格隔开)
# 返回放入之后的元素个数
127.0.0.1:6379> lpush queue a
(integer) 1
127.0.0.1:6379> lpush queue b c
(integer) 3

右侧放入队列元素单个或多个

# rpush key value [value ...]:队列右边放入元素,可以放多个(空格隔开)
127.0.0.1:6379> rpush queue d e
(integer) 5

左侧弹出首元素

# lpop key:左侧弹出一个元素
# 返回弹出的元素值
127.0.0.1:6379> lpop queue
"c"

右侧弹出首元素

# rpop key:右侧弹出一个元素
# 返回弹出的元素值
127.0.0.1:6379> rpop queue
"e"

根据下标获取元素

# lindex key index:根据下标获取元素
127.0.0.1:6379> lindex queue 0
"b"

根据下标范围获取元素

# lrange key start stop:根据下标范围获取元素,0代表开始,-1代表末尾
127.0.0.1:6379> lrange queue 0 3
1) "b"
2) "a"
3) "d"

阻塞的移出并获取列表的第一个元素弹出元素

# blpop key [key ...] timeout:b表示block,阻塞的,会持续等待放入元素,因此有超时时间的设置
# 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
# 返回值
# 如果列表为空,返回一个 nil 。 否则,返回一个含有两个元素的列表,第一个元素是被弹出元素所属的 key ,第二个元素是被弹出元素的值
127.0.0.1:6379> lrange queue 0 -1
1) "b"
2) "a"
3) "d"
127.0.0.1:6379> blpop queue 10
1) "queue"
2) "b"
# 如果指定的列表 key list1 存在数据则会返回第一个元素,否则在等待10秒后会返回 nil 
127.0.0.1:6379> BLPOP list1 10
(nil)
(10.12s)
127.0.0.1:6379>

查看Redis内部的数据结构类型

当你插入Redis一个字符串数据时,会根据你插入的值,Redis再进行内部的数据转换。
可以先看本文目录:3.存储原理(底层编码)

可以用object encoding key名查看key的value再Redis的内部数据类型。

# 查看key在Redis的对内数据类型
127.0.0.1:6379> object encoding queue
"quicklist"

3.存储原理(底层编码)

Redis源码:Redis源码怎么查看入门、Redis外部数据结构到Redis内部数据结构查看源码顺序、Redis源码查看顺序

List是怎么存储的?
在Redis早期的版本,3.2.0之前,数据量小的是ziplist,大的是linkedlist存储。
后来统一改为quicklist存储,它是ziplist和linkedlist的结合,双向链表linkedlist里面存储一个个元素ziplist。
重点关注quicklist就可以。

Redis List一种存储底层编码类型:quicklist

quicklist就是一个数组加双向链表的结构。

redisutil list 泛型 redis list 原理_哈希算法_03

quicklist.h源码文件

quicklist.h源码文件在解压后的src目录下

quicklist

quicklist最终会指向quicklistNode

redisutil list 泛型 redis list 原理_list_04

typedef struct quicklist {
    quicklistNode *head;						/* 指向双向链表的表头 */
    quicklistNode *tail;						/* 指向双向链表的表头 */
    unsigned long count;        				/* 所有的ziplist中一共存了多少个元素,即ziplist中的entry个数 */ /* total count of all entries in all ziplists */
    unsigned long len;          				/* 双向链表的长度,quicklistNode的数量 */ /* number of quicklistNodes */
    int fill : QL_FILL_BITS;              		/* ziplist最大大小,对应list-max-ziplist-size */ /* fill factor for individual nodes */
    unsigned int compress : QL_COMP_BITS; 		/* 压缩深度,对应list-compress-depth */ /* depth of end nodes not to compress;0=off */
    unsigned int bookmark_count: QL_BM_BITS;	/* 4位,bookmarks数组的大小 */
    quicklistBookmark bookmarks[];				/* bookmarks是一个可选字段,quicklist重新分配内存空间时使用,不使用时不占用空间 */
} quicklist;
quicklist中的参数:list-max-ziplist-size(fill)

正数表示单个ziplist最多包含的entry个数
负数代表单个ziplist的大小,默认8k

  • -1:4KB
  • -2:8KB
  • -3:16KB
  • -4:32KB
  • -5:64KB
quicklist中的参数:list-compress-depth(compress)

压缩深度(表示对多少个ziplist首尾的元素不进行压缩),默认是0,都进行压缩

  1. 首尾的ziplist不压缩
  2. 首尾第一第二个ziplist不压缩,以此类推

quicklistNode

quicklistNode指向实际的ziplist(压缩列表)

redisutil list 泛型 redis list 原理_哈希算法_05

typedef struct quicklistNode {
    struct quicklistNode *prev;				/* 指向前一个节点 */
    struct quicklistNode *next;				/* 指向后一个节点 */
    unsigned char *zl;						/* 指向实际的ziplist */
    unsigned int sz;             			/* 当前ziplist占用多少字节 */ /* ziplist size in bytes */
    unsigned int count : 16;     			/* 当前ziplist中存储了多少个元素,占16bit(下同),最大65536个 */ /* count of items in ziplist */
    unsigned int encoding : 2;   			/* 是否采用了LZF压缩算法压缩节点 */ /* RAW==1 or LZF==2 */
    unsigned int container : 2;  			/* 2:ziplist,未来可能支持其它存储结构 */ /* NONE==1 or ZIPLIST==2 */
    unsigned int recompress : 1; 			/* 当前ziplist是否已经被解压出来作临时使用 */ /* was this node previous compressed? */
    unsigned int attempted_compress : 1; 	/* 测试用 */ /* node can't compress; too small */
    unsigned int extra : 10; 				/* 预留给未来使用 */ /* more bits to steal for future usage */
} quicklistNode;

ziplist.c源码文件:

ziplist.c源码文件在解压后的src目录下

ziplist是什么:特殊编码的,由连续内存块组成的双向链表

redisutil list 泛型 redis list 原理_Redis_06


ziplist是一个经过特殊编码的,由连续内存块组成的双向链表

它不存储指向上一个链表节点和指向下一个链表节点的指针,而是存储上一个节点长度和当前节点长度。ziplist结构:ziplist.c文件

看它的注释,这个代表了它的数据结构组成。

redisutil list 泛型 redis list 原理_list_07

# ziplist数据结构组成
<zlbytes> <zltail> <zllen> <entry> <entry> ... <entry> <zlend>

<zlbytes> :占用内存字节数
<zltail> :列表末尾偏移量
<zllen> :节点数
<entry> <entry> ... <entry> 
<zlend> :标记末端

ziplist的几种编码方式

redisutil list 泛型 redis list 原理_Redis_08

ziplist.c文件的zlentry的源码截取

/* 并不代表实际存储方式 */ 
typedef struct zlentry {
    unsigned int prevrawlensize; /* Bytes used to encode the previous entry len*/
								 /* 存储上一个链表节点的长度值所需要的字节数*/
								 
    unsigned int prevrawlen;     /* Previous entry len. */
								 /* 上一个链表节点占用的长度 */
	
    unsigned int lensize;        /* Bytes used to encode this entry type/len.
                                    For example strings have a 1, 2 or 5 bytes
                                    header. Integers always use a single byte.*/
								 /* 存储当前链表节点长度数值所需要的字节数 */	
									
    unsigned int len;            /* Bytes used to represent the actual entry.
                                    For strings this is just the string length
                                    while for integers it is 1, 2, 3, 4, 8 or
                                    0 (for 4 bit immediate) depending on the
                                    number range. */
								 /* 当前链表节点占用的长度 */	
									
    unsigned int headersize;     /* prevrawlensize + lensize. */
								 /* 当前链表节点的头部大小(prevrawlensize + lensize),即非数据域的大小 */
	
    unsigned char encoding;      /* Set to ZIP_STR_* or ZIP_INT_* depending on
                                    the entry encoding. However for 4 bits
                                    immediate integers this can assume a range
                                    of values and must be range-checked. */
								 /* 编码方式 */	
									
    unsigned char *p;            /* Pointer to the very start of the entry, that
                                    is, this points to prev-entry-len field. */
								 /* 压缩链表以字符串的形式保存,该指针指向当前节点起始位置 */		
									
} zlentry;

4.应用场景

  • 有序列表:消息列表、文章列表、评论列表、公告列表、活动列表
  • 作为分布环境的队列、栈来使用

作为分布环境的队列、栈来使用

借助Redis Blpop 命令移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。

redisutil list 泛型 redis list 原理_list_09