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存储原理
Redis List存储的是有序的字符串(String和Hash是无序的),从左到右的队列,元素可以重复。
最大存储数量2^32-1(40亿左右元素数量)。
2.Redis-List数据类型:操作命令
Redis List存储的是有序的字符串(String和Hash是无序的),从左到右的队列,因此往队列中追加和弹出元素,也是需要区分左右的。
左侧放入队列元素单个或多个
# 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就是一个数组加双向链表的结构。
quicklist.h源码文件
quicklist.h源码文件在解压后的src目录下
quicklist
quicklist最终会指向quicklistNode
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,都进行压缩
- 首尾的ziplist不压缩
- 首尾第一第二个ziplist不压缩,以此类推
quicklistNode
quicklistNode指向实际的ziplist(压缩列表)
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是什么:特殊编码的,由连续内存块组成的双向链表
ziplist是一个经过特殊编码的,由连续内存块组成的双向链表。
它不存储指向上一个链表节点和指向下一个链表节点的指针,而是存储上一个节点长度和当前节点长度。ziplist结构:ziplist.c文件
看它的注释,这个代表了它的数据结构组成。
# ziplist数据结构组成
<zlbytes> <zltail> <zllen> <entry> <entry> ... <entry> <zlend>
<zlbytes> :占用内存字节数
<zltail> :列表末尾偏移量
<zllen> :节点数
<entry> <entry> ... <entry>
<zlend> :标记末端
ziplist的几种编码方式
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 命令移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。