Mybatis源码之缓存模块分析

缓存这个东西在很多应用中都能看到它们的身影,这次就讲讲在Mybatis中的缓存是怎么应用的,虽然说吧Mybatis中的缓存基本不怎么用,用的更多是第三方组件redis、MongoDB、MemCache等等。

Mybatis的缓存是基于Map实现的,从缓存中读写数据是缓存模块的核心功能,但是除了这个功能Mybatis也提供了很多附加功能,比如防止缓存击穿、添加缓存清空策略等等,并且这些附加的功能属性可以随意组合到核心功能上。

缓存在Mybatis中的使用介绍

Myabtis中有两个缓存,一级缓存以及二级缓存,

一级缓存是默认开启的,而二级缓存是在配置文件中开启的,默认是开启,但是可以配置不开启,Mybatis配置参数定义

一级缓存的仅仅存在于同一SqlSession当中,如果关闭了SqlSession的话,那么这个缓存就没有了,

二级缓存是跨SqlSession的存在,同一个SqlSessionFactory是有效的,在多个SqlSession当中,也会存在多个二级缓存的存在,所以就容易出现数据脏读,个人建议二级缓存还是禁止比较好,使用第三方的缓存组件。

在对应的Mapper.xml当中使用:

eviction:使用什么类型的缓存,默认LRU缓存,缓存满后,将使用的最少的缓存去除;

flushInterval:刷新时间;

readOnly:是否只读;

size:缓存大小;

blocking:是否采用阻塞策略;

 <cache eviction="FIFO" flushInterval="6000" readOnly="false" size="1024"></cache>

 缓存的引用,直接引用对应的工作空间,引用后,就会直接使用这个引用的缓存:

 <cache-ref namespace="xxx.xxx.xxxDao"></cache-ref>

设计模式

先来看看这个模块用到了哪些设计模式

装饰器模式(Decorator Pattern)

定义:允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

像这种组合附加功能的业务上也可以通过继承去组合相对应的属性,但是继承的方式是静态的,并不能控制增加新的属性,如果功能属性较多的话,那么使用继承就会有很多子类,可读性不强。

图示:

组件(Component):组件接口定义了全部组件类 和装饰器实现的行为;

组件实现类(ConcreteComponent):实现 Component接口,组件实现类就是被装饰器装饰的 原始对象,新功能或者附加功能都是通过装饰器添加 到该类的对象上的;

装饰器抽象类(Decorator):实现Component接 口的抽象类,在其中封装了一个Component 对象, 也就是被装饰的对象;

具体装饰器类(ConcreteDecorator):该实现类 要向被装饰的对象添加某些功能;

相对于继承来说,使用装饰器模式的灵活性更高,扩展性更强。

装饰器使用:

JDK中的IO输入流与输出流设计:

BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream("c://a.txt")));

在Servlet API当中的对request的封装类HttpServletRequestWrapper;

源码分析

Mybatis的缓存模块就是使用的装饰器模式

BlockingCache示例:

同一个缓存的接口Cache(这个接口是缓存模块的核心接口,定义了缓存的基本操作):

public interface Cache {/*** @return The identifier of this cache*/String getId();/*** @param key*          Can be any object but usually it is a {@link CacheKey}* @param value*          The result of a select.*/void putObject(Object key, Object value);/*** @param key*          The key* @return The object stored in the cache.*/Object getObject(Object key);/*** @param key*          The key* @return Not used*/Object removeObject(Object key);/*** Clears this cache instance.*/void clear();/*** Optional. This method is not called by the core.** @return The number of elements stored in the cache (not its capacity).*/int getSize();/*** Optional. As of 3.2.6 this method is no longer called by the core.* <p>* Any locking needed by the cache must be provided internally by the cache provider.** @return A ReadWriteLock*/default ReadWriteLock getReadWriteLock() {return null;}}

在不同属性的实现类分别封装一个Cache对象,实现了每个附加功能的组合。

FifoCache:一个先进先出的缓存,如果缓存的数量达到了临界点,则将缓存时间最长的那个缓存去除。

LoggingCache:附加了日志以及统计功能。

 LruCache:如果缓存数据达到了临界点,会将访问次数最少的缓存数据清空。

ScheduledCache:会定时清除缓存的一个缓存。

SerializedCache:一个可以序列化以及反序列化的缓存。

SoftCache:软引用缓存,如果缓存被GC回收后,对应的缓存数据也会被清空。

WeakCache:弱引用缓存,如果缓存被GC回收后,对应的缓存数据也会被清空。

SynchronizedCache:同步运行的缓存。

TransactionalCache:一个存在事务提交和回滚的缓存。

BlockingCache:包含阻塞机制的缓存

在缓存模块中用到的缓存类实际上是PerpetualCache,以上的那些缓存类仅仅是附加功能,具体实现缓存功能的是PerpetualCache。

public class PerpetualCache implements Cache {private final String id;//数据被缓存的地方private final Map<Object, Object> cache = new HashMap<>();public PerpetualCache(String id) {this.id = id;}@Overridepublic String getId() {return id;}@Overridepublic int getSize() {return cache.size();}@Overridepublic void putObject(Object key, Object value) {cache.put(key, value);}@Overridepublic Object getObject(Object key) {return cache.get(key);}@Overridepublic Object removeObject(Object key) {return cache.remove(key);}@Overridepublic void clear() {cache.clear();}@Overridepublic boolean equals(Object o) {if (getId() == null) {throw new CacheException("Cache instances require an ID.");}if (this == o) {return true;}if (!(o instanceof Cache)) {return false;}Cache otherCache = (Cache) o;return getId().equals(otherCache.getId());}@Overridepublic int hashCode() {if (getId() == null) {throw new CacheException("Cache instances require an ID.");}return getId().hashCode();}}

一个比较简单的基于Map的缓存实现类。

缓存击穿

对于数据库来说,缓存是一个很好的东西,它能够挡住大部分的数据请求,但是如果在缓存中找不到对应的key和value时,那么这个这些请求就会去请求数据库,如果这个请求是在高并发的情况或者数据是“热数据”的情况,那么数据库访问那么也会变成高并发访问查询,这就是缓存击穿了。

所以在设计缓存模块的时候就应该考虑到这个问题,在Mybatis里面这个问题已经得到解决,BlockingCache就是解决方法。

我们先来看看缓存击穿的一些解决办法:

第一种:如果当前缓存被击穿后,不应该让所有的请求去请求数据库,而是只让一个请求去请求数据库,其他线程等待,当数据查询完毕并缓存进去后,再让其他线程去查询缓存。

但是这个方法存在一个问题,面对大量的请求来说,就会有效率问题。

第二种:既然以上方法存在效率问题,那么就可以给同等类型的key上锁,让相同的请求只派出一个请求去请求数据库,其他同等类型key的请求就等待,当请求到后,那么就释放锁其他线程回去缓存中获取数据了。

但是如果key太多的话,会比较浪费资源。

综上所述:对应这种缓存的话,个人觉得应该要与业务挂钩,取得一个平衡点取设计缓存模块。

现在我们来看看BlockingCache是怎么玩的,其实就是给每个key上锁然后,请求完在释放锁。

我目前的Mybatis版本是最新的3.5.7,可能其他版本并非使用的事CountDownLatch,而是使用的ReentrantLock,基本都是一样,基于AQS同步,想要了解线程相关的东西,可以去看看我之前发布博客哟。

CacheKey

现在来看一下缓存的key是怎么组成的,缓存模块中的key值并非直接的使用SQL语句中的唯一ID啥的。

在Mybatis设计缓存时,就将key包装成一个对象,只有这个对象的hashCode相等,这两个key才能一致。

构成CacheKey的因素有四个:

1、mappedStatment的id ;

2、指定查询结果集的范围(分页信息);

3、查询所使用的SQL语句;

4、用户传递给SQL语句的实际参数值;

update方法

equals方法

Mybatis的缓存基本讲的差不多了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/537125.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mybatis源码之核心流程分析

终于谈到了Mybatis最核心的东西了&#xff0c;最核心的就是通过配置XML文件或注解中的SQL&#xff0c;直接调用接口就能执行配置好的SQL语句并封装成对应的返回类型的数据。 先看一下Mybatis使用示例&#xff1a; //创建Builder对象 SqlSessionFactoryBuilder builder new S…

Mybatis源码之与Spring集成包

这次讲讲Mybatis与Spring的整合&#xff0c;作为两款优秀的开源框架&#xff0c;被大众广泛使用&#xff0c;自然是需要强强联合的。 使用示例 先看一下怎么使用&#xff0c;首先需要引用这两款框架的jar包&#xff1a; <dependency>//spring-webmvc会自动去引入其他S…

Mybatis源码之插件模块分析

总结完这个Mybatis的整体主要功能基本上就差不多完&#xff0c;还有一些细节的部分&#xff0c;后续都会记录补充。 插件这个东西一般用的比较少&#xff0c;就算用的多的插件也算是PageHelper分页插件&#xff1b; PageHelper官网&#xff1a;https://github.com/pagehelper…

AMD推出7nm高端显卡Radeon VII,直指英伟达RTX 2080

显卡战争已经发展到了2019年&#xff0c;并且变得比任何人预想的都要激烈。 CES 2019大会上&#xff0c;AMD发布了第一款消费级的 7nm GPU&#xff0c;取名&#xff1a;Radeon VII。据了解&#xff0c;这不是 AMD 的第一颗 7nm 处理器&#xff08;早期以 AI 运算为主的 Radeon …

Spring集成Mybatis多数据源配置

既然在整理Mybatis那就把经常用的这个多数据源的笔记也整一下吧。 Spring集成Mybatis在之前就已经提到了。Spring集成Mybatis 集成Mybatis多数据源有两种方式&#xff1a; 1、创建多个SqlSessionFactory&#xff0c;扫描每个SqlSessionFactoryBean对应的包&#xff0c;形成了…

Spring文件上传

2019独角兽企业重金招聘Python工程师标准>>> Spring文件上传 1、所需依赖包&#xff1a;commons-fileupload-1.3.1.jar2、Maven配置文件pom.xml文件中加入依赖Jar包<dependency><groupId>commons-fileupload</groupId><artifactId>commons-…

算法题学到的一些小语言细节

1.要学会用i&#xff1b;可以简化很多代码&#xff1a;i&#xff1b;copyFromMe(i)&#xff1b;可以写成&#xff1a;copyFromeMe(i) 2.StringBuffer也跟列表一样有append函数&#xff1b; 3.if语句是分支不能进行循环&#xff0c;要写成while才能替代循环里面的判断 4. 这里的…

Zookeeper基础常用操作以及ACL权限

这次将Zookeeper的一些基础用法以及权限这块的都补充一下在这篇博客中。 上篇博客介绍了基于ZooKeeper实现的分布式锁&#xff0c;也介绍了一些ZooKeeper的节点类型以及监听机制&#xff0c;今天这里就不作过多的介绍了&#xff0c;大家也可以自行的去官方文档上看看更具体的介…

[中医经络学习一]足阳明胃经

人体有六脏&#xff08;心、肝、脾、肺、肾五脏&#xff0c;再加心包&#xff09;六腑&#xff08;胃、小肠、大肠、膀胱、胆、三焦&#xff09;&#xff0c;每个脏腑都联接着一条经络&#xff0c;一共12条经络&#xff0c;称“十二正经”&#xff0c;经络的走向在四肢两侧基本…

ThreadLocal原理解析以及是否需要调用remove方法

平常的开发过程中&#xff0c;如果有个类不是线程安全的&#xff0c;比如SimpleDateFormat&#xff0c;要使这个类在并发的过程中是线程安全的&#xff0c;那么可以将变量设置位局部变量&#xff0c;不过存在的问题就是频繁的创建对象&#xff0c;对性能和资源会有一定降低和消…

Maven基础及概念

前言 今天就来聊聊Maven的基础和一些比较概念性的东西&#xff0c;还有一些常用的Maven命令啥的&#xff0c;主要是某人脑子记不住&#xff0c;记在博客中让她自己看吧&#xff0c;省的费心给她找。 后续的文章会聊到Maven的一些比较高级用法&#xff0c;像自定义插件&#x…

Maven之pom.xml常用标签解析及镜像配置

前言 Maven仅仅是个打包工具而已&#xff0c;个人觉得没有太大必要花费在打包工具上&#xff0c;这里就列举一下个人觉得会常用标签的使用就好了&#xff0c;原理啥的基本就不太会去深度了解了&#xff0c;如果以后遇到需了解Maven工作原理的工作的话&#xff0c;到时候一定分…

Maven高级之插件开发

前言 终于来到了Maven的插件开发&#xff0c;其实Maven的插件并没有想象的那么难&#xff0c;刚开始讲Maven基础的时候就演示了一下JDK是如何打包的&#xff0c;Maven打包只是在JDK打包上封装了一层而已&#xff0c;Maven也支持自定义插件开发 创建 我们先使用quickstart原型…

Maven高级之archetype(原型/骨架)开发

前言 archetype这个的主要功能就是将写好的项目模块打包成一个原型&#xff0c;然后提供给其他人使用&#xff0c;这样别人就可以快速使用这个项目模板了。 这个东西虽然很多人都基本用不上&#xff0c;但原型这个东西用的好还是很方便的&#xff0c;能够在开发新项目上省去大…

深度学习在搜索业务中的探索与实践

本文根据美团高级技术专家翟艺涛在2018 QCon全球软件开发大会上的演讲内容整理而成&#xff0c;内容有修改。引言 2018年12月31日&#xff0c;美团酒店单日入住间夜突破200万&#xff0c;再次创下行业的新纪录&#xff0c;而酒店搜索在其中起到了非常重要的作用。本文会首先介绍…

SpringBoot自动配置原理流程

前言 新公司太忙了&#xff0c;都没啥空更新博客&#xff0c;就随便记录一下以前的学习笔记吧。SpringBoot是基于Spring上的衍生框架&#xff0c;只要看懂了Spring的话&#xff0c;学这个就比较简单了&#xff1b;SpringBoot也是在当前微服务时代下流行的框架&#xff0c;并且…

SpringBoot自定义Starter(自动配置类)

前言 SpringBoot其实从诞生以来围绕的核心就是快速构建项目&#xff0c;快速构建的前提是有人帮你做好轮子&#xff0c;开发者只要拿来即用就好了&#xff0c;而造好轮子的人就是SpringBoot的开发者&#xff0c;引入自动配置的形式帮助开发者快速创建项目&#xff0c;而自动配…

Java并发编程之synchronized关键字解析

前言 公司加班太狠了&#xff0c;都没啥时间充电&#xff0c;这周终于结束了。这次整理了Java并发编程里面的synchronized关键字&#xff0c;又称为隐式锁&#xff0c;与JUC包中的Lock显示锁相对应&#xff1b;这个关键字从Java诞生开始就有&#xff0c;称之为重量级锁&#xf…

通过代理模式 + 责任链模式实现对目标执行方法拦截和增强功能

前言 最近需要实现一个插件功能&#xff0c;但是如果做成两个接口的话&#xff08;即执行前和执行后&#xff09;&#xff0c;那么会降低插件的可玩性&#xff0c;所以需做成类似AOP的环绕通知形式&#xff0c;所以就使用到了责任链模式和代理模式进行实现。 介绍 代理模式(P…

vscode Go 1.11.4 编译错误 need Delve built by Go 1.11 or later

更新golang的版本为1.11.4之后vscode编译错误&#xff1a;executables built by Go 1.11 or later need Delve built by Go 1.11 or later 原因是delve的版本太老了&#xff0c;需要更新&#xff0c;且delve的github地址已经更换&#xff0c;很多教程里的地址是不对的 新地址安…