请说说mybatis的一级缓存和二级缓存

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 请说说mybatis的一级缓存和二级缓存


image.png

在面试过程中经常会问请说说mybatis的一级缓存和二级缓存,下文将带你实现如何使用。如果不敢兴趣请直接看结论。

1.理解mybatis缓存

Mybatis的一级缓存和二级缓存是Mybatis自带的。

目的:将sql的查询结果存放于缓存内,在接下来再次查询时,会直接从缓存中取出而不是从数据库里面获取。这样会提升查询速度,但是会产生脏读。

注意:一级缓存是自动开启的,二级缓存是需要手动开启的。所以开启二级缓存需要慎重考虑。(使用spring boot环境)

2.一级缓存

一级缓存是自动开启的。是sqlSession级别的缓存。

个人理解就是同一个事务之内,调取两次相同sql。会读取一级缓存。

我们只要满足以下条件就可以使用一级缓存:

  • 1.sqlSession级别的缓存。
  • 2.使用@Transactional。(必须有此注解)

此处只贴上service的代码,其余的就是普通的mvc查询代码很简单。

       @Transactional
  public City selectCity(int id) {
      System.out.println("第一次查询");
      redisDao.selectAnno(id);          //调用dao层查询方法
      System.out.println("第二次查询");
    return redisDao.selectAnno(id);  //调用dao层查询方法
  }

这样我们就可以看到查询sql。经过测试发现两次相同的查询只会打印出一条sql。

spring boot中使用开启查看sql的功能,需要指定dao层的包的位置。

logging.level.com.example.redis.dao=debug

3.二级缓存

1.什么是二级缓存

二级缓存是mapper级别的,只要是对一个mapper调用就可以使用二级缓存。

2.查询顺序

在Mybatis中查询的顺序是:二级缓存----一级缓存-------数据库。

3.使用redis作二级缓存

首先连接redis,实现Cache 接口,实现接口中的方法,从redis取值和存值。这里使用了RedisTemplate 。

public class MybatisRedisCache implements Cache {
  private static Logger log = LoggerFactory.getLogger(MybatisRedisCache.class);
  private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
  private RedisTemplate redisTemplate;
  private String id;
  private static final long EXPIRE_TIME_IN_MINUTES = 30; // redis过期时间
  public MybatisRedisCache(String id) {
    if (id == null) {
      throw new IllegalArgumentException("Cache instances require an ID");
    }
    this.id = id;
  }
  @Override
  public void clear() {
    RedisTemplate redisTemplate = getRedisTemplate();
    redisTemplate.execute((RedisCallback) connection -> {
      connection.flushDb();
      return null;
    });
    log.debug("Clear all the cached query result from redis");
  }
  @Override
  public String getId() {
    // TODO Auto-generated method stub
    return this.id;
  }
  @Override
  public Object getObject(Object key) {
    RedisTemplate redisTemplate = getRedisTemplate();
    //ValueOperations opsForValue = redisTemplate.opsForValue();
    log.debug("Get cached query result from redis");
    //return opsForValue.get(key);
    Object obj = redisTemplate.opsForValue().get(key.toString());
        return obj;
  }
  @Override
  public ReadWriteLock getReadWriteLock() {
    return readWriteLock;
  }
  @Override
  public int getSize() {
    return 0;
  }
  @Override
  @SuppressWarnings("uncheck")
  public void putObject(Object key, Object value) {
    //RedisTemplate redisTemplate = getRedisTemplate();
    //ValueOperations opsForValue = redisTemplate.opsForValue();
    //opsForValue.set(key, value, EXPIRE_TIME_IN_MINUTES, TimeUnit.MINUTES);
        redisTemplate.opsForValue().set(key.toString(), value, 2, TimeUnit.DAYS);
    log.debug("Put query result to redis");
  }
  @Override
  public Object removeObject(Object key) {
    RedisTemplate redisTemplate = getRedisTemplate();
    redisTemplate.delete(key);
    log.debug("Remove cached query result from redis");
    return null;
  }
  private RedisTemplate getRedisTemplate() {
    if (redisTemplate == null) {
      redisTemplate = ApplicationContextHolder.getBean("redisTemplate");
    }
    return redisTemplate;
  }
}

但是RedisTemplate 不是spring提供的,不能用spring的ioc进行依赖注入,所以需要我们手写来注册RedisTemplate 。

@Component
public class ApplicationContextHolder implements ApplicationContextAware {
  private static ApplicationContext applicationContext;
  @Override
  public void setApplicationContext(ApplicationContext ctx) throws BeansException {
    applicationContext = ctx;
  }
  /**
   * Get application context from everywhere
   *
   * @return
   */
  public static ApplicationContext getApplicationContext() {
    return applicationContext;
  }
  /**
   * Get bean by class
   *
   * @param clazz
   * @param <T>
   * @return
   */
  public static <T> T getBean(Class<T> clazz) {
    return applicationContext.getBean(clazz);
  }
  /**
   * Get bean by class name
   *
   * @param name
   * @param <T>
   * @return
   */
  @SuppressWarnings("unchecked")
  public static <T> T getBean(String name) {
    return (T) applicationContext.getBean(name);
  }              
}

然后在mapper文件中开启二级缓存。

<mapper namespace="com.example.redis.dao.RedisDao">
<!-- 开启二级缓存 -->
<cache type="com.example.redis.cache.MybatisRedisCache"></cache> 
  <select id="selectAnno" resultType="com.example.redis.entity.City">
    select * from city 
    <if test="id != null">where id = #{id}</if>
  </select>
  <insert id="insertIn">
    INSERT INTO `mysql`.`city` (`id`,
    `provinceId`, `cityName`, `description`) VALUES ('1', '1', 'aa', 'aa')
  </insert>
</mapper>

最后需要序列化,要不会在redis存放乱码。

@Configuration
public class RedisConfig {
  @Autowired
  private RedisTemplate redisTemplate;
  @Bean
  public RedisTemplate redisTemplateInit() {
    Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
    // 设置序列化Key的实例化对象
    redisTemplate.setKeySerializer(new StringRedisSerializer());
    // 设置序列化Value的实例化对象
    redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    redisTemplate.setHashKeySerializer(jackson2JsonRedisSerializer);
    redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
    return redisTemplate;
  }
}

然后我们测试,在不同的sqlsession中执行同一条sql时,只有第一次会打印出sql。我们在redis中会发现,执行一次sql后会在redis存储本次的查询结果。

4.结论

一级缓存:同一个事务之内,调取两次相同sql。会读取一级缓存。

二级缓存:mapper级别的,只要是对一个mapper调用就可以使用二级缓存。


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
29天前
|
缓存 Java 数据库连接
MyBatis三级缓存实战:高级缓存策略的实现与应用
MyBatis三级缓存实战:高级缓存策略的实现与应用
36 0
MyBatis三级缓存实战:高级缓存策略的实现与应用
|
29天前
|
XML 缓存 Java
MyBatis二级缓存解密:深入探究缓存机制与应用场景
MyBatis二级缓存解密:深入探究缓存机制与应用场景
67 2
MyBatis二级缓存解密:深入探究缓存机制与应用场景
|
29天前
|
存储 缓存 Java
探秘MyBatis缓存原理:Cache接口与实现类源码分析
探秘MyBatis缓存原理:Cache接口与实现类源码分析
38 2
探秘MyBatis缓存原理:Cache接口与实现类源码分析
|
2月前
|
存储 缓存 Java
什么!?实战项目竟然撞到阿里面试的原题!???关于MyBatis Plus的缓存机制
什么!?实战项目竟然撞到阿里面试的原题!???关于MyBatis Plus的缓存机制
|
2月前
|
缓存 Java 数据库连接
mybatis 数据库缓存的原理
MyBatis 是一个流行的 Java 持久层框架,它封装了 JDBC,使数据库交互变得更简单、直观。MyBatis 支持两级缓存:一级缓存(Local Cache)和二级缓存(Global Cache),通过这两级缓存可以有效地减少数据库的访问次数,提高应用性能。
285 1
|
2月前
|
存储 缓存 Java
【MyBaits】4、延迟加载、MyBatis 的缓存
【MyBaits】4、延迟加载、MyBatis 的缓存
23 0
|
3月前
|
SQL 缓存 Java
mybatis缓存详解
mybatis缓存详解
25 0
|
4月前
|
缓存 Java 数据库连接
mybatis的缓存内容(下)
mybatis的缓存内容
30 0
|
4月前
|
SQL 缓存 Java
mybatis的缓存内容(上)
mybatis的缓存内容
31 0
|
4月前
|
缓存 Java 数据库连接