Java simulates and solves cache penetration

Posted by ditusade on Thu, 08 Aug 2019 09:39:27 +0200

Java simulates and solves cache penetration
What is Cache Penetration
Cache penetration only occurs in high concurrency, that is, when there are 10,000 concurrent query data, we usually go to redis to query data first, but if there is no such data in redis, then a large part of the 10,000 concurrent data will go to mysql at once. Queried in the database.

Resolving Cache Penetration
Let me first simulate cache penetration
For example, the following code
Insert a picture description here
Pom.xml code

<?xml version="1.0" encoding="UTF-8"?>

     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.example</groupId>
<artifactId>springboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>springboot</name>
<description>Demo project for Spring Boot</description>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.1.RELEASE</version>
    <relativePath></relativePath> <!-- lookup parent from repository -->
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
</properties>

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

    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>1.1.1</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>


Application.properties

server.port=8081

DB Configuration:

spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://47.91.248.236:3306/hello?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root

spring Integrated Mybatis Environment

pojo alias scanning package

mybatis.type-aliases-package=com.itheima.domain

Load Mybatis mapping file

mybatis.mapper-locations=classpath:mapper/*Mapper.xml
MyController code, the blue code below is to mimic 10,000 concurrent threads

/**

  • sinture.com Inc.
  • Copyright (c) 2016-2018 All Rights Reserved.
    */

package com.itheima.controller;

import com.itheima.mapper.UserMapper;
import com.itheima.domain.User;
import com.itheima.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**

  • @author xinzhu
  • @ version Id: MyController. java, V 0.1 December 05, 2018, 6:29 p.m. Xinzhu Exp$
    */

@RestController
public class MyController {

@Autowired
private UserService userService;

@RequestMapping("/hello/{id}")
@ResponseBody
public User queryUser(@PathVariable Integer id){
   // Blue Code Comment Begins
   new Thread(){
        @Override
        public void run() {
            for (int x = 0; x < 10000; x++) {
                userService.queryUser(id);
            }
        }

    }.start();
    // End of Blue Code Annotation

    return userService.queryUser(id);
}

}
User class

/**

  • sinture.com Inc.
  • Copyright (c) 2016-2018 All Rights Reserved.
    */

package com.itheima.domain;

/**

  • @author xinzhu
  • @ version Id: User. java, V 0.1 December 06, 2018, 1:40 p.m. Xinzhu Exp$
    */

public class User {

// Primary key
private Integer id;
// User name
private String username;
// Password
private String password;
// Full name
private String name;

public void setId(Integer id) {
    this.id = id;
}

@Override
public String toString() {
    return "User{" +
            "id=" + id +
            ", username='" + username + '\'' +
            ", password='" + password + '\'' +
            ", name='" + name + '\'' +
            '}';
}

public Integer getId() {
    return id;
}
public String getUsername() {
    return username;
}
public void setUsername(String username) {
    this.username = username;
}

public String getPassword() {
    return password;
}
public void setPassword(String password) {
    this.password = password;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

}
UserService

package com.itheima.service;

import com.itheima.domain.User;

public interface UserService {

public User queryUser(Integer id);

}
UserService Impl, the blue code below is to imitate redis. At this point, note that the map set of the following simulated redis must be placed outside the queryUser below. That is to say, the user Map variable below must be a member variable. Otherwise, because redis are shared by multiple threads, if you put it in the queryUser() below. In the method, there are multiple threads with multiple userMap collections. The following code is to use redis if the data is queried, and database if the query is not available.

package com.itheima.service;

import com.itheima.domain.User;
import com.itheima.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class UserServiceImpl implements UserService {

@Autowired
private UserMapper userMapper;
// Blue Code Comment Begins
static Map<Integer,User> userMap=new HashMap();
static {
    User huancun_user =new User();
    huancun_user.setId(1);
    huancun_user.setName("zhangsan");
    huancun_user.setPassword("123");
    huancun_user.setName("Zhang San");
    userMap.put(1,huancun_user);
}
// End of Blue Code Annotation
public User queryUser(Integer id){
    User user=userMap.get(id);
    if(user==null){
        user= userMapper.queryUser(id);
        System.out.println("query data base");
        userMap.put(user.getId(),user);
    }else{
        System.out.println("Query Cache");
    }
    return user;
};

}
Springboot Application Code

package com.itheima;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringbootApplication {

public static void main(String[] args) {
    SpringApplication.run(SpringbootApplication.class, args);
}

}
The data in the database is as follows

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS user;
CREATE TABLE user (
id int(11) NOT NULL AUTO_INCREMENT,
username varchar(50) DEFAULT NULL,
password varchar(50) DEFAULT NULL,
name varchar(50) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO user VALUES ('1','zhangsan','123','Zhangsan');
INSERT INTO user VALUES ('2','lisi','123','Lisi');
Then we query the following links, because there is no data with id value of 2 in the map set of the above simulated redis, so we all query the database at this time. If you want to send 10000 at once, the database will be under great pressure.

Insert a picture description here

Then the printing result is as follows: print a lot of query databases and query caches, which means that there are many queries in 10,000 concurrent databases. This is to avoid. As for why there is query caching printing, because we put the query data into the simulated redis, so we just started. Most of the threads start by querying the database, and the rest are querying the data in the simulated redis cache.

query data base
query data base
query data base
query data base
query data base
query data base
query data base
query data base
query data base
query data base
query data base
query data base
query data base
Query Cache
Query Cache
Query Cache
Query Cache
Query Cache
Query Cache
Query Cache
Query Cache
Query Cache
Query Cache
Then we use double detection locks to solve the above cache penetration
How can we solve the problem of cache penetration? Even if 10,000 concurrent threads come in, then 10,000 concurrent data are not available in redis, so we should first query the data in redis, then put this data in redis, and then the remaining 9,999 threads are queried in redis. Cache penetration is resolved, so we can turn the above code into the following

For example, the following code
Insert a picture description here

Pom.xml code

<?xml version="1.0" encoding="UTF-8"?>

     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.example</groupId>
<artifactId>springboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>springboot</name>
<description>Demo project for Spring Boot</description>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.1.RELEASE</version>
    <relativePath></relativePath> <!-- lookup parent from repository -->
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
</properties>

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

    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>1.1.1</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>


Application.properties

server.port=8081

DB Configuration:

spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://47.91.248.236:3306/hello?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root

spring Integrated Mybatis Environment

pojo alias scanning package

mybatis.type-aliases-package=com.itheima.domain

Load Mybatis mapping file

mybatis.mapper-locations=classpath:mapper/*Mapper.xml
MyController code, the blue code below is to mimic 10,000 concurrent threads

/**

  • sinture.com Inc.
  • Copyright (c) 2016-2018 All Rights Reserved.
    */

package com.itheima.controller;

import com.itheima.mapper.UserMapper;
import com.itheima.domain.User;
import com.itheima.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**

  • @author xinzhu
  • @ version Id: MyController. java, V 0.1 December 05, 2018, 6:29 p.m. Xinzhu Exp$
    */

@RestController
public class MyController {

@Autowired
private UserService userService;

@RequestMapping("/hello/{id}")
@ResponseBody
public User queryUser(@PathVariable Integer id){
   // Blue Code Comment Begins
   new Thread(){
        @Override
        public void run() {
            for (int x = 0; x < 10000; x++) {
                userService.queryUser(id);
            }
        }

    }.start();
    // End of Blue Code Annotation

    return userService.queryUser(id);
}

}
User class

/**

  • sinture.com Inc.
  • Copyright (c) 2016-2018 All Rights Reserved.
    */

package com.itheima.domain;

/**

  • @author xinzhu
  • @ version Id: User. java, V 0.1 December 06, 2018, 1:40 p.m. Xinzhu Exp$
    */

public class User {

// Primary key
private Integer id;
// User name
private String username;
// Password
private String password;
// Full name
private String name;

public void setId(Integer id) {
    this.id = id;
}

@Override
public String toString() {
    return "User{" +
            "id=" + id +
            ", username='" + username + '\'' +
            ", password='" + password + '\'' +
            ", name='" + name + '\'' +
            '}';
}

public Integer getId() {
    return id;
}
public String getUsername() {
    return username;
}
public void setUsername(String username) {
    this.username = username;
}

public String getPassword() {
    return password;
}
public void setPassword(String password) {
    this.password = password;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

}
UserService

package com.itheima.service;

import com.itheima.domain.User;

public interface UserService {

public User queryUser(Integer id);

}
UserService Impl, the blue code below is to imitate redis. At this point, note that the map set of the following simulated redis must be placed outside the queryUser below. That is to say, the user Map variable below must be a member variable. Otherwise, because redis are shared by multiple threads, if you put it in the queryUser() below. In the method, there are multiple threads with multiple userMap collections. The following code is to use redis if the data is queried, and database if the query is not available.

Then the following red code is to solve the above cache penetration problem, using locks to solve the cache penetration problem, and called double detection locks, why is it called double detection locks, because there are two if statements, the first if statement is to reduce the synchronization code block in the red code, because if changed into inside If you have the data you want, then you don't need to go to the synchronization block in the red code below, so there are two if statements. As for why you need the following user= userMap.get(id);, it's because after the first thread query puts the data in the simulated redis cache, the rest of the threads should go to the same as below. When you step through the code block, you need to query the data in the cache to find the data just put in redis by the first thread, so there will be user= userMap.get(id) in the red code below.

package com.itheima.service;

import com.itheima.domain.User;
import com.itheima.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class UserServiceImpl implements UserService {

@Autowired
private UserMapper userMapper;
// Blue Code Comment Begins
static Map<Integer,User> userMap=new HashMap();
static {
    User huancun_user =new User();
    huancun_user.setId(1);
    huancun_user.setName("zhangsan");
    huancun_user.setPassword("123");
    huancun_user.setName("Zhang San");
    userMap.put(1,huancun_user);
}
// End of Blue Code Annotation
public User queryUser(Integer id){
    User user=userMap.get(id);
    // Red Code Comment Begins
    if(user==null){
        synchronized (this) {
            user= userMap.get(id);
            if (null == user) {
                user= userMapper.queryUser(id);
                System.out.println("query data base");
                userMap.put(user.getId(),user);
            }else{
                System.out.println("Query Cache");
            }
        }
    }else{
        System.out.println("Query Cache");
    }
    //End of Red Code Annotation
    return user;
};

}
The data in the database is as follows

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS user;
CREATE TABLE user (
id int(11) NOT NULL AUTO_INCREMENT,
username varchar(50) DEFAULT NULL,
password varchar(50) DEFAULT NULL,
name varchar(50) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO user VALUES ('1','zhangsan','123','Zhangsan');
INSERT INTO user VALUES ('2','lisi','123','Lisi');
Then we query the following links, because there is no data with id value of 2 in the map set of the above simulated redis, so we all query the database at this time. If you want to send 10000 at once, the database will be under great pressure.
Insert a picture description here
Then the print result is as follows, that is, only the first print query database, and then the rest are query caches, which is to solve cache penetration.

query data base
Query Cache
Query Cache
Query Cache
Query Cache
Query Cache
Query Cache
Query Cache
Query Cache
Query Cache
Query Cache
Query Cache
Query Cache
Query Cache
Query Cache
Query Cache
Query Cache
Query Cache
Query Cache
Query Cache
Query Cache
Query Cache
If you can see the students here, please give me a recommendation, Thanks.
Original address https://www.cnblogs.com/mqk100/p/11319758.html

Topics: Database Java Redis Spring Mybatis