美文网首页javaMybatis
Mybatis缓存机制之一级缓存

Mybatis缓存机制之一级缓存

作者: 山巅自相见 | 来源:发表于2022-03-01 21:33 被阅读0次

首先要知道:sqlsession(接口)的底层是HashMap储存,线程不安全,sqlsessionTemplate是其实现类,线程是安全的

一级缓存介绍

在程序运行过程中,可能会在与数据库的会话中,多次查询一条相同的sql,Mybatis提供了一级缓存的方案优化这部分场景,如果是相同的sql会优先命中一级缓存,避免对数据库的直接查询,提高了性能。具体的执行过程图如下:



每个SqlSession中都持有Executor,每个Executor中都有Local Cache。当程序执行查询的时候,Mybatis根据当前执行的语句生成'MappedStatement',在Local Cache中进行查询,如果缓存命中直接将结果返回给用户,反之会先去数据库查询,结果写入Local Cache中,最后将结果返回给用户。

一级缓存实验

接下来通过实验来了解Mybatis的一级缓存的效果。
首先是创建实例表customer

create table customer
(
  cid int auto_increment comment '用户id',
  username varchar(4) null comment '用户名称',
  age int null comment '年龄',
  constraint customer_pk
    primary key (cid)
)
comment '用户表';

并添加一条数据

实验1

开启一级缓存,范围为会话级别,调用三次queryCustomerByCid,代码示例如下:

import com.zhao.mybatiscache.mapper.L1CacheMapper;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(value = "L1CacheController")
public class L1CacheController {
  @Autowired
  private SqlSessionFactory sqlSessionFactory;

  @PostMapping(value = "queryCustomerByCid")
  public void queryCustomerByCid() {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    L1CacheMapper mapper = sqlSession.getMapper(L1CacheMapper.class);
    System.out.println(mapper.queryCustomerByCid(1));
    System.out.println(mapper.queryCustomerByCid(1));
    System.out.println(mapper.queryCustomerByCid(1));
  }
}

执行结果:



可以看到,在执行相同的查询三次,只有第一次真正的查询了数据库,后续的查询使用了一级缓存,从缓存中获取数据

实验2

增加了对数据库的修改操作,验证再一次会话中,对数据库进行一次修改,一级缓存是否失效,代码示例如下:

@PostMapping(value = "addCustomer")
public void addCustomer() {
  SqlSession sqlSession = sqlSessionFactory.openSession();
  L1CacheMapper mapper = sqlSession.getMapper(L1CacheMapper.class);
  System.out.println(mapper.queryCustomerByCid(1));
  System.out.println("增加了" + mapper.addCustomer(new Customer().setUsername("张三").setAge(18)) + "个用户");
  System.out.println(mapper.queryCustomerByCid(1));
}

执行结果:


可以看见,程序第一次执行了查询操作后,添加了一条数据,再次执行查询操作,是从数据库中查询了数据,没有命中缓存,一级缓存失效

实验3

开启两个SqlSession,在SqlSession1中查询数据,使一级缓存生效,再在SqlSession2中更新数据库,验证一级缓存只在数据库回话内部共享,代码示例如下:

@PostMapping(value = "localCacheScope")
public void localCacheScope() {
  SqlSession sqlSession1 = sqlSessionFactory.openSession();
  L1CacheMapper mapper1 = sqlSession1.getMapper(L1CacheMapper.class);
  System.out.println("sqlSession1读取数据" + mapper1.queryCustomerByCid(1));
  System.out.println("sqlSession1读取数据" + mapper1.queryCustomerByCid(1));
  SqlSession sqlSession2 = sqlSessionFactory.openSession();
  L1CacheMapper mapper2 = sqlSession2.getMapper(L1CacheMapper.class);
  System.out.println("sqlSession2更新了" + mapper2.updateCustomer(new Customer().setUsername("张三").setCid(1)) + "个用户");
  System.out.println("sqlSession1读取数据" + mapper1.queryCustomerByCid(1));
  System.out.println("sqlSession1读取数据" + mapper1.queryCustomerByCid(1));
}

可以看见,SqlSession2更新了id为1的用户,将“赵毅梵”更新成了“张三”,但是SqlSession1之后的查询还是命中了一级缓存,缓存中出现了脏数据,也证明了以前的猜想,一级缓存只会在会话内部共享。

一级缓存工作流程&源码分析

工作流程

源码解析

这个先欠着

总结

  1. Mybatis一级缓存的生命周期和SqlSession一致。
  2. Mybatis一级缓存最大范围是SqlSession内部,有多个SqlSession或者分布式的情况下,数据库写操作会引起脏数据,建议设定缓存为Statement

相关文章

网友评论

    本文标题:Mybatis缓存机制之一级缓存

    本文链接:https://www.haomeiwen.com/subject/ayplrrtx.html