优秀的编程知识分享平台

网站首页 > 技术文章 正文

一文读懂Mybatis的一级缓存机制(mybatis一级缓存原理)

nanyue 2024-10-26 11:18:04 技术文章 4 ℃

前言

MyBatis内部封装了JDBC,简化了加载驱动、创建连接、创建statement等繁杂的过程,是我们常见的持久性框架。缓存是在计算机内存中保存的临时数据,读取时无需再从磁盘中读取,从而减少数据库的查询次数,提高执行效率。Mybatis提供了一级缓存和二级缓存的支持,默认情况下只开启一级缓存。本次将带着大家深入了解Mybatis的一级缓存机制。

介绍

当我们访问数据库时,Mybatis会创建出一个SqlSession对象开启一次数据库会话。在这次会话中,我们可能会执行多次相同的查询语句,如果不采取措施,每一次查询都会访问数据库,造成资源的浪费。为了优化这部分场景,Mybatis提供了一级缓存的方案,如果是相同的SQL语句,会优先命中一级缓存,避免直接对数据库进行查询。具体执行过程如下图所示。



每个SqlSession中都持有Executor执行器,每个Excutor中有一个LocalCache缓存。Mybatis根据当前执行的SQL生产MappedStatement,在缓存中进行查询,如果缓存命中的话,直接返回结果给用户,如果缓存没有命中,查询数据库,结果写入LocalCache,再返回结果给用户。具体实现类的类关系图如下所示:



源码分析

我们将对MyBatis的源码进行分析,深入理解底层原理。

SqlSession: SqlSession是应用程序与持久层执行交互操作的单线程对象,代表着一个资源的启用,包含所有执行SQL操作的方法。默认实现类是DefaultSqlSession。

主要作用:1、获取Mapper接口;2、发送SQL给数据库;3、控制数据库事务



Executor执行器:Executor定义了查询、更新、事务、缓存操作相关的接口方法,由SqlSession依赖,并受其调度与管理。核心功能是调度执行SQL。在Executor内部完成对LocalCache的查询和写入。




Cache:Mybatis中的Cache接口,提供了缓存的基本操作。



PerpetualCache是对Cache接口最基本的实现,其原理非常简单,内部持有HashMap,对一级缓存的操作实则是对HashMap的操作。如下代码所示:



客户端与数据库的交互,首先通过DefaultSqlSessionFactory开启SqlSession:



在初始化SqlSession时,会通过configuration类创建一个新的Executor:



SqlSession创建完毕后,根据不同类型的Statement,SqlSession会执行对应的方法。比如Select语句,最终会执行到selectList方法



SqlSession把具体的查询交给Executor去执行。如果只开启了一级缓存的话,首先会进入BaseExecutor的query方法。代码如下所示:



会根据传入的参数生成CacheKey作为唯一标识,进入方法,查看具体的生成逻辑:



在上述的代码中,将MappedStatement的Id、SQL的offset、SQL的limit、SQL本身以及SQL中的参数传入了CacheKey这个类,最终构成CacheKey。在CacheKey的update方法中,会进行一个hashcode和checksum的计算,同时把传入的参数添加进updatelist中。如下代码所示:



只要两条SQL的下列五个值相同,即可以认为是相同的SQL。

Statement Id + Offset + Limmit + Sql + Params

如果在LocalCache查不到的话,就会查询数据库,并将结果写入至缓存。



如果是insert/update/delete方法,统一都会走SqlSession的update流程,同样委托执行器执行SQL。



执行方法如下,我们可以发现每次执行update前都会清空LocalCache。所以当发生修改操作时,一级缓存会失效,防止了数据脏读。



总结

1、一级缓存LocalCache只是BaseExecutor的一个字段。所以,当SqlSession的生命周期结束时,一级缓存也会被回收。

2、一级缓存由PerpetualCache实现,其内部通过HashMap完成对缓存的读写操作,所以本质上讲,一级缓存其实是通过HashMap实现的,Map中的key是由MappedStatement生成CacheKey,来确保查询的唯一性。

3、MyBatis的一级缓存最大范围是SqlSession内部,有多个SqlSession或者分布式的环境下,数据库写操作会引起脏数据,建议设定缓存级别为Statement。

Tags:

最近发表
标签列表