redis is a simple way to implement leaderboards

  • 2020-06-23 02:15:38
  • OfStack

1 introduction

To achieve a typesetting list, we usually think of mysql's order by simply rough out. But is it really elegant?

It is well known that the database is the bottleneck of the system. If you were given a list of millions and asked to rank them, it would take an awful lot of time, 10 out of 10.

Instead of caching, order by forces the use of indexes. But is it really elegant?

2 Redis's ranking

When we analyze 1 leaderboard and 1 user and 1 rank, which means we have to re-weight, we think of Set, a data structure of Java. But Set is disorganized again. Is there a structure that can preserve only one element and order.

Fortunately, there is. Redis's ZSet is one such data structure. The elements in Zset are unique, ordered, sorted by fraction from smallest to largest. As an excellent crud programmer, we started from these aspects to understand the zset structure.

2.1 Increase and modification of ZADD

The time complexity is O(M*log(N)), N is the cardinality of the ordered set, and M is the number of new members successfully added. Insert if key does not exist, update if it does.

Use the following:

redis> ZADD page_rank 10
(integer) 1


page_rankde is key, 10 is the fraction, is value

2.2 ZRANK query

Time complexity: O(log(N))

Use the following:

redis> ZRANGE salary 0 -1    #  Show all members 
1) "peter"
2) "tom"
3) "jack"

redis> ZRANK salary tom    #  According to  tom  Salary ranking, no 2
(integer) 1


key of salary, tom is value, just enter the specific key and value to query the corresponding ranking.

2. del delete

Use the del command of redis directly

3 Score Design

Going back to the implementation of the leaderboard, what matters is how the score is designed to be implemented using the zset structure. Analyze the design of the leaderboard. If the ranking is designed for one dimension, such as the number of gold COINS, then simply invert the number as a score. The inverse is because zset sort from small to large by default.

The implementation is as follows:

public Double getScore( Long oneDayGoldBean) {
  String score = String.valueOf(oneDayGoldBean);
  return -Double.valueOf(score);

If the rankings are designed according to two dimensions, such as the number of gold COINS and the duration. Since score is a parameter of type double, you can design time as a decimal, subtract the number of milliseconds per day from the number of milliseconds per day as a decimal, then concatenate it as a string, and take the reverse as score.

The implementation is as follows:

public Double getScore( Long oneDayGoldBean, Long useTime) {
  String value1 = String.valueOf(oneDayGoldBean/1.0);
  long todayEndSS = getTodayEndSS(useTime);
  String value2 = String.valueOf(todayEndSS);
  String score =value1+value2;
  return -Double.valueOf(score);

private long getTodayEndSS(long current){
  // The number of milliseconds today at zero minutes zero seconds 
  long zero = 0L;
  // Today, 23 point 59 points 59 Milliseconds in a second 
  long twelve = zero + 24 * 60 * 60 * 1000;
  return (twelve - current) / 1000;

4 Code implementation

public boolean insertLeaderboard() {
  Double score = getScore(100l, 1000l);
  return redisTemplate.opsForZSet().add("leaderboard", "1", score);

public Set checkLeaderboard() {
  // 0 -1  Returns all of them value the set value 
  return redisTemplate.opsForZSet().range("leaderboard", 0, -1);

The source code


Related articles: