一:mybatis 介绍cache和使用cache
mybatis自带的缓存构架,方便配置、功能强大、定制方便。默认情况下cache是没有被开启的。想要开启此功能需要在sql映射文件中添加这样一行:
<cache />
添加这一行后,会产生如下效果:
- 映射语句文件中的所有select 语句将会被缓存。
- 映射语句文件中的所有insert,update 和delete 语句会刷新缓存。
- 缓存会使用Least Recently Used(LRU,最近最少使用的)算法来收回。
- 根据时间表(比如no Flush Interval,没有刷新间隔), 缓存不会以任何时间顺序来刷新。
- 缓存会存储列表集合或对象(无论查询方法返回什么)的1024 个引用。
- 缓存会被视为是read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
以上所有的属性都可以通过<cache />
元素来修改:
<cache
eviction="FIFO"//回收策略
flushInterval="60000"//刷新间隔
size="512"//引用数目
readOnly="true"//只读
/>
以上表明首先创建了一个先进先出策略的缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会导致冲突。
可用的收回策略有:
- LRU – 最近最少使用的:移除最长时间不被使用的对象。
- FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
- SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
- WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
默认的是LRU。
属性flushInterval(刷新间隔)可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
属性size(引用数目)可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目。默认值是1024。
属性readOnly(只读)属性可以被设置为true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全,因此默认是false。
在<select />
标签中关于缓存的使用
属性
flushCache:将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:false。
useCache:将其设置为 true,将会导致本条语句的结果被二级缓存,默认值:对 select 元素为 true。如果设置为false,那么即使启用了<cache />
该条select的结构也不会被二级缓存。
二:下面结合springMVC+mybatis+mysql实际检验
模拟正常情况:
- console输出:
1.对比已有缓存的命中是0.0,所以从数据库中fetch数据,产生一次数据库查询。
2.紧接着第二次请求,从缓存中对比,比率是0.5,达到mybatis设定的范围,返回缓存数据,没有产生数据库查询。
3.过了60秒后请求,从缓存中对比,比率是0.33333333,没有达到mybatis设定的范围,从数据库中fetch数据,产生一次数据库查询。如果从cache标签中去掉flushInterval属性,该查询结果将始终走缓存,只当在项目中发送delete、update、insert操作时才会更新缓存。
非正常情况:
1.第一次请求正常输出=>手动从数据库删除数据、从另一个项目更改数据=>第二次查询=>返回结果 手动删除:DELETE FROM user WHERE username = '经纪人1'
2.第二次请求后输出结果
- console输出:
这样就出现了脏数据!
三:mybatis缓存使用注意事项
如果数据缓存在本地,另一个系统修改数据库或者手动修改数据库时,会出现脏数据问题。
一级缓存
Myatis的一级缓存默认为SESSION,底层用PerpetualCache,里面使用map做为存储,并没有做太多条件限制。二级缓存
MyBatis虽然全局配置开启缓存,但是还是取决于是否使用了<cache/>
标签,如果使用了二级缓存,需要注意:
每个<cache />
代表一个单独的二级缓存,如果多个Mapper需要共享同一个二级缓存,就需要使用<cache-ref/>
如果一个Mapper中查询数据时,使用了多表联查,当另一个Mapper更新相关数据时,如果没有共享一个Cache,那么下一次该Mapper查询时,就会出现读到脏数据。使用二级缓存一般基于以下原则:
不经常变动的数据,但经常会使用
数据量比较大,系统多处会用到。或者跨系统用。
对性能有特别要求的地方。
滥用二级缓存,有可能反而会降低性能,特别是根据条件查询缓存。
四:源码浅析
- 包内容浅析
cache包下,分为装饰者包(其中有些关于cache的具体类文件,比如阻塞加锁cache的委派、fifo的cache委派,使用装饰者模式起到了在不同使用场景灵活委派功能的目的,具有良好的扩展性)和cache底层实现包,以上类都实现了Cache接口。 - 调用顺序:
mybatis的关于cache有个CacheBuilder类,该类使用建造者模式,在其中有个public Cache build()方法负责具体的<cache />
属性的组装。通过MapperBuilderAssistant类调用CacheBuilder进行具体的装配顺序操作。
五:mybatis2级缓存机制、涉及的设计模式
百字不如一图
装饰者模式
装饰者模式在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装类的对象,也就是装饰来包裹真实的对象。
- 执行main()方法
public class Go {
public static void main(String[] args) {
System.out.println("套餐菜单\n");
EggsNoodles eggsNoodles = new EggsNoodles();
eggsNoodles.noodNameAndPrice();
DecoratorNoodles decoratorNoodles = new BeefNoodles(eggsNoodles);
decoratorNoodles.noodNameAndPrice();
DecoratorNoodles decoratorNoodles1 = new AubergineNoodles(eggsNoodles);
decoratorNoodles1.noodNameAndPrice();
DecoratorNoodles decoratorNoodles2 = new AubergineNoodles(decoratorNoodles);
decoratorNoodles2.noodNameAndPrice();
}
}
六:mybatis cache作用
mybatis cache 提供查询缓存,用于减轻数据库压力,提高数据库性能。快速响应用户请求,改善用户体验。适用于各种频繁查询数据且查询条件不是十分复杂的业务。
需要源码请留下邮箱
参考官方文档:
http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html#Auto-mapping