跳到主要内容

有序集合

2024年04月08日
柏拉文
越努力,越幸运

一、认识


RedisZset(有序集合,Sorted Set)是一种类似于 Set 的数据结构,它和 Set 相比最大的区别在于 Zset 中的元素是有序的。每个元素在 Zset 中都会关联一个 分数(score,这个分数用于排序,因此可以在 Zset 中进行基于分数的排序操作。Zset 结合了 Set 和列表(List)的特性:去重和有序性。

Zset 结构特点: 有序性, Zset 中的每个元素都有一个与之关联的分数(score)。分数是一个 双精度浮点数,Redis 会根据分数来对 Zset 中的元素进行排序,默认是按从小到大的顺序, 如果两个元素具有相同的分数,它们在 Zset 中的顺序是根据元素的字典顺序来决定的; 唯一性, Zset 中的每个元素都是唯一的, 你不能在同一个 Zset 中插入重复的元素,但可以更新元素的分数。元素的名字(值)是唯一的,但分数可以有重复; 高效操作, Redis 使用跳表(skiplist)和哈希表(hash table)结合的方式来实现 Zset,支持高效的元素插入、删除、查找和范围查询; 范围查询, 由于 Zset 是有序的,Redis 提供了高效的命令来执行范围查询, 你可以按照分数范围或者按排名范围获取 Zset 中的元素, 支持按分数区间、排名区间等多种方式进行查询。

Zset 优化策略: Redis 使用 跳表(Skiplist哈希表(Hash Table 结合的方式来实现 Zset(有序集合),这是为了兼顾高效的元素插入、删除和范围查询操作。跳表和哈希表各自有不同的优点,Redis 将两者结合,以在性能和内存使用上达到最佳平衡。具体来说,Redis 中的每个 Zset 元素都有一个唯一的值(成员)和一个关联的分数(score)。Zset 的每个成员在跳表和哈希表中都有对应的存储。具体工作流为: 插入元素时, Redis 会将元素的值和分数同时存入跳表和哈希表中, 在跳表中,元素根据其分数被插入到合适的位置,保持跳表的有序性, 在哈希表中,元素的值作为 key,分数作为 value 存储,确保元素的快速查找; 删除元素时, Redis 会从跳表和哈希表中删除该元素, 在跳表中,删除元素的过程是通过查找其分数所在的位置,删除该节点, 在哈希表中,删除对应的 key-value 键值对; 查找元素时, Redis 会首先通过哈希表检查元素是否存在。这一步是 O(1) 时间复杂度的操作, 如果元素存在,Redis 会在跳表中找到对应的节点,从而获得该元素的分数,或者获取它在 Zset 中的排名; 更新元素分式时, 如果需要更新元素的分数,Redis 会在哈希表中更新该元素的分数, 在跳表中移除旧的分数节点,并将元素插入到新的位置,从而保持跳表的顺序; 范围查询, 在范围查询(例如,按分数范围获取元素)时,Redis 会在跳表中高效地查找并返回符合条件的元素, 由于跳表的排序结构,可以在 O(log N) 时间内完成分数范围的查找

  • 跳表Skiplist: 是一种有序的数据结构,用于快速查找、插入和删除元素,时间复杂度为 O(log N),因此适合用于实现有序集合。跳表由多个层级的链表组成。每个元素在不同的层级链表中都有索引,元素通过不同层级的链表向下进行连接。跳表的底层链表包含所有的元素,每上一层链表都只包含部分元素,通过逐层“跳跃”可以加速查询。在 Zset 中,跳表的每个节点存储着一个元素的分数(score)和一个指向该元素的指针(成员值)。通过分数排序,跳表能高效地获取排名、范围查询等操作。跳表, 用于实现 Zset 的有序性,按照分数(score)排序。

  • 哈希表Hash Table: 提供了 O(1) 时间复杂度的查找、插入和删除操作,但它并不提供排序功能。因此,如果我们仅使用哈希表,就无法满足 Zset 的排序要求。哈希表用于存储 Zset 元素的成员(即值)。哈希表的 key 是元素的值,value 是元素的分数。由于哈希表查找是 O(1) 时间复杂度,所以它可以高效地检查元素是否存在于 Zset 中,并避免重复插入。哈希表的作用主要是帮助 Redis 快速查找 Zset 中的成员,并且能高效地进行元素的插入、删除操作。每次插入或删除元素时,Redis 会同时更新哈希表和跳表。哈希表, 用于提供高效的成员查找,避免重复的成员插入。