实现一段 "开箱即用" 的 Python 脚本,只用 Redis 原生 Sorted Set(zset) 实现:
- 实时积分榜(可扩展到任何带分数的排行榜)
- 支持 并列排名 (相同分数同榜)、获取 TopN 、查询用户所在页
- 支持 分数更新 、用户淘汰 、分页翻页
- 全部封装成函数,复制即运行。
leaderboard_demo.py
python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
纯 Redis Sorted Set 实现排行榜
功能:更新分数、TopN、分页、并列排名、查用户排名
"""
import redis
from math import ceil
# ----------- 配置 -----------
REDIS_CFG = dict(host="127.0.0.1", port=6379, db=0, decode_responses=True)
KEY = "game:score_rank" # 排行榜 key
PAGE_SIZE = 10 # 翻页大小
# ----------------------------
class LeaderBoard:
def __init__(self, key: str, r: redis.Redis):
self.key = key
self.r = r
# 更新/新增分数(可增量)
def set_score(self, user: str, score: int, incr: bool = False):
if incr:
return int(self.r.zincrby(self.key, score, user))
self.r.zadd(self.key, {user: score})
return score
# 删除用户
def remove(self, *users):
return self.r.zrem(self.key, *users)
# 获取 TopN(含分数)
def top(self, n: int, with_scores=True):
"""
返回 [(user, score), ...] 分数从高到低
"""
return self.r.zrevrange(self.key, 0, n - 1, withscores=with_scores)
# 获取用户排名(分数相同并列,排名从 1 开始)
def rank_of(self, user):
"""
并列排名做法:先拿分数,再统计 > 该分数的人数
"""
score = self.r.zscore(self.key, user)
if score is None:
return None
# zrevrangebyscore 闭区间,+inf 到 (score+1) 开区间
rank = self.r.zcount(self.key, f"({score}", "+inf") + 1
return int(rank)
# 分页读取(闭区间,左右都含)
def page(self, page: int, page_size: int = PAGE_SIZE, with_scores=True):
start = (page - 1) * page_size
stop = start + page_size - 1
return self.r.zrevrange(self.key, start, stop, withscores=with_scores)
# 总页数
def total_pages(self, page_size: int = PAGE_SIZE):
total = self.r.zcard(self.key)
return ceil(total / page_size) if total else 1
# 打印排行榜(方便看)
def print_top(self, n: int = 20):
data = self.top(n)
print(f"-------- 前 {n} 名 --------")
for rank, (user, score) in enumerate(data, 1):
print(f"{rank:>2} | {user:<10} | {score}")
# ----------------- 演示 -----------------
def main():
r = redis.Redis(**REDIS_CFG)
lb = LeaderBoard(KEY, r)
# 1. 模拟 30 个用户写分
import random
random.seed(42)
users = [f"user{i:02d}" for i in range(1, 31)]
for u in users:
lb.set_score(u, random.randint(100, 1000))
# 2. 展示 Top10
lb.print_top(10)
# 3. 查某个用户
who = "user07"
score = r.zscore(KEY, who)
rank = lb.rank_of(who)
print(f"\n{who} 分数={score} 并列排名={rank}")
# 4. 翻页示例
page = 2
print(f"\n第 {page} 页(每页 {PAGE_SIZE}):")
for idx, (user, score) in enumerate(lb.page(page), 1):
real_rank = (page - 1) * PAGE_SIZE + idx
print(f"{real_rank:>2} | {user:<10} | {score}")
# 5. 更新分数 & 再看榜
print("\n--- user07 暴击 +1000 ---")
lb.set_score("user07", 1000, incr=True)
lb.print_top(10)
if __name__ == "__main__":
main()