springboot缓存篇之mybatis一级缓存和二级缓存

前言

相信很多人都用过mybatis,这篇文章主要是介绍mybatis的缓存,了解一下mybatis缓存是如何实现,以及它在实际中的应用

一级缓存

什么是mybatis一级缓存?我们先看一个例子:

java 复制代码
@GetMapping("/list")
public Result<Void> listGoods(){
    GoodsExample goodsExample = new GoodsExample();
    goodsMapper.selectByExample(goodsExample);

    goodsMapper.selectByExample(goodsExample);
    return Result.okWithNullData();
}

上面这个例子有两个相同的查询方法,我们看看日志

我们看到即使两个查询方法一样,它也查询了两次数据库。mybatis为了优化这种情况,既然两次查询语句一样,那么就将第一次结果缓存起来,那么第二次查询就不用再查数据库。

其实要实现这个功能也是挺简单的,你只需在方法上加上@Transactional即可

java 复制代码
@Transactional
public Result<Void> listGoods(){
    // 代码省略
}

再看看运行日志

我们可以看到只有一次查询,正常情况下每发起一次查询就会创建一个SqlSession,查询结束了,SqlSession就会被销毁。如果开启了事务,那么就可以为多个查询创建一个共同的SqlSession,那么在同一个事务中,如果说存在相同的查询,那么后面的查询都会直接拿第一次查询的结果。

这么看来,mybatis的一级缓存还不错,但现实告诉你如果你的项目比较大,比较复杂,比如分布式,如果使用mybatis的一级缓存,很容易就踩坑了,因此一些大公司都会要求禁用它,而使用redis

mybatis是默认开启一级缓存的,如果要关闭,可以在配置文件这样配置:

properties 复制代码
# 默认值是session,如果要关闭,设置成statement即可
mybatis.configuration.local-cache-scope=statement

为什么说很容易踩坑呢?

其实简单来说就是第一次缓存的结果,被另外的线程更新了,那么如果后面再拿到的数据就是脏数据。

总之禁用它,使用redis代替它准没错

二级缓存

假如说现在有100个请求在同一时间请求列表数据,就上面那段代码而言,正常情况下要查询200次数据库

java 复制代码
@GetMapping("/list")
public Result<Void> listGoods(){
    GoodsExample goodsExample = new GoodsExample();
    // 第一次查询
    goodsMapper.selectByExample(goodsExample);
	
    // 第二次查询
    goodsMapper.selectByExample(goodsExample);
    return Result.okWithNullData();
}

如果开启了一级缓存,那么也要查询100次,因此mybatis就提供了二级缓存

二级缓存的设置也很简单,只需在Mapper.xml文件中加上cache标签即可

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.dao.mapper.GoodsMapper">
  <!--缓存标签-->
  <cache></cache>
  <resultMap id="BaseResultMap" type="com.example.demo.domain.entity.Goods">
    <id column="id" jdbcType="BIGINT" property="id" />
    <result column="goods_name" jdbcType="VARCHAR" property="goodsName" />
    <result column="number" jdbcType="INTEGER" property="number" />
    <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
    <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
  </resultMap>
    <!--省略代码-->
    

当我们再次请求list接口时,看看控制台的日志

我们可以看到除了第一次查询要查询数据库外,后面的几次查询列表都是直接从缓存中拿数据

mybtais二级缓存看上去好像挺好用的,但现实当中还是给你来个暴击,最好不要用。

我们要搞清楚它的作用范围,它时针对命名空间的,就比如说上面提到的mapper.xml,如果说你部署在一个节点,那么就只有一个mapper.xml,缓存的结果直接从这个空间下获取即可,但很多时候随着业务的增大可能要部署多个节点,那每个节点都有自己的mapper.xml,而每个节点更新缓存只会更新自己节点的mapper.xml,因此可能会出现下面的问题

客户端有个请求过来,从A节点获取列表数据的,如果有缓存查缓存,没有就查数据库,比如说meta 60产品数据,它的价格时6000元

客户端再发个请求过来,从B节点获取列表数据的,如果有缓存查缓存,没有就查数据库,比如说meta 60产品数据,它的价格时6000元

这是没有问题的,但现在管理人员调整了产品价格,从6000改成5000,他的这次更新请求刚好请求了B节点(客户端请求节点是随机的),此时B节点缓存刷新了

那现在是不是会出现这样的情况,有些用户看到的商品价格是6000(刚好访问了A节点,直接读取缓存),有些用户看到的价格却是5000(请求了B节点,B节点会重新查库)

所以mybtais二级缓存慎用!

相关推荐
光依旧6 小时前
MCP实战手记系列(十):MCP Server高可用实战
spring boot·高可用·nginx负载均衡·redis哨兵·mcp·微服务容错·resilience4j熔断
志尊宝8 小时前
Vue3 零基础每日笔记(073):keep-alive 页面缓存实战——列表页返回不丢状态
vue.js·笔记·缓存·#vue #前端 #前端开发·#javascript·#vue.js
禾小西9 小时前
Redis:从两大维度和三大主线建立知识体系
数据库·redis·缓存
省钱兄--zs10 小时前
西安24小时自助健身房解决方案实战:系统开发与部署全流程指南
java·spring boot·系统架构·intellij-idea·需求分析
ly768912 小时前
Spring Boot 集成 Elasticsearch 的生产实践:客户端选型、索引生命周期与批量写入容错
spring boot·elasticsearch·索引生命周期·java api client·批量写入
程序猿_极客12 小时前
【免费】分享一套优质的基于SpringBoot的服装商城管理系统的设计与实现(带可视化图表、协同过滤功能),源码+文档+视频详解(讲解)
java·spring boot·后端·服装商城管理系统
bug菌13 小时前
🤔同事突然问我:Spring的注解 @Component 和 @Service 有何不同?
java·spring boot·后端
Lonely丶墨轩13 小时前
用 Vue + Spring Boot + Python 做一个 AI 双人海龟汤游戏:从出题、联机到自动审稿等核心技术设计与实现
spring boot·后端·游戏
她的男孩15 小时前
打印模板草稿能保存,一点发布就报主从关系:我们把校验拆成了两档
java·spring boot·后端
禾小西15 小时前
Redis AOF 日志:宕机了,如何避免数据丢失?
java·redis·mybatis