一、序言
在使用MyBatis、MybatisPlus等DAO层数据库访问框架式,常常会与一级缓存、二级缓存打交道,为了增强对缓存体系的整体把控力,提高软件应用响应速度,这里对三级缓存一次梳理。
缓存固然能够提高系统性能,与此同时也带来了脏数据的副作用,系统的缓存体系、缓存结构、缓存策略、缓存介质等对可能出现的脏数据产生影响。
缓存是一把双刃剑,既能够提高应用系统的效率,同时避免脏数据发生也是不小的工作量。特别是不同的层次的缓存同时使用时,出现数据异常的概率快速提高。
二、一级缓存
以MyBatis技术为基础的一级缓存默认是开启的且无法关闭,有SESSION
和STATEMENT
两种类型。同一会话在关闭前可以执行多个语句,会话在关闭时,一级缓存生命周期结束。常见的情况是一个会话执行一条SQL语句,因此这两种类型区别不大。
mybatis:
configuration:
# 强制使用语句级缓存
local-cache-scope: statement
1、脏数据分析
一级缓存可能出现的脏数据问题:当一次会话调用两次以上相同的查询语句(包含查询条件)时,第二次以后调用会从本地缓存取数据,与此同时如果另一个会话将有关的数据修改,显而易见从缓存查询的数据是脏数据。
尽管这种现象是存在的,考虑到会话的持续时间可控,会话结束后数据查询即恢复正常,大多数情况下数据的实时行达不到此要求。
2、回避脏数据
- 强制使用语句级缓存
在全局配置中强制使用语句级缓存,防止系统因会话未及时关闭而产生的缓存脏数据
- 会话及时关闭
推荐一个会话仅执行一条SQL语句,并且SQL语句执行完毕后及时关闭会话,会话关闭时,根据事务自动提交机制,本次会话缓存自动释放。
- 避免使用复杂查询语句
将复杂查询语句转变成多条简单语句,在业务层通过事务汇总处理。事实上,随着数据量的急剧膨胀,复杂SQL语句对查询性能的负面影响越来越大。MybatisPlus连接查询解决方案甚至强烈推荐开发者弃用传统方式上的多表连接查询。
三、二级缓存
二级缓存面向namespace
,同一个 Mapper 文件下所有的 DAO 方法都能对缓存施加影响。二级缓存默认是关闭状态。
正确使用二级缓存,请参考MybatisPlus二级缓存解决方案一文。
1、脏数据分析
二级缓存产生脏数据的情况有很多,典型的场景如下:
- 联合查询
当表 A 和表 B 联合查询时,将查询数据添加至所在 Mapper 所属namespace
的缓存中,与此同时,表 A 或者表 B 对数据库数据做了更新,联合查询与更新表如果不在同一个namespace
下,在缓存刷新时间结束前是收不到更新缓存的信号的,毫无疑问是存在脏数据的。
2、回避脏数据
- 设置合理的缓存过期时间
二级缓存数据强制设置过期时间,保证缓存数据拥有被动失效的能力。
- 避免使用传统意义上的多表连接查询
强烈推荐使用MybatisPlus作为基础技术操作数据访问,保证能够正确的基于namespace
为单位的缓存数据能够主动刷新。新型多表连接查询操作,请查看MybatisPlus连接查询解决方案。
四、三级缓存
三级缓存指业务层缓存,通常面向service
层,主要缓存不常变化或者重复计算耗费CPU资源的数据。一般来讲,三级缓存存在于二级缓存之上。业务层缓存实现非常多,常见有缓存实现有:
- Caffeine使用手册
- EhCache使用手册
- Redis使用手册
业务层缓存自主可控强,能够全方位掌控缓存的生命周期,相当灵活方便。
1、使用场景
业务缓存,顾名思义是因处理业务流程而产生的数据缓存需要,比如说一项重复的计算,因为调用频率较高,因此可以对结果予以缓存。
业务层调用DAO层获取数据建议使用二级缓存完成,业务层的主要目标是使用数据,缓存数据并不是其主要职责。
五、接口缓存
接口缓存面向整个接口,面向用户端提高接口的响应性能。接口层可以直接调用数据访问层,或者调用数据访问层(二级缓存),异或调用业务层,异或调用业务层(三级缓存),甚至对接口返回结果本身进行缓存。