mybatis缓存

发布时间:2026/7/5 15:17:08

mybatis缓存 mysql缓存mysql慢查询在 MySQL 中开启慢查询日志是一个非常有用的功能它可以帮助你识别出运行缓慢的查询从而优化数据库性能。以下是开启慢查询日志的步骤1. 配置慢查询日志需要在 MySQL 的配置文件通常是my.cnf或my.ini具体位置取决于你的操作系统和 MySQL 安装方式中设置慢查询日志的相关参数。slow_query_log 1 slow_query_log_file D:\\Development\\mysql-8.0.30-winx64\\logfile.log long_query_time 2 log_queries_not_using_indexes 1 min_examined_row_limit 100 # 命令暂时开启或关闭慢查询 重启mysql失效不推荐 # SET global.slow_query_log OFF; # SET global.slow_query_log ON;low_query_log 1 1启用慢查询日志 0关闭慢查询。slow_query_log_file指定慢查询日志文件的路径。确保 MySQL 有权限写入此文件。 long_query_time设置慢查询的阈值秒例如设置为 2 表示执行时间超过2秒的查询都会被记录。log_queries_not_using_indexes记录没有使用索引的查询。 min_examined_row_limit只有当查询检查的行数超过此值时才会被记录。2. 重启 MySQL 服务修改配置文件后需要重启 MySQL 服务以使更改生效。3. 验证慢查询日志是否开启SHOW VARIABLES LIKE slow_query_log%;mybatis缓存介绍一、 一级缓存Local Cache1.1 概念作用域SqlSession 级别默认开启不能关闭。原理一次查询结果会放到当前 SqlSession 的 HashMap 中后续相同 SQL相同参数、相同 StatementId会直接从缓存取而不会再查数据库。失效场景SqlSession 关闭。SqlSession 执行了 INSERT、UPDATE、DELETE 操作缓存会清空。查询参数不同或 SQL 不同。手动调用 clearCache()。 一级缓存属于 会话缓存在 Spring Boot 中由于每次调用 Mapper 方法默认都会使用新的 SqlSession所以在事务范围内能体现。二、二级缓存Global Cache2.1 概念作用域SqlSessionFactory 级别即 Mapper namespace 级别可被多个 SqlSession 共享。默认情况全局关闭需要手动配置。原理查询结果会被序列化后存储在二级缓存区域内存或 Redis 等不同 SqlSession 之间共享缓存。失效场景执行增删改操作时清空当前 namespace 下的缓存。缓存达到最大 size 或超时。 二级缓存适合读多写少的场景。分布式系统中常用 Redis 代替内存缓存。————————————————版权声明本文为CSDN博主「你我约定有三」的原创文章遵循CC 4.0 BY-SA版权协议转载请附上原文出处链接及本声明。原文链接https://blog.csdn.net/m0_57921272/article/details/150548051一级缓存示例Spring Boot 默认行为MyBatis一级缓存也称为查询缓存是在SqlSession中保存了一个HashMap key为SQL语句value为查询出的结果当一个查询操作会判断查询的语句是否在HashMap中存在如果存在则直接取出缓存的查询结果如果不存在就继续查询数据库然后将结果缓存起来。一级缓存又被称为 SqlSession 级别的缓存会话缓存。一级缓存减少与数据库的交互次数从而降低数据库的压力进而提高响应速度。MyBatis一级缓存默认是自动开启的一级缓存是针对于单个SqlSession的不同的SqlSession之间缓存数据HashMap是无法相互影响的。当insert、update、delete操作时都会清除一级缓存导致一级缓存失效。UserMapper.javaMapper public interface UserMapper { User findById(Integer id); }UserMapper.xmlmapper namespacecom.example.mapper.UserMapper select idfindById parameterTypeint resultTypecom.example.entity.User SELECT * FROM user WHERE id #{id} /select /mapperUser 实体类Data public class User implements Serializable { private Integer id; private String username; private Integer age; }测试SpringBootTest class MybatisCacheTest { Autowired private UserMapper userMapper; Autowired private SqlSessionFactory sqlSessionFactory; Test void testFirstLevelCache() { // 获取 SqlSession手动控制事务保证在同一个 SqlSession 中 try (SqlSession session sqlSessionFactory.openSession()) { UserMapper mapper session.getMapper(UserMapper.class); // 第一次查询 - 查数据库 User u1 mapper.findById(1); System.out.println(u1); // 第二次查询 - 命中一级缓存不走数据库 User u2 mapper.findById(1); System.out.println(u2); // 执行插入 - 会清空缓存 mapper.insert(new User(null, Tom, 20)); // 更新会使一级缓存失效 userMapper.updateUser(new User(5L, monday, new Date())); // 删除会使一级缓存失效 //代码略 // 第三次查询 - 重新查数据库 User u3 mapper.findById(1); System.out.println(u3); } } }在同一个 SqlSession内多次执行相同查询会命中一级缓存。二级缓存mapper二级缓存的原理和一级缓存原理一样第一次查询会将数据放入缓存中然后第二次查询则会直接去缓存中取。但是一级缓存是基于 sqlSession 的而 二级缓存是基于 mapper文件的namespace的也就是说多个sqlSession可以共享一个mapper中的二级缓存区域并且如果两个mapper的namespace相同即使是两个mapper那么这两个mapper中执行sql查询到的数据也将存在相同的二级缓存区域中。二级缓存底层也是使用的HashMap来保存结果的。配置application.yml# 开启MyBatis二级缓存 mybatis.configuration.cache-enabledtrue在Mapper.xml中配置cache标签表示开启当前Mapper对应的二级缓存。也可以通过type属性指定缓存实现类如果不配置mybatis默认使用PerpetualCache作为缓存实现类也可以自定义缓存实现类。cache typeorg.apache.ibatis.cache.impl.PerpetualCache/cache cache typeorg.mybatis.caches.ehcache.EhcacheCache/cache?xml version1.0 encodingUTF-8 ? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.example.mybatis.mapper.UserMapper !-- 开启二级缓存 -- cache evictionLRU flushInterval60000 size512 readOnlyfalse/ select idgetUser resultTypecom.example.mybatis.entity.User SELECT * FROM user where id #{id} /select /mapper实体类需要实现Serializable接口。Data NoArgsConstructor AllArgsConstructor public class User implements Serializable { private Long id; private String username; private Date createTime; }测试SpringBootTest class SpringbootMybatisApplicationTests { Autowired private SqlSessionFactory sessionFactory; Test void testMyBatisCache() { SqlSession sqlSession1 sessionFactory.openSession(); UserMapper userMapper1 sqlSession1.getMapper(UserMapper.class); // 第一次查询发出sql语句并将查询的结果放入到二级缓存中 User user1 userMapper1.getUser(1L); System.out.println(user1); sqlSession1.close(); // 一级缓存失效但二级缓存仍保存 // 使用sqlSession2查询没有发送sql语句 SqlSession sqlSession2 sessionFactory.openSession(); UserMapper userMapper2 sqlSession2.getMapper(UserMapper.class); User user2 userMapper2.getUser(1L); System.out.println(user2); sqlSession2.close(); } }useCache表示是否使用二级缓存默认是true使用二级缓存可以配置成false禁止二级缓存每次都查询数据库。在mapper的同一个namespace中如果有其它insert、update、delete操作数据后需要刷新缓存如果不执行刷新缓存会出现脏读。flushCache”true” 属性表示即刷新缓存如果改成false则不会刷新我这里的mybatis版本测试的flushCache默认值并不是true而是false必须显式配置该参数为true才会刷新缓存select idgetUser useCachefalse flushCachetrue resultTypecom.example.mybatis.entity.User SELECT * FROM user where id #{id} /select测试-脏读SpringBootTest class SpringbootMybatisApplicationTests { Autowired private SqlSessionFactory sessionFactory; Test void testMyBatisCache() { // 第一次查询写入到二级缓存 SqlSession sqlSession1 sessionFactory.openSession(); UserMapper userMapper1 sqlSession1.getMapper(UserMapper.class); User user1 userMapper1.getUser(5L); System.out.println(user1); sqlSession1.close(); // 中间更新缓存被清空 SqlSession sqlSession2 sessionFactory.openSession(); UserMapper userMapper2 sqlSession2.getMapper(UserMapper.class); userMapper2.updateUser(new User(5L, abc, new Date())); sqlSession2.close(); // 还会继续发SQL查询, 注意必须显式配置flushCachetrue 否则数据不对 SqlSession sqlSession3 sessionFactory.openSession(); UserMapper userMapper3 sqlSession3.getMapper(UserMapper.class); User user3 userMapper3.getUser(5L); System.out.println(user3); sqlSession3.close(); } }分布式缓存PerpetualCache只能在单台服务器上有效如果在多态服务器上就没有效果了此时我们可以使用分布式缓存来实现在所有服务器上二级缓存都生效常用的分布式缓存有redis、ehcache、memcache等。那么究竟应该不应该使用二级缓存呢先来看一下二级缓存的注意事项缓存是以namespace为单位的不同namespace下的操作互不影响。insert,update,delete操作会清空所在namespace下的全部缓存, 不能做到部分删除。通常使用MyBatis Generator生成的代码中都是各个表独立的每个表都有自己的namespace。二级缓存默认是单击模式的如果要支持分布式还要配置redis而且二级缓存不支持单条数据的更新一旦发生insert/update/delete操作会一次性清空缓存还不如直接使用redis作为缓存服务器来的简单所以二级缓存并不推荐使用。springboot集成redis详见......分布式缓存配置UserMapper.xml启用二级缓存redismapper namespacecom.example.mapper.UserMapper cache typeorg.mybatis.caches.redis.RedisCache/ select idfindById parameterTypeint resultTypecom.example.entity.User SELECT * FROM user WHERE id #{id} /select /mapper测试SpringBootTest public class MybatisRedisCacheTest { Autowired private SqlSessionFactory sqlSessionFactory; Test public void testRedisCache() { try (SqlSession session1 sqlSessionFactory.openSession(); SqlSession session2 sqlSessionFactory.openSession()) { UserMapper mapper1 session1.getMapper(UserMapper.class); System.out.println(mapper1.findById(1)); session1.close(); // 数据进入 Redis UserMapper mapper2 session2.getMapper(UserMapper.class); System.out.println(mapper2.findById(1)); // 直接从 Redis 命中 } } }本地 JVM 缓存和Redis的对比使用本地 JVM 缓存和Redis的对比1 原生的二级缓存没有 Redis 的时候存储位置二级缓存是基于 Mappernamespace级别 的存储在 当前应用 JVM 的内存 里默认实现是 PerpetualCache HashMap。作用范围同一个 Mapper 下多个 SqlSession 之间可以共享缓存。生命周期缓存随着应用 JVM 存在当应用重启时缓存会消失。特点只能在 单机应用 下使用不能跨服务共享数据容量受限于 JVM 内存适合一些 读多写少的小型项目。举例应用 A 部署在服务器 X查询了 UserMapper.findById(1)MyBatis 会把数据放到本地 JVM 内存的二级缓存中。下次再查 findById(1)即使是新的 SqlSession也可以直接从本地缓存拿到结果但是如果还有一个应用 B另一个服务实例它就完全拿不到这个缓存。所以原生二级缓存本质上是“单机的 Mapper 级缓存”。4.3.2 结合 Redis 的二级缓存分布式存储位置MyBatis 不再把缓存放在 JVM 内存而是存放到 Redis 这样的分布式缓存 里。作用范围不同服务实例多个 JVM之间都能共享缓存。生命周期缓存由 Redis 管理可以持久化或设置 TTL过期时间。特点支持 多机共享缓存解决了单机缓存不一致的问题Redis 提供了更大的存储容量和高可用机制适合 分布式集群环境。举例应用 A 部署在服务器 X应用 B 部署在服务器 Y。A 查询 UserMapper.findById(1)结果会缓存到 RedisB 查询 UserMapper.findById(1)即使它的 JVM 里没缓存也能从 Redis 里拿到数据。所以结合 Redis 后二级缓存从“单机 JVM 缓存”变成了“分布式共享缓存”。

相关新闻