MyBatis二级缓存Cache Hit Ratio始终等于0

MyBatis二级缓存Cache Hit Ratio始终等于0

问题描述

在MyBatis中,不同SqlSession作用域中开启了两个相同的查询操作。但是在控制台的输出中一直显示没有命中缓存,持续进行SQL查询操作。

代码如下:

public class DaoTest {
    private static final Logger LOGGER = Logger.getLogger(DaoTest.class);

    private SqlSessionFactory getFactory() throws IOException {
        InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(stream);
        return factory;
    }

    @Test
    public void testGlobalCache() throws IOException {
        SqlSession sess1 = getFactory().openSession();
        SqlSession sess2 = getFactory().openSession();
        SqlSession sess3 = getFactory().openSession();

       try {
            EmployeeMapper mapper1 = sess1.getMapper(EmployeeMapper.class);
            Employee emp1 = mapper1.getEmpByID(1);
            sess1.close();

            EmployeeMapper mapper2 = sess2.getMapper(EmployeeMapper.class);
            Employee emp2 = mapper2.getEmpByID(1);
            sess2.close();

            EmployeeMapper mapper3 = sess3.getMapper(EmployeeMapper.class);
            mapper3.insertEmp(new Employee(null, "XX", "F", "XXX"));
            Employee emp3 = mapper3.getEmpByID(1);
            sess3.close();


            LOGGER.info("emp1 == emp2: " + String.valueOf(emp1 == emp2));
            LOGGER.info("emp1 == emp3: " + String.valueOf(emp1 == emp3));
            LOGGER.info("emp2 == emp3: " + String.valueOf(emp2 == emp3));
        } finally {
        
        }
    }
}

控制台输出:

[DEBUG] 2020-03-22 00:57:23,381 method:org.apache.ibatis.cache.decorators.LoggingCache.getObject(LoggingCache.java:60)
Cache Hit Ratio [xx.xxx.xxx.dao.EmployeeMapper]: 0.0
[DEBUG] 2020-03-22 00:57:23,837 method:org.apache.ibatis.cache.decorators.LoggingCache.getObject(LoggingCache.java:60)
Cache Hit Ratio [xx.xxx.xxx.dao.EmployeeMapper]: 0.0
[DEBUG] 2020-03-22 00:57:23,932 method:org.apache.ibatis.cache.decorators.LoggingCache.getObject(LoggingCache.java:60)
Cache Hit Ratio [xx.xxx.xxx.dao.EmployeeMapper]: 0.0


[INFO ] 2020-03-22 00:57:23,961 method:xx.xxx.xxx.DaoTest.testGlobalCache(DaoTest.java:86)
emp1 == emp2: false
[INFO ] 2020-03-22 00:57:23,961 method:xx.xxx.xxx.DaoTest.testGlobalCache(DaoTest.java:87)
emp1 == emp3: false
[INFO ] 2020-03-22 00:57:23,961 method:xx.xxx.xxx.DaoTest.testGlobalCache(DaoTest.java:88)
emp2 == emp3: false 

问题排查

  1. 是否在mybatis-conifg.xml中开启了全局缓存的设置。
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!--全局缓存:开启-->
        <setting name="cacheEnabled" value="true"/>
    </settings>

  1. 是否在mapper.xml文件中打开了cache设置。
    <cache eviction="LRU" flushInterval="600000" size="1024" readOnly="true"/>
    <!--<cache/>-->
    <!--
    eviction:       缓存的回收策略,包括FIFO/LRU/SOFT/WEAK,默认是LRU
    flushInterval:  缓存清空间隔,以毫秒为单位
    readOnly:      是否只读
        true        只读,mybatis从缓存中获取的数据不会修改
                    不安全,速度快
        false       非只读,mybatis通过序列化&反序列化的方式克隆进行数据获取
                    安全,速度慢
    size:           缓存多少元素
    type:           自定义缓存全类名(org.apache.ibatis.cache.Cache)
    -->
  1. Bean对象是否可以序列化
public class Employee implements Serializable{
    private static final long serialVersionUID = 1L;

    private Integer id;
    private String name;
    private String gender;
    private String email;
}

上述步骤才可以打开二级缓存,现在在控制台中已经有Cache Hit Ratio 0的输出,证明二级缓存已经打开,但是由于SQL的相关问题,导致的缓存没有命中。继续排查……
4. SQL 语句是否一致(一致)
5. 是否在新的SQL查询之前没有关闭上一个SqlSession,导致命中了一级缓存而不需要查询二级缓存(已关闭)。

最终结论

对上述问题都进行了排查,但是依旧没有找出问题,上网上看了半天,发现了问题:


二级缓存存在于 SqlSessionFactory 生命周期中。


我一直以为二级缓存针对的对象是一个Mapper对象,只要是针对同一个Mapper的操作,都可以实现二级缓存。但是前提是操作必须在同一个SqlSessionFactory 中进行。


在一开始的代码中,通过调用三次getFactory()并打开session,实例化了三个不同的SqlSessionFactory 对象,这样在后续的SQL操作中,是不可能命中二级缓存的。


修改如下

public class DaoTest {
    private static final Logger LOGGER = Logger.getLogger(DaoTest.class);

    private SqlSessionFactory getFactory() throws IOException {
        InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(stream);
        return factory;
    }

    @Test
    public void testGlobalCache() throws IOException {
        SqlSessionFactory factory = getFactory();
        SqlSession sess1 = factory.openSession();
        SqlSession sess2 = factory.openSession();
        SqlSession sess3 = factory.openSession();

       try {
            EmployeeMapper mapper1 = sess1.getMapper(EmployeeMapper.class);
            Employee emp1 = mapper1.getEmpByID(1);
            sess1.close();

            EmployeeMapper mapper2 = sess2.getMapper(EmployeeMapper.class);
            Employee emp2 = mapper2.getEmpByID(1);
            sess2.close();

            EmployeeMapper mapper3 = sess3.getMapper(EmployeeMapper.class);
            mapper3.insertEmp(new Employee(null, "XX", "F", "XXX"));
            Employee emp3 = mapper3.getEmpByID(1);
            sess3.close();


            LOGGER.info("emp1 == emp2: " + String.valueOf(emp1 == emp2));
            LOGGER.info("emp1 == emp3: " + String.valueOf(emp1 == emp3));
            LOGGER.info("emp2 == emp3: " + String.valueOf(emp2 == emp3));
        } finally {
        
        }
    }
}

问题解决!

  • 15
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值