在 Redis 中,List 结构是一种线性列表数据结构,支持在两端进行快速的插入和删除操作。Redis 的 List 内部实现主要依赖于两种不同的底层数据结构:压缩列表(ziplist) 和 双端链表(linked list)。选择哪种数据结构取决于 List 中元素的数量以及每个元素的大小。
压缩列表(Ziplist)
- 当 List 中的元素数量较少,并且每个元素都很小时,Redis 会使用 ziplist 来存储数据。
- Ziplist 是一种特殊编码的双向链表,它通过紧凑地存储数据来节省空间。这种结构将多个元素连续存储在一起,没有额外的指针开销。
- Ziplist 对内存的使用非常高效,因为它是连续存储的,并且对于小对象来说,不需要额外的空间来存储元数据或指针。
- 但是,当需要频繁地对中间元素进行修改时,ziplist 的性能可能会下降,因为它可能需要移动大量数据来保持内存中的连续性。
双端链表(Linked List)
- 当 List 中的元素数量较多或者单个元素较大时,Redis 会切换到双端链表的实现方式。
- 双端链表由一系列节点组成,每个节点包含一个值和两个指向相邻节点的指针(前驱和后继)。
- 这种结构非常适合在 List 的头部或尾部进行快速的插入和删除操作,时间复杂度为 O(1)。
- 相比于 ziplist,双端链表对内存的使用效率较低,因为它需要额外的指针来链接各个节点,但它更适合处理大量的元素或大型元素。
自动转换
- Redis 会在适当的时机自动在这两种数据结构之间进行转换。
- 转换的触发条件是根据配置参数
list-max-ziplist-entries
和list-max-ziplist-value
来决定的。如果 List 中的元素数量超过了list-max-ziplist-entries
或者某个元素的大小超过了list-max-ziplist-value
,那么 Redis 就会将该 List 从 ziplist 转换成双端链表。 - 同样地,如果双端链表中的元素数量减少到了阈值以下,并且所有元素都足够小,Redis 也会将其转换回 ziplist 以节省内存。
操作命令
LPUSH
和RPUSH
分别用于向 List 的左侧和右侧添加元素。LPOP
和RPOP
分别用于从 List 的左侧和右侧移除并返回元素。LRANGE
用于获取 List 中指定范围内的元素。LINDEX
用于获取 List 中指定索引位置的元素。LLEN
用于获取 List 中元素的数量。
通过这些命令,用户可以灵活地操作 List 数据结构。Redis 的这种设计允许它既能有效地利用内存,又能提供高效的访问和修改操作。