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 {
        
        }
    }
}

問題解決!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章