Redis 的有序集和(ordered set)同时具有"有序"和"集和"两种性质, 这种结构中每个元素都由一个成员和一个与成员相关联的分值组成, 其中成员与字符串方式存储, 而分值以64位双精度浮点数格式存储.

例如下面一个记录薪水的集和:

成员分值
“perter”3500
“bob”3800
“jack”4500
“tom”5000
“mary”5500

与集和一样, 有序集和中的元素都是唯一的, 同时, 成员将按照分值大小进行排序.
有序集和分值除了可以是数字外, 还可以是字符串 “+inf” 或者 “-inf”, 这两个特殊值分别表示无穷大和无穷小.
虽然有序集和的成员不可相同, 但是分值可以是相同的, 当两个或多个成员拥有相同的分值时,Redis 将按照这些成员在字典序中的大小对其进行排列.

有序集合是Redis提供的所有数据结构中最为灵活的一种, 它可以以多种不同的方式获取数据, 比如根据成员获取分值、根据分值获取成员、根据成员的排名获取成员、根据指定的分值范围获取多个成员等.

示例: 排行榜

在网站上经常会有各种各样的排行榜. 比如, 音乐网站上可能有试听排行榜, 下载排行榜等. 而视屏网站上可能看到观看排行榜, 收藏排行榜等. 甚至 GitHub 项目托管网站也有 star 排行榜.

下面代码实现了一个排行榜程序:

PYTHON
class RankingList:
    def __init__(self, client, key):
        self.client = client
        self.key = key

    def set_score(self, item, score):
        """为排行榜中指定元素设置分数, 不存在的元素会被添加到排行榜中"""
        self.client.zadd(self.key, {item: score})

    def get_score(self, item):
        """获取排行榜中指定元素的分数"""
        return self.client.zscore(self.key, item)

    def remove(self, item):
        """从排行榜中删除指定的元素"""
        self.client.zrem(self.key, item)

    def increase_score(self, item, increment):
        """将给定的元素分数增加 increment 分"""
        self.client.zincrby(self.key, increment, item)

    def decrease_score(self, item, decrement):
        """将给定的元素减少 increment 分"""
        self.client.zincrby(self.key, 0-decrement, item)

    def get_rank(self, item):
        """获取给定元素排行榜中的排名"""
        rank = self.client.zrevrank(self.key, item)
        if rank is not None:
            return rank + 1 # Redis 排名从0开始

    def top(self, n, with_score=False):
        """获取排行榜中得分最高的元素"""
        return self.client.zrevrange(self.key, 0, n-1, withsores=with_score)
Click to expand and view more

示例: 时间线

在互联网上, 很多网站都会根据发布的内容来对时间进行排序:

类似的情形还有很多, 通过对这类行为进行抽象, 写出下面的时间线程序:

PYTHON
class Timeline:
    def __init__(self, client, key):
        self.client = client
        self.key = key

    def add(self, item, time):
        """将元素添加到时间线中"""
        self.client.zadd(self.key, {item: time})

    def remove(self, item):
        """将元素从时间线中移除"""
        self.client.zrem(self.key, item)

    def count(self):
        """返回时间线包含的元素数量"""
        return self.client.zcard(self.key)

    def pagging(self, number, count, with_time=False):
        """
        按照每页 count 个元素, 取出时间线第 number 页上的所有元素, 将元素按照时间戳逆序排序
        with_time 表示是否返回时间信息
        """
        start_index = (number - 1) * count
        end_index = number * count - 1
        return self.client.zrevrange(self.key, start_index, end_index, with_time)

    def fetch_by_time_range(self, min_time, max_time, number, count, with_time=False):
        """
        按照每页 count 个元素, 取出时间线第 number 页上的所有元素, 按照时间戳逆序排序
        with_time 表示是否返回时间信息
        """
        start_index = (number - 1) * count
        end_index = start_index + count - 1
        return self.client.zrevrangebyscore(self.key, max_time, min_time, start_index, end_index, withscores=with_time)
Click to expand and view more

还有很多其他关于有序集和的内容, 这里就不再讨论了.

Start searching

Enter keywords to search articles

↑↓
ESC
⌘K Shortcut