什么是二级缓存?

二级缓存和一级缓存的原理是一样的,第一次查询,会将数据放入缓存中,然后第二次查询则会直接去缓存中取。但是一级缓存是基于的sqlSession,而二级缓存是基于mapper文件的namespace的,也就是说多个sqlSession可以共享一个mapper中的二级缓存区域,并且如何两个mapper的namespace相同,即使两个mapper,那这两个mapper中执行sql查询到的数据也将存在相同的二级缓存区域中

  • 如上图sqlSession1在查询时会从UserMapper的二级缓存中取,如果没有则执行数据库查询操作。
  • 然后写入到二级缓存中
  • sqlSession2则执行同样的UserMapper查询时,会从UserMapper的二级缓存中取,此时的二级缓存中已经有内容了,所以就可以直接取到,不再与数据库交互。
  • sqlSession3在执行事务操作(插入、更新、删除)时,会清空UserMapper的二级缓存

1. 开启二级缓存

如何使用二级缓存:

mybatis中,一级缓存是默认开启的,但是二级缓存需要配置才可以使用

  1. 在全局配置文件sqlMapConfig.xml中加入如下代码:
    <!--开启二级缓存--> <settings> <setting name="cacheEnabled" value="true"/> </settings>
  2. 其次在哪个namespace中开启二级就在哪里配置,因为mybatis有注解和xml两种方式所以:
  • 注解

注解扩展:
//我们默认使用的是mybatis自带的二级缓存,它的实现在PerpetualCache类中,所以可以写成 @CacheNamespace(implementation = PerpetualCache.class) //如果是使用redis作为二级缓存的话,下面第二部分会讲到

  • xml

这样就开启了UserMapper的二级缓存

  1. 测试一:
    我们要根据用户id查询用户信息:

注意:将缓存的pojo实现Serializable接口,为了将缓存数据取出执行反序列化操作,因为二级缓存的存储介质多种多样,不一定只在内存中,也可能在硬盘中,如果我们要再取出这个缓存的话,就需要反序列化了。所以mybatis的pojo都去实现Serializable接口

最后执行看到打印日志:

为什么System.out.println(user1==user2)为false ?二级缓存和一级缓存不同,二级缓存缓存的不是对象,而是数据,在第二次查询时底层重新创建了一个User对象,并且把二级缓存中的数据重新封装成了对象并返回。所以user1和user2不是一个对象。

  1. 测试二:
    我们在测试二中进行一下事务操作,看看是否能清空二级缓存:

​ 增加了一个修改操作,发现执行了两个select,说明提交事务会刷新二级缓存

userCache和flushCache

还可以配置userCacheflushCache

  • userCache : 是用来设置是否禁用二级缓存的,在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包中

  1. 首先我们引入jar包
    <dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-redis</artifactId> <version>1.0.0-beta2</version> </dependency>
  2. 修改Mapper.xml文件
    //**********XML方式***********: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.lagou.mapper.IUserMapper"> //表示针对于当前的namespace开启二级缓存 <cache type="org.mybatis.caches.redis.RedisCache" /> <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接口
  1. 配置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();}}//。。。。。。。。省略部分代码  @Overridepublic void putObject(final Object key, final Object value) {execute(new RedisCallback() {@Overridepublic Object doWithRedis(Jedis jedis) {jedis.hset(id.toString().getBytes(), key.toString().getBytes(), SerializeUtil.serialize(value));return null;}});}@Overridepublic Object getObject(final Object key) {return execute(new RedisCallback() {@Overridepublic Object doWithRedis(Jedis jedis) {return SerializeUtil.unserialize(jedis.hget(id.toString().getBytes(), key.toString().getBytes()));}});}@Overridepublic Object removeObject(final Object key) {return execute(new RedisCallback() {@Overridepublic Object doWithRedis(Jedis jedis) {return jedis.hdel(id.toString(), key.toString());}});}}

  1. RedisConfig redisConfig = RedisConfigurationBuilder.getInstance().parseConfiguration();

RedisConfig中封装了默认的Redis配置信息

这个方法读取了我们配置在/resource/redis.properties这个文件
RedisConfig后构建了Jedis池

  1. 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和其他的序列化类差不多,负责对象的序列化和反序列化;

转载:https://www.cnblogs.com/isdxh/p/13963636.html

jedis使用_Mybatis的二级缓存、使用Redis做二级缓存相关推荐

  1. Spring整合Redis做数据缓存(Windows环境)

    当我们一个项目的数据量很大的时候,就需要做一些缓存机制来减轻数据库的压力,提升应用程序的性能,对于java项目来说,最常用的缓存组件有Redis.Ehcache和Memcached. Ehcache是 ...

  2. DotNetty系列六:将服务端和客户端改为Winform窗口,使用Redis做为缓存,实现用户登录,好友,群组上下线显示。

    这次改动挺大的. 1.服务端和客户端改为Winform窗口.好多细节未处理,只是实现了功能.     2.使用Redis做为缓存,版本redis-3.0.1,和RedisDesktopManager做 ...

  3. oracle缓存和Redis,说说数据缓存那点事:Redis和memcached对比

    [此为"一森咖记"公众号--第86篇文章] 本文预计阅读15分钟 [引言] 当我们为一个并发量较大的应用做数据架构时,会考虑使用缓存,意欲达到三个目标: 1. 加快用户访问速度,提 ...

  4. SSM中使用redis做中间缓存,详细注释,代码可以运行

    1.介绍 在开发中经常遇到大量的重复的,高并发的查询,此时可以使用redis缓存,将结果存入到redis中,当用户发起查询请求时,先在redis中查询结果,没有命中再去访问数据库.这样可以大大减少数据 ...

  5. redis做mysql缓存的优点_面试官:如何保障数据库和redis缓存的一致性

    随着互联网的高速发展,使用互联网产品的人也越来越多,团队不可避免得也会面对越来越复杂的高并发业务场景(如下图),比如热点视频/文章的观看(读场景),热点视频/文章的评论,点赞等(写场景). 众所周知, ...

  6. redis做数据库缓存

    只读缓存 使用只读缓存时,是先把修改写到后端数据库中,再把缓存中的数据删除.当下次访问这个数据时,会以后端数据库中的值为准,重新加载到缓存中. 这样做的优点是,数据库和缓存可以保证完全一致,并且缓存中 ...

  7. spring-boot-route(十二)整合redis做为缓存

    redis简介 redis作为一种非关系型数据库,读写非常快,应用十分广泛,它采用key-value的形式存储数据,value常用的五大数据类型有string(字符串),list(链表),set(集合 ...

  8. 怎样将redis写入mysql_使用redis做mysql缓存

    应用Redis实现数据的读写,同时利用队列处理器定时将数据写入mysql. 同时要注意避免冲突,在redis启动时去mysql读取所有表键值存入redis中,往redis写数据时,对redis主键自增 ...

  9. redis写入mysql 使用redis做mysql缓存

    应用Redis实现数据的读写,同时利用队列处理器定时将数据写入mysql. 同时要注意避免冲突,在redis启动时去mysql读取所有表键值存入redis中,往redis写数据时,对redis主键自增 ...

最新文章

  1. nginx配置文件说明
  2. Linux多线程实践(一)线程基本概念和理论
  3. 好的视频编解码网址和博文地址
  4. silverlight 跨域文件位置
  5. 如何使用SSL pinning来使你的iOS APP更加安全
  6. 递归走迷宫java_在Java中的迷宫递归回溯
  7. vaadin_Vaadin应用程序中的EJB查找
  8. 1050 螺旋矩阵 (25 分
  9. 浙江理工大学计算机组成原理试卷,浙江理工大学计算机组成原理设计试题.doc...
  10. Leetcode--343. 整数拆分
  11. hive check in checkDiagnosticMessage found error
  12. 【Python3网络爬虫开发实战】3.1.1-发送请求
  13. ibatis调用mysql带OUT类型参数的存储过程并获取返回值
  14. wait()和sleep()区别(常见面试题)
  15. Hello Qt(十四)——QT绘图系统
  16. MP3固件升级(转)
  17. 普通二本毕业八年,京东就职两年、百度三年,分享大厂心得
  18. unity3d 建立3D中文立体字
  19. #进击的贪吃蛇-----将贪吃蛇,飞机大战,坦克大战功能融合形成新的游戏
  20. 【电脑技巧】设置电脑永不休眠

热门文章

  1. linux socket读写函数,Linux网络编程入门
  2. lucky前面加a还是an_微信昵称前加个“A” 不是微商就是销售?看看这个“A”的含义!...
  3. 从零开始搭建spring-cloud(4) ----Hystrix
  4. 全文2500字 详解Pandas与Lambda结合进行高效数据分析
  5. 终于可以摆脱 Pipenv 这坑货了
  6. 绝了!Pandas绘图功能
  7. 学了Python就能上手业务?你还差得远!
  8. 几个名校学霸、大厂前辈的原创公众号
  9. 基于梯度下降法的——线性回归拟合
  10. php怎么调用css格式化,css文件格式化脚本的方法