本专栏新增整合黄健宏老师的《Redis设计与实现》,以便更加有效地了解Redis 的内部构造及运作机制,帮助学者更高效得使用Redis。博文中会以虚线分区,上半区为Redis的简单使用,下半区为Redis内部实现,如果是简单地使用Redis快速完成工作或者只是想简单的了解Redis,只看上半区即可,有兴趣或等空闲的时候学习下下半区,结合源码会对Redis的理解更上一层楼。
List列表
- 基于Linked List 实现(索引 从左至右,从0开始 从右至左,从-1开始)
- 元素是字符串类型
- 列表头尾增删快,中间增删慢,增删元素是常态
- 元素可以重复出现
- 最多包涵2^32 - 1 个元素
命令说明(help @list 罗列所有关于list的命令)
B block 块,阻塞
L left 左
R right 右
X exist 存在
左右或者头尾压入元素
LPUSH key value [value......]
LPUSHX key value
RPUSH key value [value......]
RPUSHX key value
左右或者头尾弹出元素
LPOP key
RPOP key
从一个列表尾部弹出元素压入到另一个列表的头部
RPOPLUPSH source destination
返回列表中指定范围的元素
LRANGE key start stop
LRANGE key 0 -1:表示返回所有元素
获取指定位置的元素
LINDEX key index
设置指定位置元素的值
LSET key index value
列表长度,元素个数
LLEN key
从列表头部开始删除值等于value的元素count次
- LREM key count value
- count > 0 :从表头开始向表尾搜索,移除与value相等的元素,数量为count
- count < 0 :从表尾开始向表头搜索,移除与value相等的元素,数量为count的绝对值
- count = 0 :移除表中所有与value相等的值
去除指定范围外的元素
LTRIM key start stop
在列表某个存在的值前后插入元素
LINSERT key BEFORE|AFTER pivot value (在 pivot 之前或之后 插入元素值value)
如果key和pivot不存在,不进行任何操作,返回 -1
阻塞
1、左右或头尾阻塞弹出元素
BLPOP key [key....] timeout
BRPOP key [key...] timeout
2、从一个列表尾部阻塞弹出元素压入到另一个列表头部
BRPOPLPUSH list1 list2 timeout
链表 - 列表键的底层实现之一
1、链表被广泛用于实现Redis的各种功能,比如列表键、发布与订阅、慢查询、监视器等。2、每个链表节点由一个listNode结构来表示,每个节点都有一个指向前置节点和后置节点的指针,所以Redis的链表实现是双端链表。3、每个链表使用一个list结构来表示,这个结构带有表头节点指针、表尾节点指针,以及链表长度等信息。4、因为链表表头节点的前置节点和表尾节点的后置节点都指向NULL,所以Redis的链表实现是无环链表。5、通过为链表设置不同的类型特定函数,Redis的链表可以用于保存各种不同类型的值
每个链表节点使用一个adlist.h/listNode结构来表示,多个listNode可以通过prev和next指针组成双端链表。
typedef struct listNode{
//前置节点
struct listNode * prev;
//后直节点
struct listNode * next;
//节点的值
void * value;
}listNode;
Redis链表实现:
typedef struct list{
//表头节点
listNode * head;
//表尾节点
listNode * tail;
//链表所包含的节点数量
unsigned long len;
//节点值复制函数
void *(*dup)(void *ptr);
//节点值复制函数
void *(*free)(void *ptr);
//节点值对比函数
int (*match)(void *ptr,void *key);
}list;
dup、free和match 是用于实现多态链表所需的类型特定函数:dup函数用于复制链表节点所保存的值;free函数用于释放链表节点所保存的值;match函数则用于对比链表节点所保存的值和另一个输入值是否相等;
Redis的链表实现的特性可以总结如下:
- 双端:链表节点带有prev和next指针,获取某个节点的前置节点和后置节点的复杂度都是O(1)。
- 无环:表头节点的prev指针和表尾节点的next指针都指向NULL,对链表的访问以NULL为终点。
- 带表头指针和表尾指针:通过list结构的head指针和tail指针,程序获取链表的表头节点和表尾节点的复杂度为O(1)。
- 带链表长度计数器:程序使用list结构的len属性来对list持有的链表节点进行计数,程序获取链表中节点数量的复杂度为O(1)。
- 多态:链表节点使用void*指针来保存节点值,并且可以通过list结构的dup、free、match三个属性为节点值设置类型特定函数,所以链表可以用于保存各种不同类型的值。
链表和链表节点的API: