mybatis缓存专题-一文彻底搞懂mybatis一级缓存(代码片段)

IT老刘 IT老刘     2022-12-18     647

关键词:

带着问题来学习?

1.缓存的概念

1.1.什么是缓存

存在于内存中的临时数据。

1.2.为什么使用缓存

减少和数据库的交互次数,提高执行效率。(因为查询数据库是一件很费时很费效率的事,还涉及一些硬盘等io操作,而缓存是存在内存中的,读取都很快,而且效率高)

1.3.什么样的数据能使用缓存,什么样的数据不能使用

  • 适用于缓存:
经常查询并且不经常改变的。
数据的正确与否对最终结果影响不大的。
  • 不适用于缓存:
经常改变的数据
数据的正确与否对最终结果影响很大的。
例如:商品的库存,银行的汇率,股市的牌价。

2.什么是一级缓存

MyBatis 包含了一个非常强大的查询缓存特性,它可以非常方便地配置和定制。MyBatis 3 中的缓存实现的很多改进都已经实现了,使得它更加强大而且易于配置。mybatis 默认情况下只会开启一级缓存,也就是局部的 session 会话缓存

首先我们要知道什么是查询缓存?查询缓存又有什么作用?

功能:mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。

如下图,每一个 session 会话都会有各自的缓存,这缓存是局部的,也就是所谓的一级缓存

3.什么情况下会命中一级缓存

  • 相同的 sql 和 参数
  • 必须是在一个会话 Session当中
  • 必须是执行 相同的方法
  • 必须是相同的 namespace (同一个命名空间 -> 同一个mapper文件)
  • 不能够在查询之前执行 clearCache
  • 中间不能执行 任何 updatedelete ,insert (会将SqlSession中的数据全部清空)

4.Mybatis的一级缓存机制详解

一级缓存是SqlSession级别的缓存。我们都知道在操作数据库时需要构造 sqlSession对象,而在sqlSession对象中有一个数据结构(HashMap)用于存储缓存数据。

如下图:

从图上,我们可以看出,一级缓存区域是根据SqlSession为单位划分的。每次查询都会先从缓存区域找,如果找不到就会从数据库查询数据,然后将查询到的数据写入一级缓存中。Mybatis内部存储缓存使用的是一个HashMap对象,key为 hashCode + sqlId + sql 语句。而value值就是从查询出来映射生成的java对象。而为了保证缓存里面的数据肯定是准确数据避免脏读,每次我们进行数据修改后(增、删、改操作)就会执行commit操作,清空缓存区域。

/**
 * 测试1级缓存
 */
public class TestCache1 

    @Test
    public void test1() throws Exception
        try 
            String resources = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resources);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
            UserMapper um = sqlSession.getMapper(UserMapper.class);
            User user1 = um.findById(1);
            User user2 = um.findById(1);
            System.out.println(user1==user2);
         catch (IOException e) 
            e.printStackTrace();
         finally 
        
    

运行结果:

用两张图来总结:
第一次:查数据库,放入到缓存中。

第二次:直接从缓存中获取。

下面这段代码中就使用不到缓存

    @Test
    public void test2() throws Exception
        try 
            String resources = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resources);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

            SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
            SqlSession sqlSession2 = sqlSessionFactory.openSession(true);

            UserMapper um1 = sqlSession1.getMapper(UserMapper.class);
            UserMapper um2 = sqlSession2.getMapper(UserMapper.class);

            User user1 = um1.findById(1);
            User user2 = um2.findById(1);
            
            System.out.println(user1==user2);
         catch (IOException e) 
            e.printStackTrace();
         finally 
        
    

运行结果:

用两张图来总结:
第一次查询:sqlSession1查询数据库,放入到缓存中。

第二次查询:sqlSession2查询数据库,放入到缓存中。

记住是一级缓存只能是同一个SqlSession对象就行了。

5.MyBatis关闭一级缓存

一级缓存也叫本地缓存(LocalCache),Mybatis的一级缓存是会话级别(SqlSession)层面进行缓存的。Mybatis的一级缓存是默认开启的。我们开发项目中不需要做任何配置,但是如果想关闭一级缓存,可以使用localCacheScopde=statement来关闭。

<settings>
    <!-- localCacheScope是本地缓存(一级缓存)的作用域,只有两种取值:SESSION和STATEMENT,取STATEMENT意味着关闭一级缓存-->
    <setting name="localCacheScope" value="STATEMENT"/>
</settings>

6.Mybatis的一级缓存机制源码分析

SqlSession和Executor都是接口 ,而实现是 DefaultSqlSession 和 CacheExecutor

  • Client相当于测试方法 Test1
  • User@Proxy 动态代理
  • Executor 接口 才是去数据库中拿数据的跑腿的
  • SqlSession 是接口,一级缓存的实现是通过 CacheExecutor 实现的

程序入口处断点

执行MapperProxy动态代理invoke

MapperMethod调用sqlsession的selectOne方法

本质调用DefaultSqlSession的selectList方法


调用BaseExecutor的query方法

BaseExecutor的query方法生成缓存的key

可以看出缓存key中是包含了 方法和namespace和会话 这些必须相同才会去做一个缓存命中
这里面封装了缓存唯一的key

继续执行BaseExecutor的query方法判断缓存是否存在

如果缓存中不存在,执行查询数据库

缓存中查询到数据存入缓存中


如果缓存中存在,执行查询缓存,PerpetualCache调用getObject方法

7.Mybatis的一级缓存机制源码分析图解总结

DefaultSqlSession中有一个CacheExecutor
CacheExecutor 中有一个 Simpleexexutor
Simpleexexutor 中有一个叫 LocalCache (PerpetualCache类型)
LocalCache才是真正的存储缓存的地方
LocalCache 中有一个叫cache (Hashmap <Object,Object>类型的)

8.一级缓存什么时候被清空?

在执行update、insert、delete、flushCache=“true”、commit、rollback、LocalCacheScope.STATEMENT等情况下,一级缓存就都会被清空。

@Override
public void clearLocalCache() 
    if (!closed) 
      localCache.clear();
      localOutputParameterCache.clear();
    

update时,一级缓存会被清空。delete和insert都是调用这个update。可以从SqlSession的insert、update、delete方法跟踪。

LocalCacheScope.STATEMENT时,一级缓存会被清空。在BaseExecutor里的query方法中:

事务提交回滚时,一级缓存会被清空。

flushCache="true"时,一级缓存会被清空。

9.一级缓存key是什么?

下面就是一级缓存key的创建过程

@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) 
  if (closed) 
    throw new ExecutorException("Executor was closed.");
  
  CacheKey cacheKey = new CacheKey();
  cacheKey.update(ms.getId());
  cacheKey.update(rowBounds.getOffset());
  cacheKey.update(rowBounds.getLimit());
  cacheKey.update(boundSql.getSql());
  List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
  TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
  // mimic DefaultParameterHandler logic
  for (ParameterMapping parameterMapping : parameterMappings) 
    if (parameterMapping.getMode() != ParameterMode.OUT) 
      Object value;
      String propertyName = parameterMapping.getProperty();
      if (boundSql.hasAdditionalParameter(propertyName)) 
        value = boundSql.getAdditionalParameter(propertyName);
       else if (parameterObject == null) 
        value = null;
       else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) 
        value = parameterObject;
       else 
        MetaObject metaObject = configuration.newMetaObject(parameterObject);
        value = metaObject.getValue(propertyName);
      
      cacheKey.update(value);
    
  
  if (configuration.getEnvironment() != null) 
    // issue #176
    cacheKey.update(configuration.getEnvironment().getId());
  
  return cacheKey;


key的生成策略:id + offset + limit + sql + param value + environment id,这些值都相同,生成的key就相同。

10.是否会存在数据不一致问题?

不会 因为数据库本身是具有事务隔离级别的 默认是可重复读 也就是说任何一个事务在它内部读到的数据都是一致的,所以并不存在数据不一致的问题

11.一级缓存总结

  • 一级缓存的生命周期和SqlSession对象的生命周期一致。所以缓存维护在SqlSession中的属性executor里。

  • 一级缓存默认开启。可以通过修改配置项把一级缓存关掉。

  • 清空一级缓存的方式有:

update、insert、delete
flushCache="true"
commit、rollback
LocalCacheScope.STATEMENT

mybatis缓存专题-一文彻底搞懂mybatis二级缓存(代码片段)

...属性3.1.type3.2.eviction3.3.flushInterval3.4.size3.5readOnly3.6blocking4.MyBatis的缓存机制整体设计以及二级缓存的工作模式5.使用二级缓存,必须要具备的条件6.一级缓存 查看详情

两张图彻底搞懂mybatis的mapper原理!

作者:肥朝简单使用这是一个简单的Mybatis保存对象的例子1@Test2publicvoidtestSave()throwsException{3//创建sessionFactory对象4SqlSessionFactorysf=newSqlSessionFactoryBuilder().5build(Resources.getResourceAsStream("mybatis-config.xm 查看详情

一文搞懂mybatis架构与工作原理(代码片段)

前言本文将从宏观角度分析Mybatis的架构与工作原理。架构Mybatis的功能架构分为三层:API接口层:提供给外部使用的接口API,开发人员通过这些API操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体... 查看详情

mybatis专题-----代码生成器关联查询缓存

MybatisGenerator(MBG)概念MyBatisGenerator:MyBatis的开发团队提供了一个很强大的代码生成器,代码包含了数据库表对应的实体类、Mapper接口类、MapperXML文件和Example对象等,这些代码文件中几乎包含了全部的单表操作方法,使用MBG可以极... 查看详情

一文彻底搞懂zookeeper(代码片段)

本文是基于CentOS7.9系统环境,进行Zookeeper的学习和使用1.Zookeeper简介1.1什么是ZookeeperZookeeper是一个开源的分布式的,为分布式应用提供协调服务的Apache项目。本质上,就是文件系统+通知机制1.2Zookeeper工作机制Zookeepe... 查看详情

一文彻底搞懂zookeeper(代码片段)

本文是基于CentOS7.9系统环境,进行Zookeeper的学习和使用1.Zookeeper简介1.1什么是ZookeeperZookeeper是一个开源的分布式的,为分布式应用提供协调服务的Apache项目。本质上,就是文件系统+通知机制1.2Zookeeper工作机制Zookeepe... 查看详情

一文彻底搞懂slam技术(代码片段)

什么是SLAM?SLAM (simultaneouslocalizationandmapping),也称为CML(ConcurrentMappingandLocalization),即时定位与地图构建,或并发建图与定位。问题可以描述为:将一个机器人放入未知环境中的未知位置,是否有办法让机器人一边逐步描... 查看详情

一文彻底搞懂slam技术(代码片段)

什么是SLAM?SLAM (simultaneouslocalizationandmapping),也称为CML(ConcurrentMappingandLocalization),即时定位与地图构建,或并发建图与定位。问题可以描述为:将一个机器人放入未知环境中的未知位置,是否有办法让机器人一边逐步描... 查看详情

一文彻底搞懂前端沙箱(代码片段)

什么是“沙箱”沙箱(Sandbox)[1]也称作:“沙箱/沙盒/沙盘”。沙箱是一种安全机制,为运行中的程序提供隔离环境。通常是作为一些来源不可信、具破坏力或无法判定程序意图的程序提供实验之用。沙箱能够安全的执行不受信... 查看详情

一文彻底搞懂docker中的namespace(代码片段)

什么是namespacenamespace是对全局系统资源的一种封装隔离。这样可以让不同namespace的进程拥有独立的全局系统资源。这样改变一个namespace的系统资源只会影响当前namespace中的进程,对其它namespace中的资源没有影响。以前Linux也... 查看详情

一文彻底搞懂kafka(代码片段)

Kafka的学习和使用本文是基于CentOS7.9系统环境,进行Kafka的学习和使用一、Kafka的简介1.1Kafka基本概念(1)什么是KafkaKafka是一个分布式的基于发布/订阅模式的消息队列,主要应用于大数据实时处理领域(2)消息队列点对点模式... 查看详情

一文彻底搞懂hbase(代码片段)

本文是基于CentOS7.9系统环境,进行HBase的学习和使用一、HBase的简介1.1HBase基本概念HBase是一种分布式、可扩展、支持海量数据存储的NoSQL数据库,可以解决HDFS随机写的问题1.2HBase数据模型逻辑上,HBase的数据模型同关系... 查看详情

一文彻底搞懂hbase(代码片段)

本文是基于CentOS7.9系统环境,进行HBase的学习和使用一、HBase的简介1.1HBase基本概念HBase是一种分布式、可扩展、支持海量数据存储的NoSQL数据库,可以解决HDFS随机写的问题1.2HBase数据模型逻辑上,HBase的数据模型同关系... 查看详情

一文彻底搞懂leveldb架构(代码片段)

leveldbleveldb是一个写性能十分优秀的存储引擎,是典型的LSM-tree的实现。LSM的核心思想是为了换取最大的写性能而放弃掉部分读性能。那么,为什么leveldb写性能高?简单来说它就是尽量减少随机写的次数。leveldb首先将... 查看详情

一文彻底搞懂leveldb架构(代码片段)

leveldbleveldb是一个写性能十分优秀的存储引擎,是典型的LSM-tree的实现。LSM的核心思想是为了换取最大的写性能而放弃掉部分读性能。那么,为什么leveldb写性能高?简单来说它就是尽量减少随机写的次数。leveldb首先将... 查看详情

一文彻底搞懂leveldb架构(代码片段)

leveldbleveldb是一个写性能十分优秀的存储引擎,是典型的LSM-tree的实现。LSM的核心思想是为了换取最大的写性能而放弃掉部分读性能。那么,为什么leveldb写性能高?简单来说它就是尽量减少随机写的次数。leveldb首先将... 查看详情

一文彻底搞懂mysql基础:b树和b+树的区别

一文彻底搞懂MySQL基础:B树和B+树的区别_码农富哥-CSDN博客_b树和b+树有什么区别写在前面大家在面试的时候,肯定都会被问到MySql的知识,以下是面试场景:面试官:对于MySQL,你对他索引原理了解吗... 查看详情

一文彻底搞懂cookiesessiontoken到底是什么(代码片段)

点击上方关注“终端研发部”设为“星标”,和你一起掌握更多数据库知识责编:架构君 | 来源:不学无数的程序员链接:https://my.oschina.net/u/4030990/blog/3136476上一篇好文:MySQL数据库的优化,你知道有哪... 查看详情