有序集合
一、认识
Redis
的 Zset
(有序集合,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
会同时更新哈希表和跳表。哈希表, 用于提供高效的成员查找,避免重复的成员插入。