当前位置: 首页>编程语言>正文

springboot慢sql记录 springboot数据库缓存

Spring boot 缓存

1. spring cache

spring cache 是spring 3.1 引入的新技术,

核心思想:调用一个缓存方法时会把该方法参数和返回结果,作为一个键值存入缓存中,等到下次使用同样的参数调用该方法时,不在执行该方法,直接从缓存中获取结果进行返回,从而实现缓存功能。

Spring 中提供了3个注解来实现缓存。

  1. @Cacheable
  2. @CachePut
  3. @CacheEvict

下面通过一个实例,来了解spring cache.这里使用的spring-data-jpa 。也可以使用spring-mybatis stater。

springboot 2.2.6

mybatis 8.0.15

maven 3.6.1

Jdk 11

配置文件

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 这里使用的是MySql 8.0.15 数据库,需要加时区,保证自己的数据库db_jdbc 存在。
spring.datasource.url=jdbc:mysql://localhost:3306/db_jpa?\
  useSSL=true&useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=666666

#JPA 配置
# validate加载Hibernate 时验证创建的数据库表
# create 每次加载hibernate,重新创建数据库表结构,这就是导致数据库丢失的原因
# drop-create 加载hibernate 时创建,退出时删除
# update 加载hibernate 时自动更新数据库
# none 启动时不做任何操作
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect

# 控制台打印Sql
spring.jpa.show-sql=true
server.port=8080
  1. 引用依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

</dependency>

主类上配置@EnableCaching // 开启缓存

@SpringBootApplication
//开启缓存
@EnableCaching
public class Springboot01CacheApplication {

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

}

实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String username;
    private String password;
}

repository接口

@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
}

控制类UserController

@Controller
@ResponseBody
public class UserController {
    @Autowired
    private UserRepository userRepository;
    @GetMapping("findAll")

    public List<User> findAll(){
        List<User> userList = userRepository.findAll();
        return userList;
    }

    //http://localhost:8080/saveUser?id=1&username=deyou&password=1234
    //缓存测试CachePut
    @GetMapping("saveUser")
    //将返回值放入缓存
    @CachePut(value = "user",key = "#id")
    public User saveUser(Integer id, String username,String password){
        User user = new User(id, username, password);
        userRepository.save(user);
        System.out.println("测试Save"+user);
        return user;

    }
    //http://localhost:8080/queryUser?id=1
    //查询用户
    @GetMapping("/queryUser")
    //使用这个注解,方法执行会先在缓存里找,如果有直接返回,若果没有返回值放入缓存,再返回。
    @Cacheable(value = "user",key = "#id")
    public Optional<User> queryUser(Integer id){
        Optional<User> user = userRepository.findById(id);
        return user;
    }

    //
    @GetMapping("/deleteUserById")
    //删除对应Id 的缓存,同时也把数据删除了。
    @CacheEvict(value = "user" ,key ="#id")
    public String deleteUserById(Integer id){
        userRepository.deleteById(id);
        return "delete success";
    }
    //http://localhost:8080/deleteCacheId?id=1
    @GetMapping("/deleteCacheId")
    //删除对应Id 的缓存,同时也把数据删除了。
    @CacheEvict(value = "user" ,key ="#id")
    public String deleteCacheId(Integer id){

        return "delete cacheId " + id ;
    }

    @GetMapping("deleteCache")
    //删除所有缓存
    @CacheEvict(value = "user",allEntries = true)
    public String deleteCache(){
        return "clean all cache";
    }
}

启动项目: 在浏览器上输出对应方法上的链接,观察缓存效果。

2. 使用 redis

使用redis 做缓存数据库。默认读者已经完成redis 的安装。

新建一个springboot 项目。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>io.projectreactor</groupId>
    <artifactId>reactor-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

application.properties 默认配置如下,如果不同自行更改

# 默认
spring.redis.host=localhost
# 默认
spring.redis.port=6379

选用User实体类和上面一样。这里User类实现了序列化接口。方便redis 对象实例化

@Data
@AllArgsConstructor
@NoArgsConstructor
@EntityScan
public class User implements Serializable {
    private int id;
    private String username;
    private String password;
}

新建一个RedisService.java

@Service
public class RedisService {
    @Resource
    private RedisTemplate<String,Object> redisTemplate;

    public void set(String key, Object value){
        //将key值序列化
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        //将值(Object)序列化
        redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Object.class));
        ValueOperations<String,Object> vo=redisTemplate.opsForValue();
        vo.set(key, value);

    }

    public Object get(String key){

        ValueOperations<String,Object> vo=redisTemplate.opsForValue();
        return vo.get(key);
    }
}

控制类UserController

@RestController
public class UserController {
    @Autowired
    private RedisService redisService;
    //http://localhost:8080/saveUser?id=1&username=deyou&password=1234
    @GetMapping("saveUser")
    public String saveUser(Integer id,String username,String password){
        User user = new User(id,username,password);
        redisService.set(id.toString(),user);
        return "success";
    }
    //http://localhost:8080/getUserById?id=1
    @GetMapping("getUserById")
    public Object getUserById(Integer id){
        //id.toString 转为字符串
        Object o = redisService.get(id.toString());
        return o;
    }
}

测试:必须在启动redis 服务器的情况下启动项目。

执行在浏览器中http://localhost:8080/saveUser?id=1&username=deyou&password=1234,将一个实例化对象存入Redis 数据库。

执行:http://localhost:8080/getUserById?id=1 将在浏览器中获得存入redis 中的对应的实例化对象数据。

同样开启redis 服务器可以进行如下测试

127.0.0.1:6379> keys *

  1. “1”
    127.0.0.1:6379> get 1
    “{“id”:1,“username”:“deyou”,“password”:“1234”}”
    127.0.0.1:6379>

下面开始使用redis 做缓存数据库。

新建一个UserRepository 接口。

public interface UserRepository extends JpaRepository<User,Integer> {
}

RedisService 增加如下方法

//增加一个set方法
//设置key value 过期时间和过期单位,过期后还要从Mysql数据库中查询
public void set(String key, Object value, Long time, TimeUnit t ){
    redisTemplate.setKeySerializer(new StringRedisSerializer());
    redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));
    ValueOperations<String, Object> ov = redisTemplate.opsForValue();
    ov.set(key,value,time,t);

}

UserContoller 增加如下代码

@Autowired
    private UserRepository userRepository;
	//对mysql 数据库插入一条数据
    //http://localhost:8080/saveuser2?id=1&username=deyou&password=1234
    @GetMapping("/saveuser2")
    public User saveuser2(Integer id,String username,String password){
        User user = new User(id,username,password);

        User save = userRepository.save(user);
        System.out.println(save);
        return user;
    }
    //查询数据,本方法完成了redis 缓存功能
    //http://localhost:8080/getUser?id=1
    @GetMapping("getUser")
    public Object getUser(Integer id){
        Object o = redisService.get(id.toString());
        //测试代码
        System.out.println("redis中存在=========="+o);
        if(o == null){
            o = userRepository.findById(id).get();
            if(o != null){
                //可以自行修改时间测试,避免等待时间过长
                redisService.set(id.toString(),o,100L, TimeUnit.SECONDS);
                System.out.println("redis 中存入对象json串=========="+o);
            }

        }
        return o;
     }

启动redis,启动项目进行测试。

http://localhost:8080/saveuser2?id=1&username=deyou&password=1234 存储数据。

http://localhost:8080/getUser?id=1 查询数据,注意观察控制台查询语句。自行测试。

3. 使用 memcached

由于之前没接触过过memcached 数据库。

这里简单说一下安装过程,我的电脑系统是window10 ,直接去菜鸟教程找到了最新版本的压缩包

下载到电脑之后解压。

我们使用管理员身份执行以下命令将 memcached 添加来任务计划表中:

schtasks /create /sc onstart /tn memcached /tr "'c:\memcached\memcached.exe' -m 512"

**注意:**你需要使用真实的路径替代 c:\memcached\memcached.exe。

然后后双击memcached.exe 就开启服务了。

然后开始学习springboot memcache 缓存,过程和redis 差不多。这里需要写一个使用一个依赖

<dependency>
    <groupId>net.spy</groupId>
    <artifactId>spymemcached</artifactId>
    <version>2.12.3</version>
</dependency>

其他依赖是

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
    </exclusions>
</dependency>

新建一个MemcacheConfig 类来对数据库建立连接。(这个类十分重要。)

package com.zdy.springboot.config;

import net.spy.memcached.MemcachedClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.net.InetSocketAddress;


@Component
public class MemcachedConfig implements CommandLineRunner {
    private Logger logger =  LoggerFactory.getLogger(this.getClass());

    @Value("${memcache.ip}")
    private String memcacheIp;

    @Value("${memcache.port}")
    private Integer memcachePort;

    private MemcachedClient client = null;

    @Override
    public void run(String... args) throws Exception {
        try {
            client = new MemcachedClient(new InetSocketAddress(memcacheIp,memcachePort));
        } catch (IOException e) {
            logger.error("Connection to server failed",e);
        }
        logger.info("Connection to server Memcached success");
    }

    public MemcachedClient getClient() {
        return client;
    }


    public Boolean set(String key,int time,String value) {
        Boolean b = false;
        try{//time 单位s
            b=(this.getClient().set(key, time, value)).get();
        }catch (Exception e){
            logger.error(e.getMessage());
        }
        return b;
    }

    public Boolean add(String key,int time,String value){
        Boolean b = false;
        try{
            b=(this.getClient().add(key, time, value)).get();
        }catch (Exception e){
            logger.error(e.getMessage());
        }
        return b;
    }

    public Object replace(String key,int time,String value){
        Boolean b = false;
        try{
            b=(this.getClient().replace(key, time, value)).get();
        }catch (Exception e){
            logger.error(e.getMessage());
        }
        return b;
    }

    public Object append(String key,int time,String value){
        Boolean b = false;
        try{
            b=(this.getClient().append(key, value)).get();
        }catch (Exception e){
            logger.error(e.getMessage());
        }
        return b;
    }

    public Object prepend(String key,int time,String value){
        Boolean b = false;
        try{
            b=(this.getClient().prepend(key, value)).get();
        }catch (Exception e){
            logger.error(e.getMessage());
        }
        return b;
    }

    public Object cas(String key,int time,String value){
        return this.getClient().cas(key, time, value);
    }

    public Object get(String key){
        return this.getClient().get(key);
    }

    public Boolean delete(String key){
        Boolean b = false;
        try{
            b=(this.getClient().delete(key)).get();
        }catch (Exception e){
            logger.error(e.getMessage());
        }
        return b;
    }

    public long incr(String key,Integer value){
        return this.getClient().incr(key,value);
    }

    public long decr(String key,Integer value){
        return this.getClient().decr(key,value);
    }

}

application.properties配置ip 地址和端口

# memcached 缓存机制配置
memcache.ip=localhost
memcache.port=11211
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 这里使用的是MySql 8.0.15 数据库,需要加时区,保证自己的数据库db_jdbc 存在。
spring.datasource.url=jdbc:mysql://localhost:3306/db_jpa?\
  useSSL=true&useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=666666

#JPA 配置
# validate加载Hibernate 时验证创建的数据库表
# create 每次加载hibernate,重新创建数据库表结构,这就是导致数据库丢失的原因
# drop-create 加载hibernate 时创建,退出时删除
# update 加载hibernate 时自动更新数据库
# none 启动时不做任何操作
spring.jpa.hibernate.ddl-auto=create
spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect

# 控制台打印Sql
spring.jpa.show-sql=true

新建一个User 类,和上面一样,这里就不贴代码了

新建控制类。实现对象实例往Memcached 里的保存,查询,删除。

UserController.java

@RestController
public class UserController {
    //memcached
    @Resource
    private MemcachedConfig memcachedConfig;
    //mysq
    @Autowired
    private UserRepository userRepository;

    //向memcached 里添加数据
    //http://localhost:8080/saveUser?id=1&userName=deyou&userPassword=1234
    @GetMapping(value = "saveUser")
    public Boolean saveUser(Long id, String userName, String userPassword){
        User user = new User(id, userName, userPassword);
        return memcachedConfig.set(id.toString(), 1000,user.toString());
    }
    //向memcached 里查询数据
    //http://localhost:8080/getUserById?id=1
    @GetMapping(value = "getUserById")
    public Object getUserById(Long id) {
        return memcachedConfig.get(id.toString());
    }


    //添加删除数据
    //http://localhost:8080/deleteCacheById?id=1
    @GetMapping(value = "deleteCacheById")
    public Boolean deleteCacheById(Long id) {
        return memcachedConfig.delete(id.toString());
    }
}

启动 Memcached 服务,启动项目,浏览器中输出对象方法上的地址,观察效果。

使用Memcahced 做缓存

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}

在UserController 中插入如下代码

//mysq
@Autowired
private UserRepository userRepository;
    //开始向mysql 数据库中添加数据
    //http://localhost:8080/saveUser2?id=1&userName=deyou&userPassword=12345
    @GetMapping("/saveUser2")
    public User saveUser2(Long id, String userName, String userPassword) {
        User user = new User(id, userName, userPassword);
        System.out.println(user);
        userRepository.save(user);
        return user;
    }
    //查询数据,注意观察控制台信息
    //http://localhost:8080/getUserById2?id=1
    @GetMapping(value = "getUserById2")
    public Object getUserById2(Long id) {
        Object object = memcachedConfig.get(id.toString());
        if (object == null) {
            object = (userRepository.findById(id)).get();
            if (object != null) {
                //time 单位为s ,超过指定时间,memcached 数据丢失。
                memcachedConfig.set(id.toString(), 10, object.toString());
            }
        }
        return object;
    }

启动服务器。重启项目。先执行http://localhost:8080/saveUser2?id=1&userName=deyou&userPassword=12345向数据库中插入一条数据,再访问http://localhost:8080/getUserById2?id=1第一个在控制台打出了SQL.因为没有缓存数据。第二次没有打印SQL,因为这次查询的是Memcached.

总结:这里简要总结一下redis 和Memcached 的区别

  1. 数据类型不同
  2. 数据一致性
  3. value 值得大小,redis 1GB ,Memcached 1M
  4. 存储方式,redis 支持数据持久化。Memcached 数据全部存在 内存中。
  5. 网络模型:Redis 是单线程IO复用模型,Memcached 是多线程IO 复用模型。
  6. 应用场景:Memcached 多用于缓存数据集,临时数据、session。而Redis 除了可以用缓存数据库外,还可以用作消息队列,数据堆栈。


https://www.xamrdz.com/lan/5a91932628.html

相关文章: