大家好,我是热心的大肚皮,皮哥。
redis中的list是啥?
redis中的列表相当于java中的LinkedList,注意它是链表不是数组。当列表弹出最后一个元素,该数据结构被删除,内存被回收。
- 插入、删除 - 时间复杂度O(1)
- 索引定位 - 时间复杂度O(N)
redis中的列表也常用做异步队列,将需要延后处理的任务数据序列化成字符串存入列表,另一个线程从这个列表中轮询数据。
先进先出:队列
代码语言:javascript复制> rpush books python java golang
(integer) 3
> llen books
(integer) 3
> lpop books
"python"
> lpop books
"java"
> lpop books
"golang"
> lpop books
(nil)
先进后出:栈
代码语言:javascript复制> rpush books python java golang
(integer) 3
> llen books
(integer) 3
> rpop books
"golang"
> rpop books
"java"
> rpop books
"python"
> rpop books
(nil)
以下慢操作使用时要格外注意。
代码语言:javascript复制> rpush books python java golang
(integer) 3
> lindex books #O(N)慎用
"java"
> lrange books 0 -1 # 获取所有元素,O(N)慎用
1) "python"
2) "java"
3) "golang"
> ltrim books 1 -1 # O(N)慎用
list如何实现?
数据量少时,使用一块连续内存存储-ziplist(压缩链表),数据量较多时,采用quicklist存储。如下图。
压缩链表-ziplist
代码语言:javascript复制struct ziplist<T> {
int32 zlbytes; // 整个压缩列表占用d的字节数
int32 zltail_offset; //最后一个元素距离起始位置的偏移量
int16 zllength;// 元素个数
T[] entries;// 元素内容
int8 zlend; // 结束位,固定值 0xFF
}
struct entry {
int<var> prevlen;//前一个entry的长度,支持倒序遍历使用
int<var> encoding;//元素类型编码
optional byte[] content;//内容
}
这么设计虽然在插入操作很方便,很快,但是也有个弊端,数据都是按照这种格式紧凑的放到一起没有冗余空间,那么每次修改时都要对压缩链表进行修改,要进行级联更新。
快速链表-quicklist
如果list中数据过多,那性能怎么保证呢?这时候就用到了quicklist。它是ziplist与linkedlist的组合体。
代码语言:javascript复制struct ziplist<T> {
....
}
struct ziplist_compressed {
int32 size;
byte[] compressed_data;//内容
}
struct quicklistNode {
quicklistNode* prev;
quicklistNode* next;
ziplist* zl;//对应的压缩列表
int32 size;// ziplist的字节总数
int16 count;// ziplist的元素数量
int2 encoding;//存储方式,原生数组还是LZF压缩存储
。。。
}
struct quicklist{
quicklistNode* head;
quicklistNode* tail;
long count;//元素数量
int nodes;//ziplist 节点个数
int compressDepth;//LZF算法压缩深度
。。。。
}
quicklist内部默认单个ziplist长度为8k字节,为了节约空间,还会对quicklist进行压缩,compressDepth为1时代表压缩深度为1,即首尾两个ziplist不压缩;compressDepth为2时代表压缩深度为2,即首尾两个ziplist不压缩及首尾第二个ziplist都不压缩。