Sorted Sets trong Redis: Leaderboard, Ranking và Beyond

Photo of author

Văn Ngọc Tân

Bạn có biết?

Khi bạn chơi game và nhìn thấy bảng xếp hạng top 100 người chơi, hay khi bạn lướt Twitter và thấy “trending topics” — rất có thể Redis Sorted Sets đang làm việc phía sau. Đây là cấu trúc dữ liệu mạnh mẽ nhất trong Redis cho các bài toán xếp hạng và ưu tiên.

Sorted Sets là gì?

Sorted Set (ZSET) là một tập hợp các phần tử không trùng lặp, mỗi phần tử có một score (điểm số) dùng để sắp xếp. Khác với Set thông thường, Sorted Sets luôn được duy trì theo thứ tự tăng dần của score.

Tưởng tượng như một bảng xếp hạng:

  • Member — Tên người chơi (ví dụ: “player:1001”)
  • Score — Điểm số (ví dụ: 9500)
  • Redis tự động sắp xếp theo score từ thấp đến cao

Tại sao dùng Sorted Sets?

  • O(log N) cho insert, update, delete — siêu nhanh
  • O(log N + M) cho range queries — lấy top N phần tử
  • ✅ Tự động sắp xếp — không cần sort thủ công
  • ✅ Hỗ trợ range queries linh hoạt
  • ✅ Kết hợp được với Sets (union, intersect)

Các lệnh cơ bản

Thêm và lấy dữ liệu

# Thêm phần tử với score
ZADD leaderboard 9500 "player:1001"
ZADD leaderboard 8700 "player:1002"
ZADD leaderboard 9900 "player:1003"
ZADD leaderboard 7200 "player:1004"
ZADD leaderboard 9100 "player:1005"

# Thêm nhiều phần tử cùng lúc
ZADD leaderboard 8500 "player:1006" 9300 "player:1007"

# Lấy tất cả (tăng dần theo score)
ZRANGE leaderboard 0 -1
# 1) "player:1004" (7200)
# 2) "player:1006" (8500)
# 3) "player:1002" (8700)
# 4) "player:1005" (9100)
# 5) "player:1007" (9300)
# 6) "player:1001" (9500)
# 7) "player:1003" (9900)

# Lấy top 3 (với scores)
ZRANGE leaderboard 0 2 REV WITHSCORES
# 1) "player:1003" 2) "9900"
# 3) "player:1001" 4) "9500"
# 5) "player:1007" 6) "9300"

Lấy theo rank và score

# Xếp hạng của một phần tử (0-indexed, thấp nhất = 0)
ZRANK leaderboard "player:1001"
# (integer) 5  → xếp hạng 6 (từ dưới lên)

# Xếp hạng từ cao xuống thấp
ZREVRANK leaderboard "player:1001"
# (integer) 1  → xếp hạng 2 (từ trên xuống)

# Lấy score của một phần tử
ZSCORE leaderboard "player:1001"
# "9500"

# Đếm số phần tử có score trong khoảng
ZCOUNT leaderboard 8000 9500
# (integer) 5

Cập nhật score

# Tăng score
ZINCRBY leaderboard 500 "player:1002"
# "9200"  → player:1002 từ 8700 lên 9200

# Giảm score
ZINCRBY leaderboard -200 "player:1003"
# "9700"  → player:1003 từ 9900 xuống 9700

# Cập nhật score trực tiếp
ZADD leaderboard 10000 "player:1001" XX
# XX: chỉ cập nhật nếu member đã tồn tại

Xóa phần tử

# Xóa một phần tử
ZREM leaderboard "player:1004"

# Xóa theo rank (xóa 3 người cuối)
ZREMRANGEBYRANK leaderboard 0 2

# Xóa theo score (xóa người có điểm < 8000)
ZREMRANGEBYSCORE leaderboard 0 7999
Bảng xếp hạng game leaderboard hiển thị thứ hạng người chơi
Bảng xếp hạng game — Sorted Sets là cấu trúc dữ liệu lý tưởng cho leaderboard real-time

Range Queries nâng cao

Lấy theo khoảng score

# Lấy tất cả người chơi có điểm từ 9000 đến 10000
ZRANGEBYSCORE leaderboard 9000 10000
# 1) "player:1005" (9100)
# 2) "player:1007" (9300)
# 3) "player:1001" (9500)
# 4) "player:1003" (9700)

# Với scores
ZRANGEBYSCORE leaderboard 9000 10000 WITHSCORES

# Giới hạn kết quả (offset 0, lấy 2 kết quả)
ZRANGEBYSCORE leaderboard 9000 10000 LIMIT 0 2

# Lấy theo score từ cao xuống thấp
ZREVRANGEBYSCORE leaderboard 10000 9000

Lấy theo khoảng score mở

# Score > 9000 (dùng "(" để loại trừ)
ZRANGEBYSCORE leaderboard (9000 +inf

# Score >= 9000 và < 10000
ZRANGEBYSCORE leaderboard 9000 (10000

# Score < 9000
ZRANGEBYSCORE leaderboard -inf (9000

Use Cases thực tế

1. Game Leaderboard

# Người chơi đạt điểm mới
ZADD game:leaderboard 15200 "player:1001"

# Lấy top 10
ZREVRANGE game:leaderboard 0 9 WITHSCORES

# Xếp hạng của một người chơi
ZREVRANK game:leaderboard "player:1001"

# Lấy 5 người xung quanh rank của mình
my_rank = ZREVRANK game:leaderboard "player:1001"
ZREVRANGE game:leaderboard (my_rank-2) (my_rank+2) WITHSCORES

2. Rate Limiting theo thời gian

# Ghi nhận request với timestamp làm score
ZADD rate:user:1001 1711785600 "req:abc123"
ZADD rate:user:1001 1711785601 "req:def456"

# Xóa requests cũ hơn 1 phút
ZREMRANGEBYSCORE rate:user:1001 0 (now - 60)

# Đếm requests trong 1 phút qua
ZCOUNT rate:user:1001 (now - 60) now

# Nếu > 100 requests → reject
if ZCOUNT rate:user:1001 (now - 60) now > 100:
    return "Rate limit exceeded"

3. Trending Topics

# Khi có hashtag mới, tăng score
ZINCRBY trending:hashtags 1 "#redis"
ZINCRBY trending:hashtags 1 "#python"
ZINCRBY trending:hashtags 1 "#redis"

# Lấy top 10 trending
ZREVRANGE trending:hashtags 0 9 WITHSCORES
# 1) "#redis"    2) "2"
# 3) "#python"   4) "1"

4. Priority Queue

# Thêm job với priority (score thấp = ưu tiên cao)
ZADD queue:jobs 1 "send_email:user123"
ZADD queue:jobs 3 "generate_report"
ZADD queue:jobs 2 "resize_image:456"

# Lấy job ưu tiên cao nhất
job = ZRANGE queue:jobs 0 0
# "send_email:user123"

# Xóa sau khi xử lý
ZREM queue:jobs "send_email:user123"

5. Time-based Feed

# Thêm bài viết với timestamp làm score
ZADD feed:user:1001 1711785600 "post:123"
ZADD feed:user:1001 1711785900 "post:456"
ZADD feed:user:1001 1711786200 "post:789"

# Lấy 20 bài mới nhất
ZREVRANGE feed:user:1001 0 19

# Lấy bài trong 24 giờ qua
yesterday = now - 86400
ZRANGEBYSCORE feed:user:1001 yesterday now

Sorted Sets vs Lists vs Sets

Tiêu chí Lists Sets Sorted Sets
Sắp xếp Theo thứ tự thêm Khôngcó thứ tự Theo score
Trùng lặp ✅ Cho phép ❌ Không ❌ Không
Thêm O(1) O(1) O(log N)
Lấy top N O(N) O(N log N) O(log N + M)
Use case Queue, Stack Tags, Unique items Leaderboard, Ranking

Best Practices

  1. Dùng score hợp lý — Timestamp cho time-based, điểm số cho ranking
  2. Giới hạn kích thước — Dùng ZREMRANGEBYRANK để giữ leaderboard nhỏ
  3. Tránh ZRANGE 0 -1 — Với sorted set lớn, dùng LIMIT
  4. Kết hợp EXPIRE — Đặt TTL cho rate limiting keys
  5. Dùng REV — ZREVRANGE cho leaderboard (cao xuống thấp)

Bước tiếp theo

Bạn đã nắm vững Sorted Sets trong Redis! Tiếp theo, chúng ta sẽ tìm hiểu về Redis Streams — cơ chế event log thế hệ mới, mạnh mẽ hơn Pub/Sub với consumer groups và message persistence.

👉 Bài tiếp theo: Redis Streams — Event Log thế hệ mới

0 0 đánh giá
Đánh giá bài viết
Theo dõi
Thông báo của
guest
0 Góp ý
Cũ nhất
Mới nhất Được bỏ phiếu nhiều nhất
Phản hồi nội tuyến
Xem tất cả bình luận