Redis Actual-Data Structure Set Actual Filter User Registration Duplicate Submission Information

Overview: This series of blogs covers relevant content from a hands-on course recorded by debug: Getting Started with Cache Middleware Redis Technology and Application Scenario Actual Warfare (SpringBoot2.x +Redback System Design and Warfare) , interested little buddies can click to learn on their own (after all, it's faster to master technology in the form of video!)Columns for articles: Introduction and Practice of Cache Middleware Redis Technology

Summary: There is no doubt that Collection Set is also an important data structure in the cache middleware Redis. Its internal stored elements/members have the characteristics of "unique", "random", and so on. It also has a wide range of application scenarios in actual project development.In this paper, we will introduce and implement a more typical business scenario, Duplicate Submission, which is how to use the related features of Set Collection to implement "Filter duplicate submission messages at user registration"!

Content: In the previous articles, we introduced Redis's data structure~List, briefly introduced its basic features and its common and typical application scenarios in real projects!Beginning with this article, we will introduce and implement Redis's other data structure, Collection Set, which includes its basic features, command line lists in the Dos environment, and code battles for real-world scenarios in the Spring Boot 2.0 project.

Redis's data structure, set Set, is almost the same thing as the set set set in our math and JavaSE, all of which are "out of order", "unique", that is, the elements stored in the set are out of order and not duplicated!

In addition, its underlying design also has the "same work as the same work". That is, it is implemented by using hash tables, so its corresponding operations such as add, delete and find are all O(1).


1. DOS command line practice (based on redis-cli.exe tool)

Let's start with the command line under DOS to simply understand and practice the related commands of the Set, including its common operation commands and the operation commands of the Mathematical Level Set, as shown in the following figure:

(1) Common operation commands are "Add", "Query-Get List of Elements in a Collection", "Query-Get Number of Members in a Collection", "Query-Get List of Elements of Random Number in a Collection", "Query-Determine whether an element is a member of a Collection", "Delete-Remove Elements in a Collection", and so on.

Below, let's post some typical and common operation commands corresponding to the actual operation, in which the meaning of the corresponding commands can be viewed by your little buddies against the above picture!

127.0.0.1:6379> SADD classOneStudents jacky xiaoming debug michael white
(integer) 5
127.0.0.1:6379> SMEMBERS classOneStudents
1) "jacky"
2) "michael" 
3) "debug"
4) "xiaoming"
5) "white"
127.0.0.1:6379> SCARD classOneStudents
(integer) 5
127.0.0.1:6379> SADD classTwoStudents jacky xiaohong mary
(integer) 3
127.0.0.1:6379> SISMEMBER jacky classOneStudents
(integer) 0
127.0.0.1:6379> SISMEMBER classOneStudents jacky
(integer) 1
127.0.0.1:6379> SPOP classOneStudents
"white"
127.0.0.1:6379> SMEMBERS classOneStudents
1) "debug"
2) "jacky"
3) "xiaoming"
4) "michael"
127.0.0.1:6379> SRANDMEMBER classOneStudents 1
1) "jacky"
127.0.0.1:6379> SRANDMEMBER classOneStudents 3
1) "michael"
2) "xiaoming"
3) "debug"
127.0.0.1:6379> SRANDMEMBER classOneStudents 10
1) "jacky"
2) "michael"
3) "xiaoming"
4) "debug"

(2) The operation commands of the Mathematical Level set are interesting. Here we mainly introduce the three operation commands of intersection, difference and union, as shown in the following figure:

In the same way, we still paste out the DOS operations corresponding to these operation commands. The meaning of the corresponding commands can be viewed by your little buddies according to the picture above!

127.0.0.1:6379> SDIFF classOneStudents classTwoStudents
1) "white"
2) "xiaoming"
3) "debug"
4) "michael"
127.0.0.1:6379> SDIFF classTwoStudents classOneStudents
1) "xiaohong"
2) "mary"
127.0.0.1:6379> SINTER classOneStudents classTwoStudents
1) "jacky"
127.0.0.1:6379> SUNION classOneStudents classTwoStudents
1) "debug"
2) "jacky"
3) "xiaohong"
4) "xiaoming"
5) "michael"
6) "mary"

2. Code operation corresponding to Set command

Based on these operation commands, the following is a project built on Spring Boot 2.0 to start with a wave of "code battle" in the form of "Java Unit Test" and convert "command line operations under Dos" into actual code operations, as follows:

      @Test
    public void method3() {
        log.info("----Start Collection Set test");
        final String key1 = "SpringBootRedis:Set:10010";
        final String key2 = "SpringBootRedis:Set:10011";
        redisTemplate.delete(key1);
        redisTemplate.delete(key2);
        SetOperations<String, String> setOperations = redisTemplate.opsForSet();
        setOperations.add(key1, new String[]{"a", "b", "c"});
        setOperations.add(key2, new String[]{"b", "e", "f"});
        log.info("---aggregate key1 Elements of:{}", setOperations.members(key1));
        log.info("---aggregate key2 Elements of:{}", setOperations.members(key2));
        log.info("---aggregate key1 Randomly take one element:{}", setOperations.randomMember(key1));
        log.info("---aggregate key1 Random sampling n Elements:{}", setOperations.randomMembers(key1, 2L));
        log.info("---aggregate key1 Number of elements:{}", setOperations.size(key1));
        log.info("---aggregate key2 Number of elements:{}", setOperations.size(key2));
        log.info("---element a Is it a collection key1 Elements of:{}", setOperations.isMember(key1, "a"));
        log.info("---element f Is it a collection key1 Elements of:{}", setOperations.isMember(key1, "f"));
        log.info("---aggregate key1 And collections key2 The difference element of:{}", setOperations.difference(key1, key2));
        log.info("---aggregate key1 And collections key2 Intersection elements:{}", setOperations.intersect(key1, key2));
        log.info("---aggregate key1 And collections key2 The union elements:{}", setOperations.union(key1, key2));
        log.info("---From Collection key1 A random element pops up:{}", setOperations.pop(key1));
        log.info("---aggregate key1 Elements of:{}", setOperations.members(key1));
        log.info("---take c From Collection key1 Remove from the list of elements in:{}", setOperations.remove(key1, "c"));
    }

Click on the Run button icon on the left side of the unit test method to run the unit test method, and the results are as follows:

The corresponding api is not introduced one by one, its method name can be said to be "see the name". Everyone can also follow the example of "see the name". After practice, they will find that it is actually not so complex!


3. Typical application scenarios in practice~Filter duplicate submissions when users register

Let's take a typical application scenario in real-world project development as an example, and use real-world code practices to implement the various important features of Set, that is, uniqueness and disorder.

We start with "Uniqueness of Elements in Set Collection" and code war with "Filtering duplicate submissions at user registration".

To be honest, the business scenario of "duplicate submission" is not uncommon in actual project development. For example, users repeatedly click buttons when submitting information on the front end. If you do not take appropriate restrictions at this time, it is very likely that there will be multiple identical data entries in the database table!Let's take "Repeated submission of information at user registration" as a case for code practice.

(1) To do things properly, we must first take advantage of the tools. First, we establish a "user information table user" in the database, whose DDL is as follows:

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT 'Full name',
  `email` varchar(100) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT 'mailbox',  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_email` (`email`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='User table';

Then use mybatis's code generator or reverse engineering to generate Entity entity information, Mapper's list of operation interfaces, and Mapper's apper.xml for dynamic Sql operation. I won't post the corresponding source code here, and you can go to the address provided at the end of the article to download and view it!

(2) Next, we build a Controller and develop a request method to process the "registration information" submitted by front-end users with the following source code:

/**
 * Data type is Set - Data elements are not duplicated (filter out duplicate elements; determine if an element exists in a large set)
 * @Author:debug (SteadyJack) – wx:debug0868 
**/
@RestController
@RequestMapping("set")
public class SetController extends AbstractController {
    @Autowired
    private SetService setService;
    //TODO: Submit User Registration
    @RequestMapping(value = "put",method = RequestMethod.POST,consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public BaseResponse put(@RequestBody @Validated User user, BindingResult result){
        String checkRes=ValidatorUtil.checkResult(result);
        if (StrUtil.isNotBlank(checkRes)){
            return new BaseResponse(StatusCode.Fail.getCode(),checkRes);
        }
        BaseResponse response=new BaseResponse(StatusCode.Success);
        try {
            log.info("----User registration information:{}",user);
            response.setData(setService.registerUser(user));
        }catch (Exception e){
            response=new BaseResponse(StatusCode.Fail.getCode(),e.getMessage());
        }
        return response;
}
}

(3) The processing logic of its Service is as follows:

/**
 * Collection set service processing logic
 * @Author:debug (SteadyJack)
 * @Link: weixin-> debug0868 qq-> 1948831260
**/
@Service
public class SetService {
    private static final Logger log= LoggerFactory.getLogger(SetService.class);
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    //TODO: User Registration
    @Transactional(rollbackFor = Exception.class)
    public Integer registerUser(User user) throws Exception{
        if (this.exist(user.getEmail())){
            throw new RuntimeException(StatusCode.UserEmailHasExist.getMsg());
        }
        int res=userMapper.insertSelective(user);
        if (res>0){
            SetOperations<String,String> setOperations=redisTemplate.opsForSet();
            setOperations.add(Constant.RedisSetKey,user.getEmail());
        }
        return user.getId();
    }
    //TODO: Determine if the mailbox already exists in the cache
    private Boolean exist(final String email) throws Exception{
        //TODO: Writing Two
        SetOperations<String,String> setOperations=redisTemplate.opsForSet();
        Long size=setOperations.size(Constant.RedisSetKey);
        if (size>0 &&  setOperations.isMember(Constant.RedisSetKey,email)){
            return true;
        }else{
            User user=userMapper.selectByEmail(email);
            if (user!=null){
                setOperations.add(Constant.RedisSetKey,user.getEmail());
                return true;
            }else{
                return false;
            }
        }
    }

From this code, we can see that before inserting user information into the database, we need to determine if the user exists in the set of cache collections, and if so, tell the front end that the "user mailbox" already exists (here we think the user's mailbox is unique, of course, you can adjust it to "user name" only...)If the mailbox does not exist in the Cache Collection Set, insert it into the database, and after "Successful insertion of database tables", insert the user mailbox into the Cache Collection Set.

It is worth mentioning that in the logic of "determining if the mailbox already exists in the cache Set", we first determine if it exists in the cache. If it does not exist, for insurance purposes, we then go to the database to check if the mailbox really does not exist. If it does not exist, we add it "first" to the cache Set (so that the front end can be avoided to some extent from being duplicated)When the submit button is clicked repeatedly, instantaneous high concurrency occurs, which reduces the risk of concurrency security)!

Of course, there is still a problem with this writing: if you "drop the chain" when inserting a database, an exception occurs and you don't insert it, but at this time we "added the mailbox once in the cache to determine if it exists in the Set of cache collections", and the mailbox will never be registered (but in fact the mailbox will never be registered)Not really inserted into the database!)


(4) Now that there is a problem, we have to solve it first, as shown in the following code, the service logic registered for our transformed users:

     @Transactional(rollbackFor = Exception.class)
    public Integer registerUser(User user) throws Exception{
        if (this.exist(user.getEmail())){
            throw new RuntimeException(StatusCode.UserEmailHasExist.getMsg());
        }
        int res=0;
        try{
            res=userMapper.insertSelective(user);
            if (res>0){
                redisTemplate.opsForSet().add(Constant.RedisSetKey,user.getEmail());
            }
        }catch (Exception e){
            throw e;
        }finally {
            //TODO: If res is not greater than 0, an exception has occurred when inserting into the database.
            //TODO: Remove this mailbox from the cache Set at this time
            //TODO: Because if you join once and don't remove it, you will never be able to register the mailbox
            if (res<=0){
                redisTemplate.opsForSet().remove(Constant.RedisSetKey,user.getEmail());
            }
        }
        return user.getId();
    }

From the service processing logic, we can see that API methods that mainly use the Set of collections include: inserting, determining whether or not an element in the Set, the number of elements in the Set, removing specified elements in the Set, and so on.


Finally, we turn on Postman to test the interface. The final test results are shown in the following figures:

Okay, we've covered this article. I suggest you follow the example code provided in this article. Only after you have passed this article can you know how useful this is, otherwise you will become a "chatter"!Partners interested in Redis related technology stacks and practical application scenarios can learn from courses recorded by debug, 51cto College: Getting Started with Cache Middleware Redis Technology and Application Scenario Actual Warfare (SpringBoot2.x +Redback System Design and Warfare)

Supplement:

1. The related source code involved in this article can be check ed out at this address for viewing and learning: https://gitee.com/steadyjack/SpringBootRedis

2. At present debug has compiled and recorded a video tutorial about the content in this article, and interested little partners can go to watch and learn: https://edu.51cto.com/course/20384.html


Tags: Java Redis Database Spring

Posted on Sat, 08 Feb 2020 08:25:02 -0800 by qing