在实际项目中,通常对数据库查询的性能要求很高,而MyBatis提供了查询缓存来缓存数据,从而达到提高性能的要求。
MyBatis的查询缓存分为一级缓存和二级缓存。一级缓存是sqlsession级别的缓存,二级缓存是mapper级别的缓存,二级缓存是多个sqlsession共享的。
一级缓存可用性:
一级缓存基于sqlsession,sqlsession又是单线程不共享的。所以只能在本次请求中使用,而如果本次请求有commit操作,比如update、insert、delete,那么缓存就会被刷新。简单来说,只有连续两次执行相同语句,一级缓存才有效果,比如:
select * from tb_user // 执行一次SQL语句 select * from tb_user // 缓存查询
而如果有commit操作
select * from tb_user // 执行一次SQL语句 update tb_user set name = 'lay' where id = 1 // commit 操作,清除了缓存 select * from tb_user // 第二次执行查询SQL
第一种情况,个人认为基本上不太会出现。所以一级缓存在一定程度上来说,基本上没有用。
二级缓存可用性:
二级缓存是基于mapper的,也就是说,一个mapper会有一个缓存,多个mapper会有多个缓存。并且mapper的缓存是被所有sqlsession共享的,不会因为sqlsession的关闭而清空。
select * from tb_user // 第一个sqlsession进来 select * from tb_user // 第二个sqlsession进来,查询到的是第一个sqlsession的缓存数据
缓存清空的情况
select * from tb_user // 一个sqlsession进来 update tb_user set name = 'lay' where id = 1 // 执行了commit操作,清空缓存 select * from tb_user // 第二个sqlsession进来,得重新执行sql
二级缓存属于一个mapper,也就是当前mapper中如果执行了commit操作,当前mapper才会被清空,那么显而易见的问题就出现了。如果有多表关联查询,commit操作只清空当前的mapper,其它mapper有关联的数据就还是旧的数据,不是出现了数据不一致的情况了吗?
比如:
select * from tb_user // 第一个mapper缓存了数据 select * from tb_class A left join tb_user B on A.userId = B.id // 第二个mapper缓存了数据,并关联tb_user的数据 update tb_user set name = 'lay' where id = 1 // 第一个mapper中执行了commit操作,清空了第一个mapper的缓存数据 select * from tb_class A left join tb_user B on A.userId = B.id // 第二个mapper的数据,查询到的是缓存,而且是旧的数据
所以,由于mybatis的二级缓存的机制,多表查询容易出现数据不一致问题,这点需要注意。当然网上也有一些解决方案,不过个人觉得那些方案会增加项目的复杂度,如果项目越来越大mapper的关联越复杂,那些解决方案最终可能导致项目难以理解的问题。
因此,如果对数据的实时性要求不高,查询频率却很高,比如某些排行榜?那么当前mapper使用二级缓存能大大提高性能。如果对数据实时性要求很高,频繁地执行commit操作,那么当前mapper还是不使用二级缓存了。简单来说,根据当前mapper的数据实时性来判断是否使用二级缓存。