Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Mybatis源码解析(十):一级缓存和二级缓存

Mybatis源码解析(十):一级缓存和二级缓存

作者头像
冬天vs不冷
发布于 2025-01-21 00:32:33
发布于 2025-01-21 00:32:33
76704
代码可运行
举报
文章被收录于专栏:springbootspringboot
运行总次数:4
代码可运行

前言

一、缓存策略

  • 一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的
  • 二级缓存是mapper级别的缓存。多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的

二、一级缓存演示

  • 一级缓存默认开启

演示一

  • 同一个sqlSession的相同查询,sql日志打印一次而且两次对象相等,第二次查询结果是第一次查询的缓存结果

演示二

  • 同一个sqlSession的两次相同查询,中间添加一个更新操作,每次查询都要打印日志而且结果不相等,第一次查询结果缓存失效了
  • 更新操作,即使没有commit,结果不变,那么失效原因就在update里面

演示三

  • 不同的sqlSession的相同查询,两次sql日志打印而且对象不相等,第二次查询没有拿到第一次查询结果的缓存

三、一级缓存源码

1、作用范围

  • 查询数据库方法,在此之前已经判断一级缓存中不存在查询数据
  • key(Cache对象)的组成部分:statementId、分页参数、带?的sql、参数值、环境id
  • 一级缓存就是PerpetualCache localCache
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  List<E> list;
  // 1. 首先向本地缓存中存入一个ExecutionPlaceholder的枚举类占位value
  localCache.putObject(key, EXECUTION_PLACEHOLDER);
  try {
    // 2. 执行doQuery方法
    list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
  } finally {
    // 3. 执行完成移除这个key
    localCache.removeObject(key);
  }
  // 4. 查询结果存入缓存中
  localCache.putObject(key, list);
  // 5. 如果MappedStatement的类型为CALLABLE,则向localOutputParameterCache缓存中存入value为parameter的缓存
  if (ms.getStatementType() == StatementType.CALLABLE) {
    localOutputParameterCache.putObject(key, parameter);
  }
  return list;
}

查看一级缓存对象PerpetualCache localCache

  • 说到底一级缓存就是cache这个HashMap对象
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class PerpetualCache implements Cache {

  private final String id;

  private final Map<Object, Object> cache = new HashMap<>();

  @Override
  public void putObject(Object key, Object value) {
    cache.put(key, value);
  }

  @Override
  public Object getObject(Object key) {
    return cache.get(key);
  }
  ...
}

查看一级缓存对象PerpetualCache的位置

  • 一级缓存对象在执行器基础抽象类下
  • Mybatis源码解析(五):SqlSession会话的创建-此篇讲了SqlSessioin的创建包含了不同执行器的创建,而BaseExecutor又是不同执行器的基类,必定需要创建
  • 所以,一级缓存是跟随着SqlSession的
  • 一级缓存演示一:相同的sqlSession就可以从localCache中获取以前存的value
  • 一级缓存演示三:不相同的sqlSession,sqlSession1的缓存中有值,但sqlSession2需要到它的缓存中获取,所以拿不到

2、缓存失效

  • 根据一级缓存的演示二可知,相同的sqlSession的update方法会导致缓存失效

进入udpate源码

  • 这里有个清除缓存方法,将一级缓存localCache的Map对象clear
  • 一级缓存的演示二:缓存map已被清空,所以缓存失效
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public int update(MappedStatement ms, Object parameter) throws SQLException {
  ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
  if (closed) {
    throw new ExecutorException("Executor was closed.");
  }
  clearLocalCache();
  return doUpdate(ms, parameter);
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public void clearLocalCache() {
  if (!closed) {
    localCache.clear();
    localOutputParameterCache.clear();
  }
}

四、二级缓存演示

  • 二级缓存不是默认开启
  • Mapper配置文件中配置标签
  • 实体类要实现Serializable接口,因为二级缓存会将对象写进硬盘,就必须序列化,以及兼容对象在网络中的传输

演示一

  • 不同的sqlSession相同的查询,sql日志只有一个,说明二级缓存生效了
  • 对象不相等。内容一样,地址不一样,后面源码说
  • 两次查询,只有调用commit或者close方法,二级缓存才会生效
  • 记录了命中率,第一次缓存中没有,命中率0.0,第二次查询缓存中有,查询两次,缓存命中率0.5

演示二

  • 两次查询之间添加更新操作,依然缓存命中0.5
  • 二级缓存,只有更新操作commit以后才会缓存失效
  • 一级缓存,只需要更新操作,不需要commit就会缓存失效

五、二级缓存源码

1、<cache>标签的解析

捋一下xml与对象之间的关系

  • 全局配置文件会被解析成Configuration对象
  • 映射配置文件会被解析成多个MappedStatement对象,每个<select><insert><update><delete>标签会被解析成一个MappedStatement对象
  • MappedStatement对象集合挂在Configuration对象下面
  • <cache>标签在映射配置文件的增删改查标签外,那它应该单独挂在Configuration对象下

<cache>标签解析内容

  • 解析<cache>标签内的属性,通过这些属性创建Cache对象
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private void cacheElement(XNode context) {
  if (context != null) {
    // 解析<cache>标签type属性的值,在这可以自定义type的值,比如redisCache,如果没有指定默认就是PERPETUAL
    String type = context.getStringAttribute("type", "PERPETUAL");
    Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
    // 获取负责过期的eviction对象,默认策略为LRU
    String eviction = context.getStringAttribute("eviction", "LRU");
    Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
    // 清空缓存的频率 0代表不清空
    Long flushInterval = context.getLongAttribute("flushInterval");
    // 缓存容器的大小
    Integer size = context.getIntAttribute("size");
    // 是否只读
    boolean readWrite = !context.getBooleanAttribute("readOnly", false);
    // 是否阻塞
    boolean blocking = context.getBooleanAttribute("blocking", false);
    // 获得Properties属性
    Properties props = context.getChildrenAsProperties();
    builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
  }
}

Cache对象创建方法

  • 默认情况创建的对象和一级缓存对象一样,都是PerpetualCache
  • Cache对象赋值MappedStatement对象构建器的currentCache属性,构建MappedStatement对象使用
  • addCache方法:将Cache对象添加到Configuration对象
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public Cache useNewCache(Class<? extends Cache> typeClass,
    Class<? extends Cache> evictionClass,
    Long flushInterval,
    Integer size,
    boolean readWrite,
    boolean blocking,
    Properties props) {
  // 1.生成cache对象
  Cache cache = new CacheBuilder(currentNamespace)
      .implementation(valueOrDefault(typeClass, PerpetualCache.class))
      .addDecorator(valueOrDefault(evictionClass, LruCache.class))
      .clearInterval(flushInterval)
      .size(size)
      .readWrite(readWrite)
      .blocking(blocking)
      .properties(props)
      .build();
  // 2.添加到configuration中
  configuration.addCache(cache);
  // 3.并赋值给MapperBuilderAssistant中的currentCache属性
  currentCache = cache;
  return cache;
}
  • 装饰者模式,包了两层,最里面的引用还是PrerpetualCache

2、Cache对象存在位置

MappedStatement对象下

  • 创建Cache对象时候,赋值了变量currentCache,此时创建MappedStatement使用
  • 后面几乎大部分使用缓存都是从MappedStatement ms对象中获取Cache对象
  • MappedStatement对象是解析映射配置文件生成的,不论创建多少SqlSession,都用的同一个MappedStatement对象,这就解释了二级缓存是跨sqlSession的
  • 二级缓存map对象里的value这里还是空,后面的操作会填充

Configuration对象下

  • 创建Cache对象后,调用了一个addCache方法
  • 二级缓存以Map集合形式存在全局配置文件Configuration
  • key:<mapper namespace=“com.xc.mapper.UserMapper”>标签的namespace值
  • 这个位置的Cache几乎没啥用,知道Configuration对象下有它即可
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Configuration {
  protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
  ...
  public void addCache(Cache cache) {
    caches.put(cache.getId(), cache);
  }
  ...
}

3、二级缓存的存和取

  • ms.getCache():从MappedStatemet对象中获取SynchronizedCache包装对象,内部引用是PerpetualCache
  • tcm.getObject:从二级缓存中取值
  • delegate.query:查询一级缓存,没有再查数据库,上面一级缓存中有讲
  • tcm.putObject:二级缓存中存值 (注意:此处只是存到map集合中,没有真正存到二级缓存中)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
    throws SQLException {
  // 获取二级缓存
  Cache cache = ms.getCache();
  if (cache != null) {
    // 刷新二级缓存 (存在缓存且flushCache为true时)
    flushCacheIfRequired(ms);
    if (ms.isUseCache() && resultHandler == null) {
      ensureNoOutParams(ms, boundSql);
      @SuppressWarnings("unchecked")
      // 从二级缓存中查询数据
      List<E> list = (List<E>) tcm.getObject(cache, key);
      // 如果二级缓存中没有查询到数据,则查询一级缓存及数据库
      if (list == null) {
        // 委托给BaseExecutor执行
        list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        // 将查询结果 要存到二级缓存中(注意:此处只是存到map集合中,没有真正存到二级缓存中)
        tcm.putObject(cache, key, list); // issue #578 and #116
      }
      return list;
    }
  }
  // 委托给BaseExecutor执行
  return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
  • tcm里面层层包裹,但都是同一个二级缓存对象SynchronizedCache@2695

tcm对象

  • tcm即TransactionalCacheManager:事务缓存管理器
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class CachingExecutor implements Executor {

  private final Executor delegate;
  private final TransactionalCacheManager tcm = new TransactionalCacheManager();
  ...
}
  • TransactionalCacheManager里主要维护着一个transactionalCaches的Map集合
    • Cache:SynchronizedCache类,内部引用delegate还是PerpetualCache
    • TransactionalCache:事务缓存类,内部引用是SynchronizedCache
  • tcm的存值和取值就是对应的putObject和getObject方法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class TransactionalCacheManager {

  // Cache 与 TransactionalCache 的映射关系表
  private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap<>();

  public void clear(Cache cache) {
    // 获取 TransactionalCache 对象,并调用该对象的 clear 方法,下同
    getTransactionalCache(cache).clear();
  }

  public Object getObject(Cache cache, CacheKey key) {
    // 直接从TransactionalCache中获取缓存
    return getTransactionalCache(cache).getObject(key);
  }

  public void putObject(Cache cache, CacheKey key, Object value) {
    // 直接存入TransactionalCache的缓存中
    getTransactionalCache(cache).putObject(key, value);
  }
...
}
1)tcm.putObject:二级缓存中存值
  • 如果上面所说二级缓存存值和取值没问题,那么二级缓存的演示一,不需要commit就能实现二级缓存
  • 原因这里的二级缓存中存值只是存到map集合,没有存到二级缓存
  • 猜想那就是commit操作把这个map集合数据转移到二级缓存

进入tcm.putObject方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void putObject(Cache cache, CacheKey key, Object value) {
  // 直接存入TransactionalCache的缓存中
  getTransactionalCache(cache).putObject(key, value);
}

进入getTransactionalCache方法

  • 从transactionalCaches这个Map集合的找有没有key=Cache对象
    • 有,则将transactionalCaches集合中的事务缓存返回
    • 没有,则根据cache(SynchronizedCache)创建事务缓存返回且添加transactionalCaches集合中
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private TransactionalCache getTransactionalCache(Cache cache) {
  // 从映射表中获取 TransactionalCache
  return MapUtil.computeIfAbsent(transactionalCaches, cache, TransactionalCache::new);
}

进入putObject方法

  • 将key=CacheKey,value=查询结果放入entriesToAddOnCommit集合
  • 这里不是二级缓存位置,只是临时位置,commit以后才会将此处值转移到二级缓存位置
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class TransactionalCache implements Cache {
  ...
  /**
   *  // 在事务被提交前,所有从数据库中查询的结果将缓存在此集合中
   */
  private final Map<Object, Object> entriesToAddOnCommit;

  @Override
  public void putObject(Object key, Object object) {
    // 将键值对存入到 entriesToAddOnCommit 这个Map中中,而非真实的缓存对象 delegate 中
    entriesToAddOnCommit.put(key, object);
  }
  ...
}
2) tcm.getObject:二级缓存中取值
  • 与存值一样,先通过getTransactionalCache方法,获取事务缓存
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public Object getObject(Cache cache, CacheKey key) {
  // 直接从TransactionalCache中获取缓存
  return getTransactionalCache(cache).getObject(key);
}

进入getObject方法

  • delegate:SynchronizedCache对象,内部实际引用是PerpetualCache,二级缓存
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class TransactionalCache implements Cache {
  /**
   * 委托的 Cache 对象。
   *
   * 实际上,就是二级缓存 Cache 对象。
   */
  private final Cache delegate;
  /**
   * 提交时,清空 {@link #delegate}
   *
   * 初始时,该值为 false
   * 清理后{@link #clear()} 时,该值为 true ,表示持续处于清空状态
   */
  private boolean clearOnCommit;
  /**
   *   在事务被提交前,当缓存未命中时,CacheKey 将会被存储在此集合中
   */
  private final Set<Object> entriesMissedInCache;

  @Override
  public Object getObject(Object key) {
    // issue #116
    // 查询的时候是直接从delegate中去查询的,也就是从真正的缓存对象中查询
    Object object = delegate.getObject(key);
    // 如果不存在,则添加到 entriesMissedInCache 中
    if (object == null) {
      // 缓存未命中,则将 key 存入到 entriesMissedInCache 中
      entriesMissedInCache.add(key);
    }
    // issue #146
    // 如果 clearOnCommit 为 true ,表示处于持续清空状态,则返回 null
    if (clearOnCommit) {
      return null;
    } else {
      // 返回 value
      return object;
    }
  }
  ...
}
3)commit操作
  • 查看tcm即TransactionalCacheManager的提交
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public void commit(boolean required) throws SQLException {
  delegate.commit(required);
  tcm.commit();
}
  • 遍历事务缓存集合
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void commit() {
  for (TransactionalCache txCache : transactionalCaches.values()) {
    txCache.commit();
  }
}
  • flushPendingEntries方法:将entriesToAddOnCommit转移到二级缓存
  • reset重置方法 将entriesToAddOnCommit集合清空
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void commit() {
  // 如果 clearOnCommit 为 true ,则清空 delegate 缓存
  if (clearOnCommit) {
    delegate.clear();
  }
  // 将 entriesToAddOnCommit、entriesMissedInCache 刷入 delegate(cache) 中
  flushPendingEntries();
  // 重置
  reset();
}

flushPendingEntries真正存入二级缓存方法

  • 这里的putObject是将对象序列化成二进制对象,获取时候反序列化,所以第一次的查询结果与第二次获取的缓存结果不相等,因为他两不是同一个对象,只是内容相同
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private void flushPendingEntries() {
  // 将 entriesToAddOnCommit 中的内容转存到 delegate 中
  for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
    // 在这里真正的将entriesToAddOnCommit的对象逐个添加到delegate中,只有这时,二级缓存才真正的生效
    delegate.putObject(entry.getKey(), entry.getValue());
  }
}

二级缓存存取总结:

  • 二级缓存存值,放到了事务缓存TransactionalCache对象的entriesToAddOnCommit的map集合中
  • 二级缓存取值,从事务缓存TransactionalCache对象的delegate(内部引用PerpetualCache)中获取
  • 存和取不在一个地方,commit操作会将entriesToAddOnCommit集合数据序列化二进制数据转移到delegate
  • 这样也就能解释二级缓存的演示一了

4、缓存失效

  • 查看更新方法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public int update(MappedStatement ms, Object parameterObject) throws SQLException {
  flushCacheIfRequired(ms);
  return delegate.update(ms, parameterObject);
}
  • 查询方法flushCacheRequired属性为false,更新方法为true
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private void flushCacheIfRequired(MappedStatement ms) {
  Cache cache = ms.getCache();
  if (cache != null && ms.isFlushCacheRequired()) {
    tcm.clear(cache);
  }
}
  • 通过Cache对象获取事务缓存对象,再调用clear方法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void clear(Cache cache) {
  // 获取 TransactionalCache 对象,并调用该对象的 clear 方法,下同
  getTransactionalCache(cache).clear();
}

进入clear方法

  • clearOnCommit:清理标志设置为true
  • entriesToAddOnCommit:临时存放二级缓存的集合清空,真正的二级缓存没有清空
  • 所以,更新方法没有失效,接下来要看commit
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public void clear() {
  // 标记 clearOnCommit 为 true
  clearOnCommit = true;
  // 清空 entriesToAddOnCommit
  entriesToAddOnCommit.clear();
}

进入commit方法

  • 此时clearOnCommit标志已经被设置为true,delegate则是真正的二级缓存,被清除了
  • 这样,二级缓存的演示二也就能解释清楚了
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void commit() {
  // 如果 clearOnCommit 为 true ,则清空 delegate 缓存
  if (clearOnCommit) {
    delegate.clear();
  }
  // 将 entriesToAddOnCommit、entriesMissedInCache 刷入 delegate(cache) 中
  flushPendingEntries();
  // 重置
  reset();
}

总结

  • 作用范围
    • 一级缓存放在执行器里,每次创建sqlSession都会创建执行器,所以一级缓存的作用范围在sqlSession里
    • 二级缓存放在MappedStatement里,解析映射配置文件创建而成,不论创建多少sqlSession都只用这一个MappedStatemt对象,所以二级缓存的作用范围在多个sqlSession(也就是namespace)
  • 一级缓存
    • 默认开启,就是简单得存取值
    • update操作无commit会使缓存失效
  • 二级缓存
    • 手动开启,缓存结果通过序列化传递,所以地址不同,内容相同
    • 查询流程缓存是数据只是放入临时map集合,commit以后才将数据转移到二级缓存,故查询操作后commit以后二级缓存才生效
    • update操作commit以后才会使缓存失效
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-03-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Mybatis一级缓存问题
很多 Java 后端服务都选择使用 MyBatis 作为它们的 ORM 框架,帮助简化开发。但是基本上大家都不会太关心 MyBatis 的缓存机制,基本都在使用 MyBatis 缓存的默认配置,在不了解 MyBatis 缓存机制的情况下进行开发,可能就会发生一些意料之外的事情。下面帮助大家了解 MyBatis 一级缓存和二级缓存的机制,以及如何避免 MyBatis 缓存中的坑。
政采云前端团队
2023/12/20
3860
Mybatis一级缓存问题
mybatis一级缓存和二级缓存工作方式_redis二级缓存
https://tech.meituan.com/2018/01/19/mybatis-cache.html
全栈程序员站长
2022/11/05
4550
mybatis一级缓存和二级缓存工作方式_redis二级缓存
《从Java面试题来看源码》,项目中使用 Mybatis 缓存吗?为什么项目中不用 Mybatis 的二级缓存?
答:MyBatis 的二级缓存相对于一级缓存来说,实现了 SqlSession 之间缓存数据的共享,同时粒度更加的细,能够到 namespace 级别,通过 Cache 接口实现类不同的组合,对 Cache 的可控性也更强。 但 MyBatis 在多表查询时,极大可能会出现脏数据,有设计上的缺陷,安全使用二级缓存的条件比较苛刻。 在分布式环境下,由于默认的 MyBatis Cache 实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将 MyBatis 的 Cache 接口实现,有一定的开发成本,直接使用 Redis,Memcached 等分布式缓存可能成本更低,安全性也更高。
阿提说说
2022/12/02
4480
MyBatis从入门到精通(九)—源码剖析之二级缓存细节
⼆级缓存构建在⼀级缓存之上,在收到查询请求时,MyBatis ⾸先会查询⼆级缓存,若⼆级缓存未命中,再去查询⼀级缓存,⼀级缓存没有,再查询数据库。 ⼆级缓存------》 ⼀级缓存------》数据库 与⼀级缓存不同,⼆级缓存和具体的命名空间绑定,⼀个Mapper中有⼀个Cache,相同Mapper中的MappedStatement共⽤⼀个Cache,⼀级缓存则是和 SqlSession 绑定。
共饮一杯无
2022/11/28
4170
MyBatis 源码分析 - 缓存原理
在 Web 应用中,缓存是必不可少的组件。通常我们都会用 Redis 或 memcached 等缓存中间件,拦截大量奔向数据库的请求,减轻数据库压力。作为一个重要的组件,MyBatis 自然也在内部提供了相应的支持。通过在框架层面增加缓存功能,可减轻数据库的压力,同时又可以提升查询速度,可谓一举两得。MyBatis 缓存结构由一级缓存和二级缓存构成,这两级缓存均是使用 Cache 接口的实现类。因此,在接下里的章节中,我将首先会向大家介绍 Cache 几种实现类的源码,然后再分析一级和二级缓存的实现。下面先来分析 Cache 及其实现类。
田小波
2018/09/20
1K0
MyBatis 源码分析 - 缓存原理
MyBatis为了解决二级缓存脏读问题,究竟做了那些骚操作!
MyBatis为了提高我们的查询性能,专门设计了一级缓存和二级缓存,众所周知,我们在开发环境中,使用的缓存的时候,也会遇到各种各样的挑战,比如缓存穿透,缓存雪崩,数据脏读等等各种各样的问题,MyBatis也同样,在设计二级缓存的时候,MyBatis也同样遇见了各种挑战;
止术
2020/09/15
5680
​mybatis的缓存机制源码分析之一级缓存解析
MyBatis 中的缓存指的是 MyBatis 在执行一次SQL查询时,在满足一定的条件下,会把这个sql和对应的查询结果缓存起来。当再次执行相同SQL语句的时候,就会直接从缓存中进行提取,而不是请求到数据库。当然如果中间有更新操作,缓存会失效。
用户7634691
2021/10/19
3640
​mybatis的缓存机制源码分析之一级缓存解析
Mybatis学习笔记(五)- mybatis二级缓存原理
在前边的学习中我们大概得了解了mybatis扫描和注入原理和sql的执行过程,还有mybatis插件的原理,在后边我们学习了mybatis的一级缓存的原理。那么今天我们主要学习一下mybatis二级缓存吧!
写一点笔记
2020/12/31
3710
Mybatis学习笔记(五)- mybatis二级缓存原理
从Mybatis源码到Spring动态数据源底层原理分析系列四、Mybatis缓存源码分析
在Mybatis中, 有两层缓存, 我们不区分这两层缓存哪个叫一级缓存, 哪个叫二级....我把他们分为两种缓存, 一种是Mapper级别的缓存, 一种是session级别的缓存, 接下来我们细说这两种缓存
玖柒的小窝
2021/10/31
4290
mybatis 缓存机制
mybatis支持一、二级缓存来提高查询效率,能够正确的使用缓存的前提是熟悉mybatis的缓存实现原理;
JavaFish
2019/10/17
6500
Mybatis 一级缓存和二级缓存 - Java技术债务
MyBatis是常见的Java数据库访问层框架。在日常工作中,开发人员多数情况下是使用MyBatis的默认缓存配置,但是MyBatis缓存机制有一些不足之处,在使用中容易引起脏数据,形成一些潜在的隐患。
Java技术债务
2024/06/21
1320
Mybatis 一级缓存和二级缓存 - Java技术债务
Mybatis 一二级缓存实现原理与使用指南
Mybatis 与 Hibernate 一样,支持一二级缓存。一级缓存指的是 Session 级别的缓存,即在一个会话中多次执行同一条 SQL 语句并且参数相同,则后面的查询将不会发送到数据库,直接从 Session 缓存中获取。二级缓存,指的是 SessionFactory 级别的缓存,即不同的会话可以共享。
丁威
2019/08/30
1.3K0
Mybatis 一二级缓存实现原理与使用指南
聊聊MyBatis缓存机制
前言 MyBatis是常见的Java数据库访问层框架。在日常工作中,开发人员多数情况下是使用MyBatis的默认缓存配置,但是MyBatis缓存机制有一些不足之处,在使用中容易引起脏数据,形成一些潜在的隐患。 个人在业务开发中也处理过一些由于MyBatis缓存引发的开发问题,带着个人的兴趣,希望从应用及源码的角度为读者梳理MyBatis缓存机制。 本次分析中涉及到的代码和数据库表均放在GitHub上,地址: mybatis-cache-demo 。 目录 本文按照以下顺序展开。 一级缓存介绍及相关配置。
美团技术团队
2018/03/13
1.6K0
聊聊MyBatis缓存机制
Mybatis 源码分析(三)之 Mybatis 的一级缓存和二级缓存
Mybatis系列: Mybatis 基础介绍与逆向工程的构建 :https://www.jianshu.com/p/1c18db4d7a38 Mybatis 源码分析(一)之 Mybatis 的Executor的初始化:https://www.jianshu.com/p/c7425c841337 Mybatis 源码分析(二)之 Mybatis 操作数据库的流程 :https://www.jianshu.com/p/11d354ec3612 Mybatis 源码分析(三)之 Mybatis 的一级缓存和二级缓存 :https://www.jianshu.com/p/5515640d14fe
zoro
2019/04/11
6970
Mybatis之Executor
                                                                    图1
克虏伯
2019/10/21
7060
Mybatis之Executor
Mybatis 缓存系统源码解析
Mybatis Mapper.xml 配置文件中 resultMap 节点的源码解析
Java技术编程
2020/05/21
5730
Mybatis深入源码分析之SqlSessionFactory二级缓存原理分析
mybatis的一级缓存是SqlSession缓存,在操作数据库的时候需要先创建SQL会话对象,在对象中有个HashMap用于存储缓存数据,此HashMap是当前对象私有的,其它SqlSession会话对象无法访问。
须臾之余
2019/07/30
8240
Mybatis深入源码分析之SqlSessionFactory二级缓存原理分析
mybatis的二级缓存_mybatis的一级缓存
需要注意的是二级缓存需要配置,一共有两种方式,下面分开讲解,举例中涉及到的表结构为用户表user(id,username,age)
全栈程序员站长
2022/11/08
3170
mybatis的二级缓存_mybatis的一级缓存
MyBatis 二级缓存详解
我们在上一篇文章介绍了 MyBatis 的一级缓存的作用,如何开启,一级缓存的本质是什么,一级缓存失效的原因是什么?MyBatis 只有一级缓存吗?来找找答案吧!
cxuan
2019/08/13
3770
MyBatis 二级缓存详解
@万字长文 | MyBatis 缓存到底
在 Web 应用中,缓存是必不可少的组件。通常我们都会用 Redis 或 memcached 等缓存中间件,拦截大量奔向数据库的请求,减轻数据库压力。作为一个重要的组件,MyBatis 自然也在内部提供了相应的支持。通过在框架层面增加缓存功能,可减轻数据库的压力,同时又可以提升查询速度,可谓一举两得。MyBatis 缓存结构由一级缓存和二级缓存构成,这两级缓存均是使用 Cache 接口的实现类。因此,在接下里的章节中,我将首先会向大家介绍 Cache 几种实现类的源码,然后再分析一级和二级缓存的实现。
田维常
2020/12/30
5910
相关推荐
Mybatis一级缓存问题
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验