Redis Practical Combat | Use Redis’s Sorted Set to implement the ranking function and integrate with Spring Boot

A collection of columns that you can save for emergencies

Spring Cloud practical column: https://blog.csdn.net/superdangbo/category_9270827.html

Python practical column: https://blog.csdn.net/superdangbo/category_9271194.html

Logback detailed explanation column: https://blog.csdn.net/superdangbo/category_9271502.html

tensorflow column: https://blog.csdn.net/superdangbo/category_8691332.html

Redis column: https://blog.csdn.net/superdangbo/category_9950790.html

Spring Cloud actual combat:

Spring Cloud Practical Combat | Decrypting the underlying principles of Feign, including practical source code

Spring Cloud Practical Combat | Decrypting the underlying principles of load balancing Ribbon, including practical source code

1024 Programmers Day special article:

1024 Programmers Carnival Special | ELK + collaborative filtering algorithm builds a personalized recommendation engine to intelligently realize “thousands of people, thousands of faces”

1024 Programmer’s Day Special | Deciphering Spring Cloud Hystrix Meltdown to Improve System Availability and Fault Tolerance

1024 Programmer’s Day Special | ELK + user portraits build a personalized recommendation engine to intelligently realize “thousands of people, thousands of faces”

1024 Programmer’s Day Special | OKR VS KPI, who is more suitable?

1024 Programmers Day Special | Spring Boot Practical MongoDB Sharding or Replica Set Operation

Spring practical series of articles:

Spring Practical | Spring AOP Core Tips – Sunflower Collection

Spring Practice | The secret that Spring IOC cannot tell?

National Day and Mid-Autumn Festival special series of articles:

National Day and Mid-Autumn Festival Special (8) How to use JPA in Spring Boot projects

National Day and Mid-Autumn Festival Special (7) 20 Common Programming Interview Questions for Java Software Engineers

National Day and Mid-Autumn Festival Special (6) 30 Common Treasure Programming Interview Questions for College Students

National Day and Mid-Autumn Festival Special (5) How to performance tune MySQL? Next article

National Day and Mid-Autumn Festival Special (4) How to performance tune MySQL? Previous article

National Day and Mid-Autumn Festival Special (3) Use Generative Adversarial Network (GAN) to generate paintings with a festive atmosphere, implemented by the deep learning frameworks TensorFlow and Keras

National Day and Mid-Autumn Festival Special (2) Romantic Blessings Using Generative Adversarial Networks (GAN) to generate paintings with a festive atmosphere

National Day and Mid-Autumn Festival Special (1) Romantic Blessing Method Use Recurrent Neural Network (RNN) or Long Short-Term Memory Network (LSTM) to generate blessing poems

Redis’s Sorted Set is a data structure based on score sorting. It is very important in Redis and is often used to implement functions such as rankings and approximate counters.

Redis’s Sorted Set is implemented based on Skip List. The skip table is an efficient data structure. The average time complexity of its insertion, deletion and search operations is O(log n), which is much simpler than the implementation of a balanced tree (such as a red-black tree). The structure of a jump list is similar to a linked list. In addition to storing the element value, each node also contains an array of pointers that point to the next node of the corresponding level. This multi-level pointer design allows the jump list to quickly search across multiple nodes while ensuring the efficiency and simplicity of the jump list structure.
The underlying data structure of an ordered set consists of a hash and a skip list. In a hash, elements and their associated ratings (scores) are stored. Each element has a unique score that determines its position in the jump table. When it is necessary to operate on an ordered set, Redis first finds the elements and their scores through the hash table, and then performs the corresponding operations through the jump table.
The following are some core operations of Redis Sorted Set and their corresponding core code analysis:

  1. Add element (ZADD):
    The element addition operation in the ordered set is completed through the cooperation of hash table and skip table. First, Redis stores element values and ratings in hash tables. Then, find the corresponding position in the jump table based on the score, and insert the new element into that position.
  2. Get elements (ZRANGE, ZREVRANGE):
    The operation of obtaining elements in an ordered set mainly relies on jump lists. The ZRANGE operation starts from the head of the jump list and returns elements that meet the criteria according to the given score range. The ZREVRANGE operation starts from the end of the jump list and returns qualified elements according to the given score range.
  3. Remove elements (ZREM):
    The element deletion operation first finds the corresponding element through the hash table, and then deletes the element in the jump table. Redis only needs to delete the pointer pointing to the element in the hash table, and the element in the jump table will automatically move up.
  4. Update element score (ZINCRBY):
    The update element score operation only requires modifying the score of the corresponding element in the hash table and then recalculating the position of the element in the jump table.
  5. Get the length of an ordered set (ZCARD):
    The operation of the ordered set length directly queries the number of key-value pairs in the hash table.
  6. Get random elements (ZRANDMEMBER):
    The random element acquisition operation first randomly selects an element from the hash table, and then randomly selects an element in the jump list interval where the element is located.
    Through the above operations, Redis implements the data structure of an efficient ordered set (Sorted Set) and provides high-performance sorting and range search functions.

2. Actual combat

To implement leaderboard functionality using Spring Boot and Redis, you can follow these steps:

  1. Introduce dependencies
    In your Spring Boot project’s pom.xml file, add the following dependencies:
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
</dependencies>
  1. Configure Redis
    Configure Redis connection information in the application.properties or application.yml file:
# application.properties
spring.redis.host=localhost
spring.redis.port=6379
# application.yml
spring:
  redis:
    host: localhost
    port: 6379
  1. Create a Redis template
    Create a RedisTemplate Bean:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {<!-- -->
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {<!-- -->
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        return redisTemplate;
    }
}
  1. Create a ranking entity class
    Create a ranking entity class that contains user ID, scores and other information:
import java.io.Serializable;
public class RankingEntity implements Serializable {<!-- -->
    private String userId;
    private double score;
    //Constructor, getter and setter
}
  1. Implement Redis ranking operations
    Create a service class to implement ranking-related operations, such as adding scores, querying rankings, etc.:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class RankingService {<!-- -->
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    private static final String RANKING_KEY = "ranking_list";
    /**
     * Add score
     * @param userId user ID
     * @param score score
     */
    public void addScore(String userId, double score) {<!-- -->
        ValueOperations<String, RankingEntity> valueOperations = redisTemplate.opsForValue();
        valueOperations.set(RANKING_KEY + ":" + userId, score, 60, TimeUnit.SECONDS);
    }
    /**
     * Query ranking
     * @return ranking list
     */
    public List<Object> getRankingList() {<!-- -->
        List<Object> rankingList = redisTemplate.opsForList().range(RANKING_KEY, 0, -1);
        return rankingList;
    }
    /**
     * Query user ranking
     * @param userId user ID
     * @return user ranking
     */
    public Object getUserRanking(String userId) {<!-- -->
        return redisTemplate.opsForValue().get(RANKING_KEY + ":" + userId);
    }
     * @param userId user ID
     */
    public void deleteUserScore(String userId) {<!-- -->
        ValueOperations<String, RankingEntity> valueOperations = redisTemplate.opsForValue();
        valueOperations.delete(RANKING_KEY + ":" + userId);
    }
    /**
     * Update user score
     * @param userId user ID
     * @param score new score
     */
    public void updateUserScore(String userId, double score) {<!-- -->
        ValueOperations<String, RankingEntity> valueOperations = redisTemplate.opsForValue();
        valueOperations.set(RANKING_KEY + ":" + userId, score, 60, TimeUnit.SECONDS);
    }
    /**
     * Get the length of the user ranking list
     * @return the length of the user ranking list
     */
    public long getUserRankingListSize() {<!-- -->
        return redisTemplate.opsForList().size(RANKING_KEY);
    }
    /**
     * Insert user score in user ranking list
     * @param userId user ID
     * @param score score
     * @param index insertion position, 0 means inserting to the head of the list, negative number means inserting to the end of the list
     */
    public void insertUserScore(String userId, double score, long index) {<!-- -->
        List<Object> rankingList = redisTemplate.opsForList().range(RANKING_KEY, 0, -1);
        redisTemplate.opsForList().leftPush(RANKING_KEY, score, index);
    }
    /**
     * Remove user scores from user ranking list
     * @param userId user ID
     * @param index deletion position, 0 means deleting the first element, 1 means deleting the second element, and so on.
     */
    public void deleteUserScore(String userId, long index) {<!-- -->
        List<Object> rankingList = redisTemplate.opsForList().range(RANKING_KEY, 0, -1);
        redisTemplate.opsForList().rightPop(RANKING_KEY, index);
    }
    /**
     * Get the last element in the user's ranking list
     * @return the last element in the user's ranking list
     */
    public Object getLastUserScore() {<!-- -->
        return redisTemplate.opsForList().rightPop(RANKING_KEY);
    }
    /**
     * Get the first element in the user's ranking list
     * @return the first element in the user ranking list
     */
    public Object getFirstUserScore() {<!-- -->
        return redisTemplate.opsForList().leftPop(RANKING_KEY);
    }
    /**
     * Add element to user ranking list
     * @param score added score
     */
    public void addUserScore(double score) {<!-- -->
        redisTemplate.opsForList().rightPush(RANKING_KEY, score);
    }
    /**
     * Remove elements from user ranking list
     * @param index deletion position, 0 means deleting the first element, 1 means deleting the second element, and so on.
     */
    public void deleteUserScore(long index) {<!-- -->
        redisTemplate.opsForList().rightPop(RANKING_KEY, index);
    }
    /**
     * Get user ranking list
     * @return user ranking list
     */
    public List<Object> getUserRankingList() {<!-- -->
        return redisTemplate.opsForList().range(RANKING_KEY, 0, -1);
    }
    /**
     * Set the length of user ranking list
     * @param length The new length of the user ranking list
     */
    public void setUserRankingListLength(long length) {<!-- -->
        redisTemplate.opsForList().setSize(RANKING_KEY, length);
    }
    /**
     * Get user ranking
     *
     * @param userId user ID
     * @return user ranking, if the user does not exist, return -1
     */
    public int getUserRanking(String userId) {<!-- -->
        List<Object> rankingList = getRankingList();
        Object userScore = getUserRanking(userId);
        if (userScore == null) {<!-- -->
            return -1;
        }
                     int rank = 0;
        for (Object score : rankingList) {<!-- -->
            if (score.equals(userScore)) {<!-- -->
                break;
            }
            rank + + ;
        }
        return rank;
    }
    /**
     * Get the element at the specified position in the user ranking list
     *
     * @param index specifies the position, starting from 0
     * @return the element at the specified position in the user ranking list
     */
    public Object getUserRankingListElement(long index) {<!-- -->
        return redisTemplate.opsForList().range(RANKING_KEY, 0, -1).get(index);
    }
    /**
     * Get the user score in the user ranking list
     *
     * @param userId user ID
     * @return the user score in the user ranking list, if the user does not exist, return null
     */
    public Object getUserRanking(String userId) {<!-- -->
        ValueOperations<String, RankingEntity> valueOperations = redisTemplate.opsForValue();
        return valueOperations.get(RANKING_KEY + ":" + userId);
    }
    /**
     * Whether there is a user
     *
     * @param userId user ID
     * @return whether the user exists
     */
    public boolean existsUser(String userId) {<!-- -->
        ValueOperations<String, RankingEntity> valueOperations = redisTemplate.opsForValue();
        return valueOperations.hasKey(RANKING_KEY + ":" + userId);
    }
    /**
     * Clear all user ranking data
     */
    public void clearAllUserRankingData() {<!-- -->
        redisTemplate.delete(RANKING_KEY);
    }
}