我们都知道缓存,缓存的作用也都是非常的明显,为了减轻数据库的压力,有些时候查询数据的时候,会把数据存入到缓存中,等接下来相同的请求查询数据时,直接从缓存中获取数据,不用再去访问数据库了,而合理使用缓存是优化中最常见的,避免频繁操作数据库,减轻数据库的压力,同时提高系统性能。
一级缓存是 SqlSession
级别的缓存。在操作数据库时需要构造 SqlSession
对象,在对象中有一个数据结构用于存储缓存数据。
不同的 SqlSession
之间的缓存数据区域是互相不影响的。也就是他只能作用在同一个 SqlSession
中,不同的 SqlSession
中的缓存是互相不能读取的。
一级缓存的工作原理图解:
用户发起查询请求,查找某条数据,SqlSession
先去缓存中查找,是否有该数据,如果有,读取;
如果没有,从数据库中查询,并将查询到的数据放入一级缓存区域,供下次查找使用。
但 SqlSession
执行 commit
,即增删改操作时会清空缓存。
那么清空的意义在哪,了不起都不用多说了吧,这清空的意义就是要保证我们读取的数据一定是准确的,如果你执行了增删改的操作之后,进行了 commit
之后,我们不做清空操作,那么查询的数据,肯定不是最新的,也就是会出现脏读的情况了。
如果不清空的情况下,就会有这种情况,某一件商品,库存有10件,这个时候我们查看的时候,发现库存10件,然后查询之后,写入了缓存,而接下来有人下单,一口气购买了10件,数据没了,如果我们不清空缓存中的内容,那么接下来我们还是从缓存中去取数的话,取出来的数据10就是一个错误的数据了。
二级缓存是 mapper
级别的缓存,多个 SqlSession
去操作同一个 Mapper
的 sql
语句,多个 SqlSession
可以共用二级缓存,二级缓存是跨 SqlSession
的。
二级缓存原理图:
UserMapper
有一个二级缓存区域(按 namespace
划分),每个 mapper
也有自己的二级缓存区域(按 namespace
分)。
每一个 namespace
的 mapper
都有一个二级缓存区域,如果相同两个 mapper
的 namespace
,这两个 mapper
执行 sql
查询到数据将存在相同的二级缓存区域中。
如果你使用了 Mybatis 的配置文件,那么就得增加配置为:
<settings>
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
如果你使用的是 yml 去配置的话,那么就得在 configuration
下去开启:
configuration:
cache-enabled: true
我们看一段测试代码:
@Test
public void testCache2() throws Exception {
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
User user1 = userMapper1.findUserById(1);
System.out.println(user1);
sqlSession1.close();
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
User user2 = userMapper2.findUserById(1);
System.out.println(user2);
sqlSession2.close();
}
日志打印:
DEBUG [main] - Cache Hit Ratio [com.iot.mybatis.mapper.UserMapper]: 0.0
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 103887628.
DEBUG [main] - Setting autocommit to false on JDBC Connection
[com.mysql.jdbc.JDBC4Connection@631330c]
DEBUG [main] - ==> Preparing: SELECT * FROM user WHERE id=?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
User [id=1, username=张三, sex=1, birthday=null, address=null]
DEBUG [main] - Resetting autocommit to true on JDBC Connection
[com.mysql.jdbc.JDBC4Connection@631330c]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c]
DEBUG [main] - Returned connection 103887628 to pool.
DEBUG [main] - Cache Hit Ratio [com.iot.mybatis.mapper.UserMapper]: 0.5
User [id=1, username=张三, sex=1, birthday=null, address=null]
我们可以从打印的信息看出,两个 SqlSession
,去查询同一条数据,只发起一次 select
查询语句,第二次直接从 Cache
中读取。
前面我们说到,Spring
和 MyBatis
整合时, 每次查询之后都要进行关闭 SqlSession
,关闭之后数据被清空。所以 spring
整合之后,如果没有事务,一级缓存是没有意义的。
那么如果开启二级缓存,关闭 sqlsession
后,会把该 sqlsession
一级缓存中的数据添加到 namespace
的二级缓存中。这样,缓存在 sqlsession
关闭之后依然存在。
1. 只能在一个命名空间下使用二级缓存
由于二级缓存中的数据是基于 namespace
的,即不同 namespace
中的数据互不干扰。在多个 namespace
中存在对同一个表的操作,那么这多个 namespace
中的数据可能就会出现不一致现象。
2. 在单表上使用二级缓存
如果一个表与其它表有关联关系,那么就非常有可能存在多个 namespace
对同一数据的操作。而不同 namespace
中的数据相互干扰,所以就有可能出现多个 namespace
中的数据不一致现象。
3. 查询多于修改时使用二级缓存
在查询操作远远多于增删改操作的情况下可以使用二级缓存。因为任何增删改操作都将刷新二级缓 存,对二级缓存的频繁刷新将降低系统性能。
号外!号外!
Java 极客技术微信群中有很多优秀的小伙伴在讨论技术,偶尔还有不定期的资料分享和红包发放!如果你想提升自己,并且想和优秀的人一起进步,感兴趣的朋友,可以在下方公众号后台回复:加群。
喜欢就分享支持就在看
一键四连,你的offer也四连