Skip to content

List(列表)

1. 概述

List是简单的字符串列表,按插入顺序排序。

常用命令LPUSHRPUSHLPOPRPOPLRANGELINDEXLSET

2. 底层数据结构

2.1 三种编码

编码说明适用场景
ziplist压缩列表,小列表少量元素
quicklist快速列表(Redis 3.2+)大列表
linkedlist双端链表,旧版本历史兼容

2.2 quicklist

Redis 3.2+使用quicklist作为List的底层实现:

quicklist = [quicklistNode1] → [quicklistNode2] → [quicklistNode3]
                ↓                    ↓                    ↓
            [ziplist]            [ziplist]            [ziplist]

设计思想:结合ziplist和linkedlist的优点

  • ziplist:内存紧凑,缓存局部性好
  • linkedlist:O(1)头尾操作

2.3 quicklistNode结构

c
typedef struct quicklistNode {
    struct quicklistNode *prev;
    struct quicklistNode *next;
    unsigned char *zl;           // 指向ziplist
    unsigned int sz;             // ziplist字节大小
    unsigned int count: 16;      // ziplist中元素数量
    unsigned int encoding: 2;    // 1=原生,2=lzf压缩
    unsigned int container: 2;  // 1=ziplist,2=quicklistNode
    unsigned int recompress: 1; // 是否需要解压
    unsigned int attempted_compress: 1;
    unsigned int extra: 10;
} quicklistNode;

2.4 配置参数

bash
# 压缩深度(两端各多少个节点不压缩)
list-compress-depth 0

# 单节点ziplist最大大小(字节)
list-max-ziplist-size -2

# 元素数量限制
list-max-ziplist-entries 512

3. 编码转换

ziplist → quicklist → quicklist(压缩)

转换条件:

  • list-max-ziplist-size > 0:ziplist字节大小限制
  • list-max-ziplist-entries > 0:元素数量限制

4. 面试考点

Q1: Redis List的底层实现?

答案: Redis 3.2+使用quicklist作为List的底层实现,由多个ziplist节点组成双向链表。每个quicklistNode包含一个ziplist,平衡了内存紧凑性和头尾操作的O(1)复杂度。

Q2: 为什么用quicklist而不是linkedlist?

答案: linkedlist每个节点独立分配内存,内存碎片化且缓存局部性差。quicklist将多个元素存储在一个ziplist中,减少内存分配,提高缓存命中率。综合了ziplist紧凑和linkedlist灵活的优点。

Q3: LPUSH和RPUSH的时间复杂度?

答案: 都是O(1)。quicklist在头尾操作时直接操作对应的ziplist,无需遍历。

5. 应用场景

5.1 消息队列

bash
LPUSH queue:msg "msg1"
LPUSH queue:msg "msg2"
RPOP queue:msg    # "msg1"
RPOP queue:msg    # "msg2"

注意:Redis的list做消息队列有风险,缺少消息确认机制,建议使用Stream。

5.2 排行榜

bash
LPUSH rank:user:1001 100    # 用户得分
LRANGE rank:user:1001 0 -1 # 获取排名列表

5.3 最新消息列表

bash
LPUSH latest:news "news1"
LPUSH latest:news "news2"
LPUSH latest:news "news3"
LTRIM latest:news 0 99   # 只保留前100条
LRANGE latest:news 0 -1 # 获取最新100条

6. 实践问题

Q: List如何实现异步队列?

答案: 使用RPOPLPUSH或BLPOP/BRPOP实现可靠队列:

bash
# 生产者
RPUSH queue:task "task1"

# 消费者
BRPOP queue:task 0   # 阻塞获取
RPUSH queue:processed "task1"  # 处理完移到已处理队列

Q: 如何控制List内存占用?

答案: 通过配置参数控制:

  • list-max-ziplist-size:单个ziplist最大字节
  • list-max-ziplist-entries:单个ziplist最大元素数
  • list-compress-depth:两端不压缩的节点数

最后更新: