首页新闻博问专区闪存云上钜惠 我的博客我的园子账号设置退出登录注册登录DXH’s Blog 瓶颈是对所有人的,既然绕不过去,那就把它做到极致,就成了门槛,成了核心竞争力。 首页管理随笔 - 56 文章 - 1 评论 - 16 Mybatis的二级缓存、使用Redis做二级缓存 目录什么是二级缓存?1. 开启二级缓存如何使用二级缓存:userCache和flushCache2. 使用Redis实现二级缓存如何使用3. Redis二级缓存源码分析什么是二级缓存?二级缓存和一级缓存的原理是一样的,第一次查询,会将数据放入缓存中,然后第二次查询则会直接去缓存中取。但是一级缓存是基于的sqlSession,而二级缓存是基于mapper文件的namespace的,也就是说多个sqlSession可以共享一个mapper中的二级缓存区域,并且如何两个mapper的namespace相同,即使两个mapper,那这两个mapper中执行sql查询到的数据也将存在相同的二级缓存区域中如上图sqlSession1在查询时会从UserMapper的二级缓存中取,如果没有则执行数据库查询操作。然后写入到二级缓存中sqlSession2则执行同样的UserMapper查询时,会从UserMapper的二级缓存中取,此时的二级缓存中已经有内容了,所以就可以直接取到,不再与数据库交互。sqlSession3在执行事务操作(插入、更新、删除)时,会清空UserMapper的二级缓存1. 开启二级缓存如何使用二级缓存:mybatis中,一级缓存是默认开启的,但是二级缓存需要配置才可以使用在全局配置文件sqlMapConfig.xml中加入如下代码:
其次在哪个namespace中开启二级就在哪里配置,因为mybatis有注解和xml两种方式所以:注解
注解扩展://我们默认使用的是mybatis自带的二级缓存,它的实现在PerpetualCache类中,所以可以写成
@CacheNamespace(implementation = PerpetualCache.class)
//如果是使用redis作为二级缓存的话,下面第二部分会讲到
xml
这样就开启了UserMapper的二级缓存测试一:我们要根据用户id查询用户信息:注意:将缓存的pojo实现Serializable接口,为了将缓存数据取出执行反序列化操作,因为二级缓存的存储介质多种多样,不一定只在内存中,也可能在硬盘中,如果我们要再取出这个缓存的话,就需要反序列化了。所以mybatis的pojo都去实现Serializable接口
最后执行看到打印日志:
为什么System.out.println(user1==user2)为false ?二级缓存和一级缓存不同,二级缓存缓存的不是对象,而是数据,在第二次查询时底层重新创建了一个User对象,并且把二级缓存中的数据重新封装成了对象并返回。所以user1和user2不是一个对象。测试二:
我们在测试二中进行一下事务操作,看看是否能清空二级缓存:
增加了一个修改操作,发现执行了两个select,说明提交事务会刷新二级缓存userCache和flushCache还可以配置userCache和flushCacheuserCache : 是用来设置是否禁用二级缓存的,在statement中设置可以禁用当前select语句的二级缓存,即每次查询都会发出sql。默认情况为true.
flushCache : 在mapper的同一个namespace中,如果有其它的增删改操作后需要刷新缓存,如果部执行刷新缓存会出现脏读。
设置statement配置中的flushCache=“true”,即刷新缓存,如果改成false则不会刷新,有可能出现脏读。所以一般情况下没必要改
Mybatis二级缓存和一级缓存一样也是使用到了org.apache.ibatis.cache.impl.PerpetualCache这个类是mybatis的默认缓存类,同时,想要自定义缓存必须实现cache接口2. 使用Redis实现二级缓存Mybatis自带的二级缓存是有缺点的,就是这个缓存是单服务器进行工作的,无法实现分布式缓存。
所以为了解决这个问题,必须找一个分布式缓存专门存放缓存数据。
如何使用mybatis提供了一个针对cache接口的redis实现类,在mybatis-redis包中首先我们引入jar包
org.mybatis.caches
mybatis-redis
1.0.0-beta2
修改Mapper.xml文件//XML方式*:
<select id="findAll" resultType="com.lagou.pojo.User" useCache="true">
select * from user
</select>
//注解方式***
@CacheNamespace(implementation = RedisCache .class)
public interface UserMapper {
//根据id查询用户 注解使用
@Select(“select * from user where id=#{id}”)
public User findById(Integer id);
这个类同样实现了Cache接口
配置redis的配置文件redis.host=localhost
redis.port=6379
redis.connectionTimeout=5000
redis.password=
redis.database=0
测试方法同自带的二级缓存一样。3. Redis二级缓存源码分析RedisCache和Mybatis二级缓存的方案都差不多,无非是实现Cache接口,并使用jedis操作缓存,不过在设计细节上有点区别。
我们带着问题分析源码:在RedisCache类中如何向redis中进行缓存值的存取 ?使用了哪种数据结构 ?package org.mybatis.caches.redis;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import org.apache.ibatis.cache.Cache;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
//首先其实现了Cache接口,被mybatis初始化的时候的CacheBuilder创建
//创建方式就是调用了下面的有参构造
public final class RedisCache implements Cache {
private final ReadWriteLock readWriteLock = new DummyReadWriteLock();
private String id;
private static JedisPool pool;
//有参构造
public RedisCache(final String id) {
if (id == null) {
throw new IllegalArgumentException(“Cache instances require an ID”);
}
this.id = id;
//RedisConfigurationBuilder调用parseConfiguration()方法创建RedisConfig对象
RedisConfig redisConfig = RedisConfigurationBuilder.getInstance().parseConfiguration();
//构建Jedis池
pool = new JedisPool(redisConfig, redisConfig.getHost(), redisConfig.getPort(),
redisConfig.getConnectionTimeout(), redisConfig.getSoTimeout(), redisConfig.getPassword(),
redisConfig.getDatabase(), redisConfig.getClientName());
}
//模板方法,下面的putObject和getObject、removeObject都会用到这个方法
private Object execute(RedisCallback callback) {
Jedis jedis = pool.getResource();
try {
return callback.doWithRedis(jedis);
} finally {
jedis.close();
}
}
//。。。。。。。。省略部分代码
@Override
public void putObject(final Object key, final Object value) {
execute(new RedisCallback() {
@Override
public Object doWithRedis(Jedis jedis) {
jedis.hset(id.toString().getBytes(), key.toString().getBytes(), SerializeUtil.serialize(value));
return null;
}
});
}
@Override
public Object getObject(final Object key) {
return execute(new RedisCallback() {
@Override
public Object doWithRedis(Jedis jedis) {
return SerializeUtil.unserialize(jedis.hget(id.toString().getBytes(), key.toString().getBytes()));
}
});
}
@Override
public Object removeObject(final Object key) {
return execute(new RedisCallback() {
@Override
public Object doWithRedis(Jedis jedis) {
return jedis.hdel(id.toString(), key.toString());
}
});
}
}
RedisConfig redisConfig = RedisConfigurationBuilder.getInstance().parseConfiguration();
RedisConfig中封装了默认的Redis配置信息
这个方法读取了我们配置在/resource/redis.properties这个文件
RedisConfig后构建了Jedis池put方法private Object execute(RedisCallback callback) {
Jedis jedis = pool.getResource();
try {
return callback.doWithRedis(jedis);
} finally {
jedis.close();
}
}
public void putObject(final Object key, final Object value) {
execute(new RedisCallback() {
@Override
public Object doWithRedis(Jedis jedis) {
jedis.hset(id.toString().getBytes(), key.toString().getBytes(), SerializeUtil.serialize(value));
return null;
}
});
}
我们可以看到,put方法调用了模板方法得到 一个jedis链接,然后调用doWithRedis()方法jedis.hset(id.toString().getBytes(), key.toString().getBytes(), SerializeUtil.serialize(value));
可以很清楚的看到,mybatis-redis在存储数据的时候,是使用的hash结构,把cache的id作为这个hash的key (cache的id在mybatis中就是mapper的namespace);这个mapper中的查询缓存数据作为 hash的field,需要缓存的内容直接使用SerializeUtil存储,SerializeUtil和其他的序列化类差不多,负责对象的序列化和反序列化; 分类: Java 标签: Mybatis好文要顶 关注我 收藏该文 邓晓晖
关注 - 3
粉丝 - 4 +加关注 0 0
« 上一篇: Mybatis的缓存——一级缓存和源码分析 posted @ 2020-11-12 14:13 邓晓晖 阅读(199) 评论(0) 编辑 收藏
刷新评论刷新页面返回顶部
发表评论 【福利】注册AWS账号,立享12个月免费套餐 编辑预览 7693b08a-a8f6-49f3-f45a-08d88556cc23 Markdown 帮助自动补全 不改了退出 订阅评论 [Ctrl+Enter快捷键提交]
首页 新闻 博问 专区 闪存 班级 【推荐】News: 大型组态、工控、仿真、CADGIS 50万行VC++源码免费下载
【推荐】博客园 & 陌上花开HIMMR 给单身的程序员小哥哥助力脱单啦~
【推荐】博客园 & 示说网联合策划,AI实战系列公开课第二期
【推荐】了不起的开发者,挡不住的华为,园子里的品牌专区
【推荐】未知数的距离,毫秒间的传递,声网与你实时互动
【福利】AWS携手博客园为开发者送免费套餐与抵扣券
【推荐】 阿里云折扣价格返场,错过再等一年
最新 IT 新闻:
· 拼多多财报电话会议实录:多多买菜并非社区拼团购物
· 久经沙场的战士对决:张小龙大战张一鸣
· 双十一吐槽:异化与集体无意识
· 小鹏汽车上市后首份财报:营收19亿 首度实现毛利转正
· 腾讯第三季度总收入为1254.47亿元 同比增长29%
» 更多新闻…
最新随笔1.Mybatis的二级缓存、使用Redis做二级缓存 2.Mybatis的缓存——一级缓存和源码分析 3.Mybatis复杂映射 4.Mybatis动态SQL 5.自己动手写一个持久层框架 6.最长公共子串算法(Longest Common Substring) 7.【SpringCloud】08.客户端负载均衡器:Ribbon 8.【SpringCloud】07.应用间的通信 9.【SpringCloud】06.Eureka 总结 10.【SpringCloud】05.Eureka的高可用 我的标签SpringBoot(17) SpringCloud(8) Mybatis(5) Nginx(2) Git(1) MySQL(1) 算法(1) 随笔分类 (36) Java(32) MySql(1) Nginx(2) 算法(1) 随笔档案 (56) 2020年11月(5) 2020年8月(26) 2018年9月(2) 2018年3月(1) 2018年2月(2) 2017年10月(1) 2017年7月(3) 2017年5月(5) 2016年9月(1) 2016年7月(1) 2016年6月(2) 2016年5月(1) 2014年4月(1) 2014年3月(3) 2013年4月(2) 链接 GitHub 最新评论1. Re:Mybatis动态SQL@锦槐
已修改,感谢指出–IsDxh2. Re:Mybatis动态SQL什么鬼,第二段和第三段代码不是一模一样吗?–锦槐3. Re:Mybatis动态SQL@君君的喵爸 : )…–IsDxh4. Re:Mybatis动态SQL阅–君君的喵爸5. Re:自己动手写一个持久层框架@苏小演 不是大佬,我也只是跟着训练营的视频学,然后自己总结的笔记…–IsDxh
Copyright © 2020 邓晓晖
Powered by .NET 5.0.0 on Kubernetes