Skip to content

Commit c77a0b9

Browse files
committed
post: redis ordered set & kmp algorithm
1 parent 6753de5 commit c77a0b9

File tree

26 files changed

+1887
-93
lines changed

26 files changed

+1887
-93
lines changed
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
+++
2+
date = '2025-08-30T8:00:00+08:00'
3+
draft = false
4+
title = 'Redis Ordered Set'
5+
tags = ['Redis', 'NoSQL']
6+
+++
7+
8+
Redis 的有序集和(ordered set)同时具有"有序"和"集和"两种性质, 这种结构中每个元素都由一个成员和一个与成员相关联的分值组成, 其中成员与字符串方式存储, 而分值以64位双精度浮点数格式存储.
9+
10+
例如下面一个记录薪水的集和:
11+
| 成员 | 分值 |
12+
| :--- | :--- |
13+
| "perter" | 3500 |
14+
| "bob" | 3800 |
15+
| "jack" | 4500 |
16+
| "tom" | 5000 |
17+
| "mary" | 5500 |
18+
19+
与集和一样, 有序集和中的元素都是唯一的, 同时, 成员将按照分值大小进行排序.
20+
有序集和分值除了可以是数字外, 还可以是字符串 "+inf" 或者 "-inf", 这两个特殊值分别表示无穷大和无穷小.
21+
虽然有序集和的成员不可相同, 但是分值可以是相同的, 当两个或多个成员拥有相同的分值时,Redis 将按照这些成员在字典序中的大小对其进行排列.
22+
23+
有序集合是Redis提供的所有数据结构中最为灵活的一种, 它可以以多种不同的方式获取数据, 比如根据成员获取分值、根据分值获取成员、根据成员的排名获取成员、根据指定的分值范围获取多个成员等.
24+
25+
- ZADD: 添加或更新成员
26+
```
27+
ZADD sorted_set socre number [score number ...]
28+
```
29+
默认情况下, ZADD 命令将返回成功添加的新成员数量作为返回值, 对于更新操作会返回0(未添加新成员).
30+
使用 `XX | NX` 选项来显示地指示命令 只更新 或 只添加操作
31+
```
32+
ZADD sorted [XX|NX] socre member [socre member ...]
33+
```
34+
若要返回所有被修改的成员数量(新添加 + 更新数量), 可使用 CH 选项
35+
```
36+
ZADD sorted_set [CH] socre number [score number ...]
37+
```
38+
复杂度: O(M * log(N)) 其中 M 为给定成员数量, N 为有序集和的成员数量
39+
40+
- ZREM: 移除指定的成员
41+
```
42+
ZREM sorted_set member [member ...]
43+
```
44+
ZREM 会返回被移除成员的数量作为返回值, 如果给定的某个成员不存在, 则会自动忽略该成员.
45+
复杂度: O(M * log(N)), 其中 M 为给定成员的数量, N 为有序集和中包含的成员数量.
46+
47+
- ZSCORE: 获取成员的分值
48+
```
49+
ZSCORE sorted_set member
50+
```
51+
如果给定的 member 不存在, 将返回空值
52+
复杂度: O(1)
53+
54+
- ZINCRBY: 对成员的分值进行自增或自减操作
55+
```
56+
ZINCRBY sorted_set increment member
57+
```
58+
该命令完成操作后, 将返回成员当前分值, increment 为正数时就是自增操作, 为负数时就是自减操作.
59+
如果命令给定的有序集和或者成员不存在, 则相当于 ZADD 命令, 会自动创建对应的值.
60+
复杂度: O(log(N))
61+
62+
- ZCARD: 获取有序集和的大小
63+
```
64+
ZCARD sorted_set
65+
```
66+
返回集和中成员数量, 若不存在则返回0.
67+
复杂度: O(1)
68+
69+
- ZRANK、ZREVRANK: 取得给定成员在有序集和中的排名
70+
```
71+
ZRANK sorted_set member # 升序
72+
ZREVRANK sorted_set member # 降序
73+
```
74+
若给定的集和或者成员不存在, 则返回空值.
75+
复杂度: O(log(N))
76+
77+
- ZRANGE、ZREVRANGE: 获取索引范围内的成员
78+
```
79+
ZRANGE sorted_set start end # 升序
80+
ZREVRANGE sorted_set start end # 降序
81+
```
82+
索引范围为 [start, end], 即这两个索引上的成员也会包含在命令返回的结果当中, 索引从0开始. 此外, 还可以使用负数索引, 索引最后一个从 -1 开始.
83+
默认情况下, ZRANGE 和 ZREVRANGE 命令只会返回指定索引范围内的成员, 如果用户想要获取这些成员以及其分值, 可以使用 WITHSCORES 选项
84+
```
85+
ZRANGE sorted_set start end [WITHSCORES]
86+
ZREVRANGE sorted_set start end [WITHSCORES]
87+
```
88+
如果给定的有序集和不存在, 则会返回一个空列表.
89+
复杂度: O(M + log(N)), 其中 M 为命令返回成员数量, N 为有序集和成员数量.
90+
91+
92+
### 示例: 排行榜
93+
在网站上经常会有各种各样的排行榜. 比如, 音乐网站上可能有试听排行榜, 下载排行榜等. 而视屏网站上可能看到观看排行榜, 收藏排行榜等. 甚至 GitHub 项目托管网站也有 star 排行榜.
94+
95+
下面代码实现了一个排行榜程序:
96+
- 这个程序使用 ZADD 命令向排行榜中添加被排序的元素及分数, 并使用 ZREVRANK 命令获取元素在排行榜中的排名, 以及使用 ZSCORE 命令去获取元素的分数
97+
- 当不再需要对某个元素进行排序的时候, 可以调用 ZREM 命令实现 `remove()` 方法, 从排行榜中移除该元素
98+
- 如果用户想要修改某个被排序的元素的分数, 则调用 ZINCRBY 命令实现的 `increase_score()` 方法或者 `decrease_score()` 方法即可
99+
- 当用户想要获取排行榜前N位的元素及其分数时, 只需要调用由 ZREVRANGE 命令实现的 `top()` 方法即可
100+
101+
```Python
102+
class RankingList:
103+
def __init__(self, client, key):
104+
self.client = client
105+
self.key = key
106+
107+
def set_score(self, item, score):
108+
"""为排行榜中指定元素设置分数, 不存在的元素会被添加到排行榜中"""
109+
self.client.zadd(self.key, {item: score})
110+
111+
def get_score(self, item):
112+
"""获取排行榜中指定元素的分数"""
113+
return self.client.zscore(self.key, item)
114+
115+
def remove(self, item):
116+
"""从排行榜中删除指定的元素"""
117+
self.client.zrem(self.key, item)
118+
119+
def increase_score(self, item, increment):
120+
"""将给定的元素分数增加 increment 分"""
121+
self.client.zincrby(self.key, increment, item)
122+
123+
def decrease_score(self, item, decrement):
124+
"""将给定的元素减少 increment 分"""
125+
self.client.zincrby(self.key, 0-decrement, item)
126+
127+
def get_rank(self, item):
128+
"""获取给定元素排行榜中的排名"""
129+
rank = self.client.zrevrank(self.key, item)
130+
if rank is not None:
131+
return rank + 1 # Redis 排名从0开始
132+
133+
def top(self, n, with_score=False):
134+
"""获取排行榜中得分最高的元素"""
135+
return self.client.zrevrange(self.key, 0, n-1, withsores=with_score)
136+
```
137+
138+
- ZRANGEBYSCORE、ZREVRANGEBYSCORE: 获取分值范围内的成员
139+
```
140+
ZRANGEBYSCORE sorted_set min max
141+
ZREVRANGEBYSCORE sorted_set max min
142+
```
143+
命令的 min 参数和 max 参数分别用于指定用户想要获取的最小分值和最大分值, 要注意这两条命令接受最大最小值的顺序正好相反.
144+
该命令也可以使用 WITHSCORES 参数来同时获取成员及其分值
145+
```
146+
ZRANGEBYSCORE sorted_set min max [WITHSCORES]
147+
ZREVRANGEBYSCORE sorted_set max min [WITHSCORES]
148+
```
149+
默认情况下, 该命令会返回范围内的所有成员, 有时成员数量很多, 就可以使用 LIMIT 选项来限制命令返回的成员数量.
150+
```
151+
ZRANGEBYSCORE sorted_set min max [LIMIT offset count]
152+
ZREVRANGEBYSCORE sorted_set min max [LIMIT offset count]
153+
```
154+
其中, offset 是指从满足 min 和 max 的元素中, 跳过前面的 offset 个元素, count 是从 offset 之后开始, 返回接下来的 count 个元素.
155+
156+

0 commit comments

Comments
 (0)