Based on redis to achieve the World Cup ranking function project combat

  • 2020-06-12 10:57:21
  • OfStack

[

An aside:

This site first to recommend a good WeChat public number:

Interested friends can pay attention to this site WeChat public number [code farmers that thing], more web production effects source code and learn dry goods oh!!

]

demand

Some time ago, I did a World Cup score ranking. Guess the tie in 64 matches of the World Cup. Guess correctly +1 point, wrong +0 point. One person can only guess once a game.

1. Show the top 100 list.

2. Display individual rankings (e.g., Sheet 3, your current rank is 106579).

Analysis of the

At the beginning, I planned to use mysql database to do it directly. When I met one problem, everyone's score would change. How could I obtain the ranking of individuals? The database can do row_num sorting by score, but this method requires a full table scan and the query becomes very slow when the number of participants reaches 10,000.

The Ranking feature of redis perfectly matches this requirement. Let's see how I did it.

implementation

1. Introduction to redis sorts sets

The Sorted Sets data type is like a mix of set and hash. Like sets1, Sorted Sets is a unique, non-repeating string. It can be said that Sorted Sets is also a kind of Sets.

Sorted Sets is implemented through the dual-port data structure of Skip List(skip table) and hash Table(hash table), so Redis performs O(log(N)) every time an element is added. So when we asked for sorting, Redis didn't need to do any work at all, it was already sorted. The score of the element can be updated at any time.

2. RedisTemplate is used in springboot

This paper mainly USES redisTemplate to operate redis, of course, you can also use ES66en-ES67en, depending on personal preference.

I have a single point of redis on the machine and the configuration file is as follows


server:
 port: 9001
spring:
 redis:
 database: 0
 url: redis://user:123@127.0.0.1:6379
 host: 127.0.0.1
 password: 123
 port: 6379
 ssl: false
 timeout: 5000

The Maven dependency is introduced as follows


<parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>2.0.4.RELEASE</version>
</parent>

<dependencies>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-test</artifactId>
 </dependency>
</dependencies>

3. Code implementation

1. Inject redis and declare key as constant SCORE_RANK


 @Autowired
 private StringRedisTemplate redisTemplate;
 public static final String SCORE_RANK = "score_rank";

2. Add default ranking data

Here, for loops to create a collection and then adds 100,000 pieces of data in bulk


 /**
 *  A batch of new 
 */
 @Test
 public void batchAdd() {
 Set<ZSetOperations.TypedTuple<String>> tuples = new HashSet<>();
 long start = System.currentTimeMillis();
 for (int i = 0; i < 100000; i++) {
 DefaultTypedTuple<String> tuple = new DefaultTypedTuple<>(" zhang 3" + i, 1D + i);
 tuples.add(tuple);
 }
 System.out.println(" The cycle time :" +( System.currentTimeMillis() - start));
 Long num = redisTemplate.opsForZSet().add(SCORE_RANK, tuples);
 System.out.println(" Batch added time :" +(System.currentTimeMillis() - start));
 System.out.println(" Number of rows affected: " + num);
 }
[

/ / output
Cycle time :56
Batch added time :1015
Affected rows: 100,000

]

3. Get the top 10 (in reverse order by score)

Two fetching methods are provided, one with score and one without


 /**
 *  Get ranking list 
 */
 @Test
 public void list() {

 Set<String> range = redisTemplate.opsForZSet().reverseRange(SCORE_RANK, 0, 10);
 System.out.println(" Gets the ranking list :" + JSON.toJSONString(range));
 Set<ZSetOperations.TypedTuple<String>> rangeWithScores = redisTemplate.opsForZSet().reverseRangeWithScores(SCORE_RANK, 0, 10);
 System.out.println(" Gets a list of rankings and scores :" + JSON.toJSONString(rangeWithScores));
 }

// The output 
 Gets the ranking list :[" zhang 399999"," zhang 399998"," zhang 399997"," zhang 399996"," zhang 399995"," zhang 399994"," zhang 399993"," zhang 399992"," zhang 399991"," zhang 399990"," zhang 399989"]
 Gets a list of rankings and scores :[{"score":100000.0,"value":" zhang 399999"},{"score":99999.0,"value":" zhang 399998"},{"score":99998.0,"value":" zhang 399997"},{"score":99997.0,"value":" zhang 399996"},{"score":99996.0,"value":" zhang 399995"},{"score":99995.0,"value":" zhang 399994"},{"score":99994.0,"value":" zhang 399993"},{"score":99993.0,"value":" zhang 399992"},{"score":99992.0,"value":" zhang 399991"},{"score":99991.0,"value":" zhang 399990"},{"score":99990.0,"value":" zhang 399989"}]

4. Increase the score of Li 4

To add "Lee 4" to the leaderboard, redis will be inserted at the time of insertion and removed at the time of extraction without any sorting


 /**
 *  A single new 
 */
 @Test
 public void add() {
 redisTemplate.opsForZSet().add(SCORE_RANK, " li 4", 8899);
 }

5. Get the Rank of Li 4


 /**
 *  Obtain a single ranking 
 */
 @Test
 public void find(){
 Long rankNum = redisTemplate.opsForZSet().reverseRank(SCORE_RANK, " li 4");
 System.out.println(" li 4 Personal ranking: " + rankNum);

 Double score = redisTemplate.opsForZSet().score(SCORE_RANK, " li 4");
 System.out.println(" li 4 The scores of :" + score);
 }
[

/ / output
Li 4's personal rank: 91101
Li 4's score :8899.0

]

6. Count the number of people in the score range

redis also provides a method for statistical score intervals, as follows


 /**
 *  Count the number of people between the two scores 
 */
 @Test
 public void count(){
 Long count = redisTemplate.opsForZSet().count(SCORE_RANK, 8001, 9000);
 System.out.println(" statistical 8001-9000 Number of people between :" + count);
 }
[

/ / output
Statistics between 8001-9000 :1001

]

7. Get the cardinality of the collection (quantity size)


 /**
 *  Gets the cardinality of the entire collection ( The number of size )
 */
 @Test
 public void zCard(){
 Long aLong = redisTemplate.opsForZSet().zCard(SCORE_RANK);
 System.out.println(" The cardinality of the set is: " + aLong);
 }

[

/ / output
The cardinality of the collection is: 100001

]

8. Use add scores

This method USES addition directly on the original score; Without this element, it is created, and score starts at 0. Add again


<parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>2.0.4.RELEASE</version>
</parent>

<dependencies>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-test</artifactId>
 </dependency>
</dependencies>
0 [

/ / output
Li 4 score +1000 post: 9899.0

]

4. The inductive

What features of redis did we use in the above test class? In the above example, we used single additions, batch additions, getting the top 10, and getting the single ranking, but redisTemplate provides more methods.

New or update

There are three ways, one single, one batch, to add scores (starting at 0 if none exist).


<parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>2.0.4.RELEASE</version>
</parent>

<dependencies>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-test</artifactId>
 </dependency>
</dependencies>
1

delete

Deletion offers three ways: via key/values deletion, via ranking range deletion, and via score range deletion.


<parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>2.0.4.RELEASE</version>
</parent>

<dependencies>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-test</artifactId>
 </dependency>
</dependencies>
2

check

1. List query:

There are two categories, positive and negative. The list below is only in positive order, and in reverse order you simply add reverse to the method


// Gets a collection of list values from a ranking interval 
Set<V> range(K key, long start, long end);
// Get the list value and score set through the ranking interval 
Set<TypedTuple<V>> rangeWithScores(K key, long start, long end);
// Gets a collection of list values from a fraction interval 
Set<V> rangeByScore(K key, double min, double max);
// Get the list value and the score set through the score interval 
Set<TypedTuple<V>> rangeByScoreWithScores(K key, double min, double max);
// through Range Object deletions are then retrieved for set ranking 
Set<V> rangeByLex(K key, Range range);
// through Range Object deletions and retrieval limit Set ranking of quantities 
Set<V> rangeByLex(K key, Range range, Limit limit);

2. Single query

Obtain single ranking and score via key/value. The following lists only in positive order, and in reverse order simply add reverse to the method


<parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>2.0.4.RELEASE</version>
</parent>

<dependencies>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-test</artifactId>
 </dependency>
</dependencies>
4

statistical

The number of people in the statistical score interval, the statistical set base.


<parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>2.0.4.RELEASE</version>
</parent>

<dependencies>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-test</artifactId>
 </dependency>
</dependencies>
5

conclusion

These are some examples of using the leaderboard feature in redis and how to do it with redis. redis isn't just a cache, it's a database, and it provides a lot of functionality that we can all take advantage of.

Here I use redis to display the ranking of World Cup points. No matter it is convenient in batch updating or obtaining personal ranking, it has a high efficiency and also reduces the pressure on database operation, achieving a good effect.


Related articles: