缓存及缓存机制
传统的关系型数据库,十分强调数据的一致性,并为此降低读写性能付出了巨大的代价,虽然关系型数据库存储数据和处理数据的可靠性很不错,但一旦面对海量数据的处理的时候效率就会变得很差,特别是遇到高并发读写的时候性能就会下降的非常厉害。
比如这样一个场景:使用sql语句(select * from user where id =1;)查询了数据库中id为1的数据,此时数据库会进行查询操作,并返回一个User类型的数据,没有进行增删改的操作,然后再次使用sql语句查询数据库,程序会重复上面的步骤.在这种频繁的查询操作下,对数据库来说是一个巨大的挑战.
所以我们引入了缓存(cache)
,将经常不被修改并且频繁查询的数据放入内存中,减少对数据库的频繁读写操作,降低数据库的压力.当要读取数据时,会首先从内存中查找需要的数据,如果找到了则直接执行,找不到的话则从数据库中找。由于内存的运行速度比从数据库中查询快得多,故缓存的作用就是帮助查询更快地运行。
Mysql缓存机制
就是缓存sql 文本及缓存结果,用KV形式(是一种以键值对存储数据的一种形式,可以理解为一个大的map,每个键都会对应一个唯一的值。)保存再服务器内存中,如果运行相同的sql,服务器直接从缓存中去获取结果,不需要在再去解析、优化、执行sql。 如果这个表修改了,那么使用这个表中的所有缓存将不再有效,查询缓存值得相关条目将被清空。
对于频繁更新的表,查询缓存不合适,对于一些不变的数据且有大量相同sql查询的表,查询缓存会节省很大的性能。
Mybatis缓存机制
和大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的支持;
一级缓存是Mybatis默认开启的,是基于 PerpetualCache 的 HashMap 本地缓存,存储作用域为 Session,当 Session flush (会话刷新)或 close 之后,该Session中的所有 Cache 就将清空。
二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache、Hazelcast等。
一级缓存
在应用运行过程中,我们有可能在一次数据库会话中,执行多次查询条件完全相同的SQL,MyBatis提供了一级缓存的方案优化这部分场景,如果是相同的SQL语句,会优先命中一级缓存,避免直接对数据库进行查询,提高性能。具体执行过程如下图所示。
每个SqlSession中持有了Executor(执行器),每个Executor中有一个LocalCache(本地缓存)。当用户发起查询时,MyBatis根据当前执行的语句生成MappedStatement(在MyBatis启动时,会解析这些包含SQL的XML文件,并将其包装成为MapperStatement对象,并将MapperStatement注册到全局的configuration对象上),在Local Cache进行查询,如果缓存命中的话,直接返回结果给用户,如果缓存没有命中的话,查询数据库,结果写入Local Cache,最后返回结果给用户。具体实现类的类关系图如下图所示。
代码流程
- 第一次执行select完毕会将查到的数据写入SqlSession内的HashMap中缓存起来
- 第二次执行select会从缓存中查数据,如果select相同切传参数一样,那么就能从缓存中返回数据,不用去数据库了,从而提高了效率
注意事项:
- 如果SqlSession执行了DML操作(insert、update、delete),并commit了,那么mybatis就会清空当前SqlSession缓存中的所有缓存数据,这样可以保证缓存中的存的数据永远和数据库中一致,避免出现脏读
- 当一个SqlSession结束后那么他里面的一级缓存也就不存在了,mybatis默认是开启一级缓存,不需要配置
一级缓存的开启与关闭
- 一级缓存默认开启
- 关闭一级缓存需要在mybatis配置文件的settings标签设置
<setting name="localCacheScope" value="SESSION"/>
导致一级缓存不命中的原因
- 一级缓存关闭;
- 一级缓存开启,但是使用了不同的SqlSession进行查询;
- 使用相同的SqlSession,但是查询条件发生了变化;
- 使用了相同的查询条件,但是两次查询之间SqlSession执行了commit、clearCache或close(关闭之后查询会报错)操作;
- 使用了相同的查询条件,但是两次查询之间SqlSession执行了insert、update或delete操作,此时无论三种标签的
flushCache 属性是否为 false,都会清空 sqlSession的缓存; - select标签的flushCache属性为true
清空一级缓存的操作
- SqlSession调用commit方法;
- SqlSession调用clearCache方法;
- select标签的flushCache属性为true;
SqlSession执行了insert、update或delete操作,此时无论三种标签的flushCache属性是否为 false;
一级缓存实验
实验1
开启一级缓存,范围为会话级别,调用三次Getbyid
代码如下所示:
public static void main(String[] args) throws IOException {
}
SqlSession ss;
Userdao udao;
//定义一个解析工具
@Before
public void setup() throws IOException {
String resource = "MybatisConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
ss = sqlSessionFactory.openSession();
udao=ss.getMapper(Userdao.class);
}
@Test
public void Getbyid() {
System.out.println(udao.Getbyid1(1));
System.out.println(udao.Getbyid1(1