目录
一级缓存LocalCache
在应用运行过程中,我们有可能在一次数据库会话中,执行多次查询条件完全相同的 SQL,MyBatis 提供了一级缓存的方案优化这部分场景,如果是相同的 SQL 语句,会优先命中一级缓存,避免直接对数据库进行查询,提高性能。
缓存过程:每个 SqlSession 中持有了 Executor,每个 Executor 中有一个 LocalCache,当用户发起查询时,MyBatis 根据当前执行的语句生成 MappedStatement,在 Local Cache 进行查询,如果缓存命中的话,直接返回结果给用户,如果缓存没有命中的话,查询数据库,结果写入 Local Cache,最后返回结果给用户。
MyBatis 的一级缓存是指在同一个会话(SqlSession)中的缓存机制。一级缓存是 MyBatis 默认开启的,它位于 SqlSession 的内部,用于缓存在同一个会话中执行的 SQL 查询结果。
一级缓存的特点如下:
- 会话级别:一级缓存是与 SqlSession 相关联的,只在同一个会话中有效。当多次执行相同的查询语句时,第一次查询的结果会被缓存下来,后续的查询会直接从缓存中获取结果,而不再发送 SQL 到数据库。
- 默认开启:MyBatis 的一级缓存是默认开启的,无需进行额外的配置。
- 本地缓存:一级缓存是基于对象引用的本地缓存,它将执行的查询结果以键值对的形式保存在内存中。由于缓存是在同一个会话中共享的,因此可以有效地减少对数据库的查询操作,提高性能。
- 更新操作会使缓存失效:一级缓存是基于会话的,当执行更新(插入、更新、删除)操作时,MyBatis 会自动清空当前会话的一级缓存,以保证数据的一致性。
1. MyBatis 一级缓存的生命周期和 SqlSession 一致。
2. MyBatis 一级缓存内部设计简单,只是一个没有容量限定的 HashMap,在缓存的功能性上有所欠缺。
3. MyBatis 的一级缓存最大范围是 SqlSession 内部,有多个 SqlSession 或者分布式(并发)的环境下,数据库写操作会引起脏数据,建议设定缓存级别为 Statement。
二级缓存
在上文中提到的一级缓存中,其最大的共享范围就是一个 SqlSession 内部,如果多个 SqlSession 之间需要共享缓存,则需要使用到二级缓存。
开启二级缓存后,会使用 CachingExecutor 装饰 Executor,进入一级缓存的查询流程前,先在 CachingExecutor 进行二级缓存的查询。
二级缓存开启后,同一个 namespace 下的所有操作语句,都影响着同一个 Cache,即二级缓存被多个 SqlSession 共享,是一个全局的变量。
当开启缓存后,数据的查询执行的流程为:
二级缓存 -> 一级缓存 -> 数据库
即:
- 查询请求进入 SqlSession。
- 首先,会检查二级缓存(即全局缓存)是否存在与查询请求相匹配的缓存结果。如果缓存命中,将直接返回缓存中的数据,跳过后续步骤。
- 如果二级缓存未命中,那么会继续检查一级缓存(即本地缓存)是否存在与查询请求相匹配的缓存结果。一级缓存是针对当前 SqlSession 的缓存,如果存在缓存命中,将直接返回缓存中的数据,跳过后续步骤。
- 如果一级缓存未命中,将会向数据库发送查询语句,并从数据库中获取查询结果。
- 获取到的查询结果会同时存放到一级缓存和二级缓存中,以便下次查询时可以直接命中缓存。
1. MyBatis 的二级缓存相对于一级缓存来说,实现了 SqlSession 之间缓存数据的共享,同时粒度更加细,能够到 namespace 级别,通过 Cache 接口实现类不同的组合,对 Cache 的可控性也更强。
2. MyBatis 在多表查询时,极大可能会出现脏数据,有设计上的缺陷,安全使用二级缓存的条件比较苛刻。
3. 在分布式环境下,由于默认的 MyBatis Cache 实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将 MyBatis 的 Cache 接口实现,有一定的开发成本,直接使用Redis、Memcached 等分布式缓存可能成本更低,安全性也更高
(要将 MyBatis 的 Cache 接口实现为集中式缓存,可以使用第三方的缓存框架,如 Redis 或 Memcached)
以下是关于二级缓存的要点:
- 跨会话缓存:二级缓存是跨会话级别的缓存,它可以在不同的 SqlSession 之间共享缓存数据。当一个会话(SqlSession)执行查询操作时,查询结果会被存储在二级缓存中。其他会话在执行相同的查询时,会先检查二级缓存,如果缓存中存在相应的结果,则直接从缓存中获取,而不再发送 SQL 到数据库。
- 配置开启:要使用二级缓存,需要在 MyBatis 的配置文件中进行相应的配置。可以通过在映射文件(Mapper XML)或映射接口上添加 <cache> 元素来开启二级缓存。另外,还需要确保相应的实体类(POJO)实现了序列化接口,以便缓存的对象可以进行序列化和反序列化操作。
- 默认不开启:与一级缓存不同,二级缓存并不是默认开启的。需要手动进行配置,并且可以针对每个映射文件或接口进行单独的配置。默认情况下,二级缓存是禁用的。
- 存储位置:二级缓存的数据存储在一个单独的缓存区域中,可以是内存、磁盘或其他缓存实现。MyBatis 并不提供默认的缓存实现,而是允许使用第三方的缓存框架,如 Ehcache、Redis 等。
- 更新操作和失效:当执行更新(插入、更新、删除)操作时,会自动刷新相应的缓存数据,以保证数据的一致性。此时,二级缓存中与更新操作相关的缓存项将被标记为失效,后续查询会重新从数据库中获取最新的数据。
总结来说,二级缓存是 MyBatis 提供的一种跨会话的缓存机制,可以在不同的 SqlSession 之间共享缓存数据。它需要手动配置开启,并使用第三方缓存框架实现数据的存储。通过合理使用二级缓存,可以提高系统的性能和效率。
近日总结:焦虑还是有一定焦虑的,五月都快到中旬了......七月初距离感觉又远又近......