Brother, the ranking list of the glory of the king is realized through Redis?

insert image description here

In Glory of Kings, we will play ranking matches, and what everyone pays most attention to is your rank and your ranking among friends.

As a programmer, have you ever thought about how this rank ranking is realized? Will understanding its implementation principle be helpful to the upper score?

Just look at my rankings and you'll know, the answer is no lol.

1. Ranking Design Scheme

From a technical point of view, we can choose different technical solutions to design the leaderboard according to the type of the leaderboard.

1. Database direct sorting

In low-data-volume scenarios, there are many ranking lists that use database direct sorting.

For example, let's make a programmer salary ranking list to see which city has the richest programmers.

According to data from a recruitment website, the average monthly salary of programmers in China in 2023 will be 12,000 yuan, the highest in Beijing, reaching 21,000 yuan, and the lowest in Xi'an, only 7,000 yuan.

The following is the average monthly salary list of programmers in several major cities:

  1. Beijing: 21,000 yuan
  2. Shanghai: 19,000 yuan
  3. Shenzhen: 18,000 yuan
  4. Hangzhou: 16,000 yuan
  5. Guangzhou: 15,000 yuan
  6. Chengdu: 13,000 yuan
  7. Nanjing: 12,000 yuan
  8. Wuhan: 11,000 yuan
  9. Xi'an: 7,000 yuan

As you can see from this list, I'm sorry for dragging everyone down.

This can be done with a database, and there are not many cities in total, and if you come to the top 100, it will be exhausted.

For data of this magnitude, adding indexes and using top well will not exceed 100ms. In the case of small requests and small amounts of data, there is no problem at all in using the database as a leaderboard.

2. King of Glory Friends Ranking

This type of list is ranked according to the data of your own friends. This type of list does not need to store the data of each friend in the database, but obtains your own friend list, obtains the real-time score of the friend, and uploads it locally on the client. Perform local sorting to display the King of Glory friend rankings, because it takes time to pull data from the database, for example, once a minute, because it is not pulled in real time, this type of list puts less pressure on the database.

Let's explore how to use Redis in Java to achieve high-performance leaderboards?

2. Redis implements the counter

1. What is the counter function?

A counter is a common function used to record the number of occurrences of some kind of event. In the application, the counter can be used to track user behavior, count the number of clicks, the number of views, etc.

For example, you can use counters to record the number of times an article has been read, or to count the number of times a product has been purchased. By tracking counts, you can understand trends in your data and make smarter decisions.

2. Redis implements the principle of the counter

Redis is a high-performance in-memory database that provides rich data structures and commands, and is very suitable for implementing counter functions. In Redis, we can use the string data type and related commands to implement counters.

(1) Use the INCR command to realize the counter

A Redis INCRcommand is an atomic operation that increments a number stored in a key by 1. If the key does not exist, it will be created and initialized to 0 before being incremented. This allows us to easily implement the counter functionality.

Let's demonstrate how to use Redis INCRcommands to implement counters through Java code:

import redis.clients.jedis.Jedis;

public class CounterExample {
    
    

    public static void main(String[] args) {
    
    
        Jedis jedis = new Jedis("localhost", 6379);

        String articleId = "article:123";
        String viewsKey = "views:" + articleId;

        // 使用INCR命令递增计数
        long views = jedis.incr(viewsKey);

        System.out.println("Article views: " + views);

        jedis.close();
    }
}

In the above code, we have used the Jedis client library to connect to the Redis server and INCRincrement a views:article:123counter stored in the key using the command. Every time this code is executed, the counter is incremented and we can easily get the number of views of the article.

(2) Use the INCRBY command to realize the counter

In addition to a single increment of 1, we can also use INCRBYthe command to increase the specified amount at once. This is very useful for some scenarios that need to increase a large amount at once.

Let's continue with the example above, but this time we use INCRBYthe command to increment the view count:

import redis.clients.jedis.Jedis;

public class CounterExample {
    
    

    public static void main(String[] args) {
    
    
        Jedis jedis = new Jedis("localhost", 6379);

        String articleId = "article:123";
        String viewsKey = "views:" + articleId;

        // 使用INCRBY命令递增计数
        long views = jedis.incrBy(viewsKey, 10); // 一次增加10

        System.out.println("Article views: " + views);

        jedis.close();
    }
}

In the above code, we used INCRBYthe command to increase the number of article views by 10 at one time. This is useful in scenarios where statistics need to increment a large number of counts at once.

By using Redis's INCRand INCRBYcommands, we can easily implement high-performance counter functions. The atomic operation of these commands ensures the accuracy of counting, and is very suitable for scenarios that require frequent update counts.

3. Realize the "Glory of the King" ranking list through Redis?

I don't know whether the leaderboard of Glory of Kings is made with Redis, but in my project, the leaderboard is indeed made with Redis, which is a real deal.

did you see it? A man who masters algorithms is invincible wherever he goes.

1. What is the ranking function?

The leaderboard is a common function used to record the ranking of certain items, usually sorting the items according to certain rules. In social media, games, e-commerce and other fields, the leaderboard function is widely used to enhance user participation and competitiveness. For example, a social media platform can display the most active users through a leaderboard, and a game can display a player's score ranking, etc.

2. Redis implements the principle of leaderboard

In Redis, we can use the Sorted Set data structure to achieve an efficient leaderboard function. An ordered set is a collection of key-value pairs, each member is associated with a score, and Redis will sort the members according to their scores. This allows us to easily implement leaderboard functionality.

(1) Use the ZADD command to add members and scores

Redis ZADDcommands are used to add members and corresponding scores to sorted sets. If the member already exists, its score can be updated. Let's demonstrate how to use ZADDcommands to add members and scores to the leaderboard through Java code:

import redis.clients.jedis.Jedis;

public class LeaderboardExample {
    
    

    public static void main(String[] args) {
    
    
        Jedis jedis = new Jedis("localhost", 6379);

        String leaderboardKey = "leaderboard";
        String player1 = "PlayerA";
        String player2 = "PlayerB";

        // 使用ZADD命令添加成员和分数
        jedis.zadd(leaderboardKey, 1000, player1);
        jedis.zadd(leaderboardKey, 800, player2);

        jedis.close();
    }
}

In the above code, we use ZADDthe command to add PlayerAand PlayerBas members to leaderboardthe sorted set and assign scores to each. In this way, we have created a record of two players in the leaderboard.

(2) Use the ZINCRBY command to update member scores

In addition to adding members, we can also use ZINCRBYcommands to update the scores of existing members. This is useful for updating scores in leaderboards in real time.

Let's continue with the example above, but this time we'll use ZINCRBYthe command to increase the player's score:

import redis.clients.jedis.Jedis;

public class LeaderboardExample {
    
    

    public static void main(String[] args) {
    
    
        Jedis jedis = new Jedis("localhost", 6379);

        String leaderboardKey = "leaderboard";
        String player1 = "PlayerA";
        String player2 = "PlayerB";

        // 使用ZINCRBY命令更新成员分数
        jedis.zincrby(leaderboardKey, 200, player1); // 增加200分

        jedis.close();
    }
}

In the above code, we have used ZINCRBYthe command to PlayerAincrease the score by 200 points. This method can be used to record changes in players' scores, points, etc., so as to update the leaderboard data in real time.

By using Redis ordered collections and ZADDcommands ZINCRBYsuch as , we can easily implement high-performance leaderboard functions. The atomic operation of these commands ensures the accuracy and consistency of the ranking, which is very suitable for scenarios that require frequent update of the leaderboard.

My strongest Baili, 12-5-6, can lose this? Something must be wrong, server performance?

4. Performance optimization of counters and leaderboards

In this section, we will focus on how to optimize the performance of the counter and leaderboard functions in high-concurrency scenarios. With sound strategies and techniques, we can ensure that the system maintains high performance while handling large amounts of data and user requests.

1. How to optimize the performance of the counter?

(1) Use Redis transaction

In high concurrency scenarios, multiple users may operate on the same counter at the same time, which may cause concurrency conflicts. To avoid this situation, Redis transactions can be used to ensure atomic operations. A transaction wraps a set of commands in an atomic operation, ensuring that either all of these commands are executed successfully, or none of them are executed.

Here is an example showing how to use Redis transactions for counter operations:

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.exceptions.JedisException;

public class CounterOptimizationExample {
    
    

    public static void main(String[] args) {
    
    
        Jedis jedis = new Jedis("localhost", 6379);

        String counterKey = "view_count";
        try {
    
    
            // 开始事务
            Transaction tx = jedis.multi();
            // 对计数器执行加1操作
            tx.incr(counterKey);
            // 执行事务
            tx.exec();
        } catch (JedisException e) {
    
    
            // 处理事务异常
            e.printStackTrace();
        } finally {
    
    
            jedis.close();
        }
    }
}

In the above code, we use the Jedis client library to MULTIopen a transaction through the command, and then execute the command in the transaction INCRto increase the value of the counter. Finally, EXECexecute the transaction using the command. If an error occurs during transaction execution, we can JedisExceptionhandle the exception by catching it.

(2) Use distributed locks

Another way to optimize counter performance is to use distributed locks. Distributed locks can ensure that only one thread can operate on the counter at the same time, avoiding concurrency conflicts. This mechanism ensures that updates to the counter are serialized, thus avoiding race conditions.

The following is an example of implementing distributed locks using the Redisson framework:

import org.redisson.Redisson;
import org.redisson.api.RLock;

public class CounterOptimizationWithLockExample {
    
    

    public static void main(String[] args) {
    
    
        Redisson redisson = Redisson.create();
        RLock lock = redisson.getLock("counter_lock");

        try {
    
    
            lock.lock(); // 获取锁
            // 执行计数器操作
        } finally {
    
    
            lock.unlock(); // 释放锁
            redisson.shutdown();
        }
    }
}

In the above code, we used the Redisson framework to create a distributed lock. Get the lock by calling lock.lock(), then perform the counter operation, and finally lock.unlock()release the lock. This ensures that only one thread can perform counter operations at a time.

2. How to optimize the performance of the leaderboard?

(1) Pagination query

In the leaderboard, there is usually a large amount of data, and if all the data is queried at once, performance may be affected. To solve this problem, paging queries can be used. Divide the leaderboard data into multiple pages, and query a small part of the data each time to reduce the burden on the database.

The following is an example of a paged query leaderboard:

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Tuple;
import java.util.Set;

public class LeaderboardPaginationExample {
    
    

    public static void main(String[] args) {
    
    
        Jedis jedis = new Jedis("localhost", 6379);

        String leaderboardKey = "leaderboard";
        int pageSize = 10; // 每页显示的数量
        int pageIndex = 1; // 页码

        // 获取指定页的排行榜数据
        Set<Tuple> leaderboardPage = jedis.zrevrangeWithScores(leaderboardKey, (pageIndex - 1) * pageSize, pageIndex * pageSize - 1);

        for (Tuple tuple : leaderboardPage) {
    
    
            String member = tuple.getElement();
            double score = tuple.getScore();
            System.out.println("Member: " + member + ", Score: " + score);
        }

        jedis.close();
    }
}

In the above code, we use zrevrangeWithScoresthe command to get the leaderboard data of the specified page. By calculating the start index and end index, we can realize the pagination query function.

(2) Use cache

In order to further improve the query performance of the leaderboard, the leaderboard data can be cached to reduce access to the database. For example, you can use Redis to cache the latest leaderboard data, and update the cache regularly to keep the data fresh.

Here is an example of caching leaderboard data:

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Tuple;
import java.util.Set;

public class LeaderboardCachingExample {
    
    

    public static void main(String[] args) {
    
    
        Jedis jedis = new Jedis("localhost", 6379);

        String leaderboardKey = "leaderboard";
        String cacheKey = "cached_leaderboard";
        int cacheExpiration = 300; // 缓存过期时间,单位:秒

        // 尝试从缓存中获取排行榜数据
        Set<Tuple> cachedLeaderboard = jedis.zrevrangeWithScores(cacheKey, 0, -1);

        if (cachedLeaderboard.isEmpty()) {
    
    
            // 如果缓存为空,从数据库获取数据并更新缓存
            Set<Tuple> leaderboardData = jedis.zrevrangeWithScores(leaderboardKey, 0, -1);
            jedis.zadd(cacheKey, leaderboardData);
            jedis.expire(cacheKey, cacheExpiration);
            cachedLeaderboard = leaderboardData;
        }

        for

 (Tuple tuple : cachedLeaderboard) {
    
    
            String member = tuple.getElement();
            double score = tuple.getScore();
            System.out.println("Member: " + member + ", Score: " + score);
        }

        jedis.close();
    }
}

In the above code, we first try to get the leaderboard data from the cache. If the cache is empty, we get the data from the database and store the data in the cache. Use expirethe command to set the expiration time of the cache to keep the data fresh.

5. Practical application cases

In this section, we will use two practical cases to show how to use the counter and leaderboard functions of Redis to build social media like system and game player leaderboard system. These cases will help you better understand how to apply the functions of Redis to practical scenarios.

1. Case study of social media like system

(1) Problem background

Suppose we want to build a social media platform where users can like articles, photos, etc. We want to be able to count the number of likes for each content and display the most popular content in real time.

(2) System Architecture

  • The number of likes for each content can be maintained using the counter function of Redis.
  • We can use an ordered set (Sorted Set) to maintain the ranking information of the content, and use the number of likes of the content as the score.

(3) Data model

  • Each piece of content has a unique identifier, such as an article ID or photo ID.
  • Use a counter to record the number of likes for each piece of content.
  • Use an ordered set to record the rank of the content, and the score associated with the content ID.

(4) Redis operation steps

  1. When the user likes, use the Redis INCRcommand to increase the number of likes for the corresponding content.
  2. Use ZADDthe command to add the content's ID and likes as a score to an ordered collection.

Java code example

import redis.clients.jedis.Jedis;

public class SocialMediaLikeSystem {
    
    

    private Jedis jedis;

    public SocialMediaLikeSystem() {
    
    
        jedis = new Jedis("localhost", 6379);
    }

    public void likeContent(String contentId) {
    
    
        // 增加点赞数
        jedis.incr("likes:" + contentId);

        // 更新排名信息
        jedis.zincrby("rankings", 1, contentId);
    }

    public long getLikes(String contentId) {
    
    
        return Long.parseLong(jedis.get("likes:" + contentId));
    }

    public void showRankings() {
    
    
        // 显示排名信息
        System.out.println("Top content rankings:");
        jedis.zrevrangeWithScores("rankings", 0, 4)
                .forEach(tuple -> System.out.println(tuple.getElement() + ": " + tuple.getScore()));
    }

    public static void main(String[] args) {
    
    
        SocialMediaLikeSystem system = new SocialMediaLikeSystem();
        system.likeContent("post123");
        system.likeContent("post456");
        system.likeContent("post123");

        System.out.println("Likes for post123: " + system.getLikes("post123"));
        System.out.println("Likes for post456: " + system.getLikes("post456"));

        system.showRankings();
    }
}

In the above code, we created a SocialMediaLikeSystemclass called to simulate the social media like system. We use the Jedis client library to connect to the Redis server, and implement the functions of likes, getting likes and displaying rankings. Every time a user likes, we use INCRa command to increment the number of likes and ZINCRBYa command to update the ranking information in the sorted set. By calling zrevrangeWithScoresthe command, we can get the content with the top number of likes.

2. Case study of player leaderboard

(1) Problem background

In a multiplayer online game, we want to be able to track and display player leaderboards in real time to encourage player participation and increase the competitiveness of the game.

(2) System Architecture

  • Each player's score can be maintained using Redis' counter function.
  • We can use an ordered set to maintain the player's ranking, with the player's score as the score.

(3) Data model

  • Each player has a unique ID.
  • Use a counter to keep track of each player's score.
  • Use an ordered set to record the player's rank, and the score associated with the player ID.

(4) Redis operation steps

  1. When the player completes the game, use Redis ZINCRBYcommands to increase the player's score.
  2. Use ZREVRANKthe command to get the player's rank.

(5) Java code example

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Tuple;

import java.util.Set;

public class GameLeaderboard {
    
    

    private Jedis jedis;

    public GameLeaderboard() {
    
    
        jedis = new Jedis("localhost", 6379);
    }

    public void updateScore(String playerId, double score) {
    
    
        jedis.zincrby("leaderboard", score, playerId);
    }

    public Long getPlayerRank(String playerId) {
    
    
        return jedis.zrevrank("leaderboard", playerId);
    }

    public Set<Tuple> getTopPlayers(int count) {
    
    
        return jedis.zrevrangeWithScores("leaderboard", 0, count - 1);
    }

    public static void main(String[] args) {
    
    
        GameLeaderboard leaderboard = new GameLeaderboard();
        leaderboard.updateScore("player123", 1500);
        leaderboard.updateScore("player456", 1800);
        leaderboard.updateScore("player789", 1600);

        Long rank = leaderboard.getPlayerRank("player456");
        System.out.println("Rank of player456: " + (rank != null ? rank + 1 : "Not ranked"));

        Set<Tuple> topPlayers = leaderboard.getTopPlayers(3);
        System.out.println("Top players:");
        topPlayers.forEach(tuple -> System.out.println(tuple.getElement() + ": " + tuple.getScore()));
    }
}

In the above code, we created a GameLeaderboardclass called class to simulate the game player leaderboard system. We also use the Jedis client library to connect to the Redis server, and implement the functions of updating player scores, getting player rankings, and getting the top players. A player's score can be updated using zincrbythe command, which zrevrankis used to

Get the player's ranking, note that the ranking starts counting from 0. By calling zrevrangeWithScoresthe command, we can get the top players and their scores.

6. Summary and Best Practices

In this blog post, we took an in-depth look at how to use Redis to build high-performance counters and leaderboards. Through actual cases and detailed Java code examples, we learned how to apply these functions in practical applications to improve system performance and user experience. Let's summarize the value of Redis in counter and leaderboard functions in this section, and provide some best practice guidelines.

1. The value of Redis in counters and leaderboards

By using Redis's counter and leaderboard functions, we can achieve the following values:

  • Real-time and high performance : Redis's in-memory storage and optimized data structures enable counter and leaderboard functions to be implemented with extremely high performance. This is very important for scenarios where data needs to be updated and queried in real time.

  • Increased user engagement : In applications such as social media and games, counter and leaderboard features can incentivize user engagement. By displaying the number of likes or leaderboards, users feel more interactive and competitive, which increases user engagement.

  • Data statistics and analysis : Through statistical counting and ranking data, we can obtain valuable data insights. These data can be used to analyze user behavior, optimize content recommendations, etc., thereby guiding business decisions.

2. Best Practice Guidelines

Here are some best practice guidelines for building counter and leaderboard functionality with Redis:

  • Appropriate data structure selection : Select an appropriate data structure according to actual needs. The counter can use a simple String type, and the leaderboard can use an ordered set (Sorted Set) to store data.

  • Guarantee data accuracy : In a high-concurrency environment, use Redis transactions, pipelines, and distributed locks to ensure the data accuracy of counters and leaderboards. Avoid race conditions caused by concurrent writes.

  • Periodic data cleaning : Regularly clean up counters and ranking data that are no longer needed to reduce data volume and improve query efficiency. Commands can be used ZREMRANGEBYRANKto remove outdated data from leaderboards.

  • Moderate cache : For leaderboard data, you can consider adding a moderate cache to improve query efficiency. But pay attention to balance cache updates and data consistency.

By following these best practices, you can better apply Redis's counter and leaderboard features, bringing better performance and user experience to your application.

Redis catches everything

If there is no Redis in 2023, it will be eliminated

Diagram Redis, talk about Redis persistence, RDB snapshot and AOF log

Redis single-threaded or multi-threaded? IO multiplexing principle

Why is the maximum number of slots in the Redis cluster 16384?

What exactly are Redis cache penetration, breakdown, and avalanche? 7 pictures tell you

Implementation of Redis distributed lock

Redis distributed cache, seckill

The principle and application scenarios of Redis Bloom filter to solve cache penetration

Guess you like

Origin blog.csdn.net/guorui_java/article/details/132529948