>  기사  >  Java  >  Java의 MyBatis 쿼리 캐시에 대한 자세한 설명

Java의 MyBatis 쿼리 캐시에 대한 자세한 설명

怪我咯
怪我咯원래의
2017-06-30 10:57:301382검색

쿼리 캐시의 사용은 주로 쿼리 액세스 속도를 향상시키는 것입니다. 이 기사에서는 주로 MyBatis 쿼리 캐시를 소개합니다. 필요한 친구는 주로 쿼리 액세스 속도를 향상시키기 위해 쿼리 캐시의 사용을 참조할 수 있습니다. 매번 데이터베이스 쿼리로부터 결과 데이터를 얻는 대신, 동일한 데이터에 대해 사용자가 반복적으로 쿼리하는 과정을 단순화하여 접근 속도를 향상시킵니다.

MyBatis의 쿼리 캐싱 메커니즘은 캐시 영역의 범위(

라이프 사이클

)에 따라 두 가지 유형으로 나눌 수 있습니다: 1단계 캐시와 2단계 캐시

1단계 쿼리 캐시MyBatis 첫 번째 수준 캐시는 org.apache.ibatis.cache.impl.PerpetualCache 클래스를 기반으로 하는 HashMap 로컬 캐시이며 해당 범위는 Sqlsession입니다. 동일한 Sqlsession에서 동일한 SQL 문을 두 번 실행합니다. 첫 번째 실행 후 쿼리 결과가 캐시에 기록됩니다. 두 번째 실행에서는 데이터베이스를 쿼리하지 않고 캐시에서 직접 데이터를 가져오므로 쿼리 효율성이 향상됩니다.

Sqlsession이 종료되면 Sqlsession의 첫 번째 수준 캐시가 더 이상 존재하지 않습니다. MyBatis의 첫 번째 수준 캐시는 기본적으로 켜져 있으며 끌 수 없습니다.

1. 1차 캐시 존재 증명

테스트 클래스:

//证明一级缓存的存在
@Test
public void test01(){
 //第一次查询
 Student student = dao.selectStudentById(2);
 System.out.println(student);
 //第二次查询
 Student student2 = dao.selectStudentById(2);
 System.out.println(student2);  
}

mapper:

<mapper namespace="com.hcx.dao.IStudentDao">  
  <select id=selectStudentById resultType="com.hcx.beans.Student">
   select * from student where id=#{id}
  </select>
</mapper>


콘솔:

실행 후 DB에서 단 한 번만 실행되는 것으로 확인되었습니다. Query를 수행하면 두 번째 결과가 직접 출력됩니다. 두 번째는 Sqlsession 캐시에서 읽혀집니다.

2. 캐시에서 데이터를 읽는 기준은 sql ID입니다.

1차 캐시는 동일한 SQL 문의 쿼리 결과가 아닌 동일한 sql 매핑 ID의 쿼리 결과를 캐시합니다. MyBatis 내부 쿼리 캐시는 1차 쿼리이든 2차 쿼리이든 최하위 계층은 해시맵을 사용하여 구현됩니다. 키는 SQL의 ID 관련 내용이고 값은 쿼리 결과입니다. 데이터베이스에서.

mapper:

<mapper namespace="com.hcx.dao.IStudentDao">
  <select id=selectStudentById resultType="com.hcx.beans.Student">
   select * from student where id=#{id}
  </select>
  <select id="selectStudnetById2" resultType="com.hcx.beans.Student">
   select id,name,age,score,birthday from student where id=#{id}
  </select>
</mapper>

dao 인터페이스:

public interface IStudentDao {  
 Student selectStudentById(int id);
 Student selectStudentById2(int id); 
}

Test class:

//证明从一级缓存中读取数据的依据:
//MyBatis:sql的id+sql语句
//hibernate:查询结果对象的id
@Test
public void test02(){
 Student student = dao.selectStudentById(2);
 System.out.println(student);

 Student student2 = dao.selectStudentById2(2);
 System.out.println(student2);  
}

Console:

콘솔을 살펴보니 두 번째 쿼리 결과가 첫 번째와 정확히 같았는데 두 번째 쿼리가 캐시에서 데이터를 읽지 않고, DB에서 직접 쿼리를 수행합니다. 캐시에서 데이터를 읽는 기준은 쿼리 결과가 아닌 쿼리 sql의 매핑 ID이기 때문입니다.

3. 추가, 삭제 및 수정이 첫 번째 수준 쿼리 캐시에 미치는 영향

Sqlsession.commit() 제출 여부에 관계없이 추가, 삭제 및 수정 작업은 첫 번째 항목을 지웁니다. -레벨 쿼리 캐시를 사용하면 DB에서 쿼리를 다시 선택할 수 있습니다.

테스트 클래스:

@Test
public void test03(){
 Student student = dao.selectStudentById(2);
 System.out.println(student);
 //增删改操作都会清空一级缓存,无论是否提交
 dao.insertStudent(new Student("赵六",26,96.6));
 Student student2 = dao.selectStudentById(2);
 System.out.println(student2);  
}

콘솔:


2. 내장된 보조 쿼리 캐시MyBatis 쿼리 캐시의 범위는 매핑 파일 매퍼의 네임스페이스에 따라 구분되며, 동일한 네임스페이스의 매퍼 쿼리 데이터는 동일한 캐시 영역에 저장됩니다. 서로 다른 네임스페이스의 데이터는 서로 간섭하지 않습니다.

1차 캐시와 2차 캐시 모두 네임스페이스에 따라 별도로 저장됩니다. 그러나 첫 번째 수준 캐시와 두 번째 수준 캐시의 차이점은 Sqlsession이 닫히면 Sqlsession의 데이터가 존재하지 않는다는 것입니다. 즉, 첫 번째 수준 캐시가 더 이상 존재하지 않는다는 것입니다. 두 번째 수준 캐시의 수명 주기는 Sqlsession이 닫혀 있는지 여부에 관계없이 전체 응용 프로그램과 동기화됩니다.

두 번째 수준 캐시를 사용하는 목적은 데이터를 공유하는 것이 아닙니다. MyBatis는 쿼리된 개체가 아닌 sql id를 기반으로 캐시에서 데이터를 읽기 때문입니다. 따라서 2차 캐시에 있는 데이터는 여러 쿼리 간에 공유할 의도가 없습니다(모든 쿼리의 쿼리 결과에 개체가 존재하는 한 캐시에서 직접 읽어오는 것입니다. 이것이 바로 데이터 공유입니다. 최대 절전 모드의 캐시는 공유용이지만 MyBatis는 공유용이 아니지만 쿼리 결과의 저장 시간을 연장하고 시스템 성능을 향상시키기 위한 것입니다.

1. 두 번째 수준 캐시 사용

두 번째 수준 캐시를 사용하려면

엔티티 직렬화

매퍼 매핑 파일에 e20c50086bb6799ec27a6a73752fc01b 태그를 추가하세요

1.

쿼리 결과에 포함된 엔터티 클래스는 java.io.Serialized 인터페이스를 구현해야 합니다. 엔터티 클래스에 상위 클래스가 있거나 도메인 속성이 있는 경우 상위 클래스와 도메인 속성 클래스도

직렬화 인터페이스

를 구현해야 합니다.

public class Student implements Serializable{
 private Integer id;
 private String name;
 private int age;
 private double score;
}

2. 매퍼 매핑 파일에 e20c50086bb6799ec27a6a73752fc01b 태그를 추가하세요.

매퍼 매핑 파일의 e20c50086bb6799ec27a6a73752fc01b 하위 태그를 추가하세요.

<mapper namespace="com.hcx.dao.IStudentDao">
 <cache/>
  <select id=selectStudentById resultType="com.hcx.beans.Student">
   select * from student where id=#{id}
  </select>
</mapper>

3. 2차 캐시 구성

e20c50086bb6799ec27a6a73752fc01b 태그에 관련 속성 설정을 추가하여 2차 캐시의 작동 성능을 제어하세요. 설정을 지정하지 않으면 기본값이 유지됩니다.

<cache eviction="IFIO" flushInterval="10800000"
  readOnly="true" size="512"/>

eviction:逐出策略。当二级缓存中的对象达到最大值时,就需要通过逐出策略将缓存中的对象移出缓存。默认为LRU。常用的策略有FIFO和LRU

flushInterval:刷新缓存的时间间隔,单位毫秒。这里的刷新缓存即清空缓存。一般不指定,即当执行增删改时刷新缓存。

readOnly:设置缓存中数据是否只读。只读的缓存会给所有调用者返回缓存对象的相同实例,因此这些对象不能被修改,这提供了很重要的性能优势。但读写的缓存会返回缓存对象的拷贝。这会慢一些,但是安全,因此默认是false。
size:二级缓存中可以存放的最多对象个数。默认为1024个。

2.二级缓存的存在性证明

对于映射文件中的同一个查询,肯定是同一个namespace中的查询。在一次查询后,将Sqlsession关闭,再进行一次相同查询,发现并没有到DB中进行select查询,说明二级缓存是存在的。

//证明二级缓存的存在
@Test
public void test01(){
 //第一次查询
 Student student = dao.selectStudentById(2);
 System.out.println(student);
 sqlSession.close();
 sqlSession = MyBatisUtils.getSqlSession();
 dao = sqlSession.getMapper(IStudentDao.class);
 //第二次查询
 Student student2 = dao.selectStudentById(2);
 System.out.println(student2);  
}


查看控制台:

Cache Hit Ratio表示缓存命中率。开启二级缓存后,每执行一次查询,系统都会计算一次二级缓存的命中率。第一次查询也是先从缓存中查询,只不过缓存中一定是没有的。所以会再从DB中查询。由于二级缓存中不存在该数据,所以命中率为0.但第二次查询是从二级缓存中读取的,所以这一次的命中率为1/2=0.5。当然,若有第三次查询,则命中率为1/3=0.66

3.增删改对二级缓存的影响

增删改操作,无论是否进行提交sqlSession.commit(),均会清空一级、二级缓存,使查询再次从DB中select。

测试类:

@Testpublic void test02(){
 //第一次查询
 Student student = dao.selectStudentById(2);
 System.out.println(student);
 sqlSession.close();
 sqlSession = MyBatisUtils.getSqlSession();
 dao = sqlSession.getMapper(IStudentDao.class);
 //插入
 dao.insertStudent(new Student("",0,0));
 //第二次查询
 Student student2 = dao.selectStudentById(2);
 System.out.println(student2);  
}

控制台:

注意,在第二次查询时的缓存命中率为0.5,但还是从DB中查询了。说明在缓存中与该查询相对应的key是存在的,但其value被清空。而value被清空的原因是前面执行了对DB的增删改操作,所以不会从缓存中直接将null值返回,而是从DB中进行查询。

说明:

二级缓存的清空,实质上是对所查找key对应的value置为null,而非将e6fd748e50e82e7bde0d1e0efd3285df对,即entry对象删除。

从DB中进行select查询的条件是:缓存中根本不存在这个key或者缓存中存在该key所对应的entry对象,但value为null。

设置增删改操作不刷新二级缓存:

若要使某个增、删或改操作不清空二级缓存,则需要在其2fe416ebc99307b5baa877beb4126ed0或f353c4e6f3e13c8cf4dcf649d0662b12或89f2e2a6c0b1bd54a34bdf16f85e8819中添加属性flushCache="false",默认为true。

<insert id="insertStudent" flushCache="false">
  insert into student(name,age,score) values(#{name},#{age},#{score})
 </insert>

4.二级缓存的关闭

二级缓存默认为开启状态。若要将其关闭,则需要进行相关设置。
根据关闭的范围大小,可以分为全局关闭和局部关闭

1.全局关闭(在配置文件中设置)

全局关闭是指整个应用的二级缓存全部关闭,所有查询均不使用二级缓存。全局开关设置在主配置文件的全局设置c5c29794bda4e81a53ad53fcf163f82c中,该属性为cacheEnabled,设置为false,则关闭;设置为true,则开启,默认值为true。即二级缓存默认是开启的。

<!-- 关闭二级缓存 -->
<settings>
 <setting name="cacheEnabled" value="false"/>
</settings>

2.局部关闭(在映射文件的每个select中设置)

局部关闭指整个应用的二级缓存是开启的,但只是针对某个815c08aea17433a6272b681dc1d34168查询,不使用二级缓存。此时可以单独只关闭该815c08aea17433a6272b681dc1d34168标签的二级缓存。

在该要关闭二级缓存的815c08aea17433a6272b681dc1d34168标签中,将其属性useCache设置为false,即可关闭该查询的二级缓存。该属性默认为true,即每个815c08aea17433a6272b681dc1d34168查询的二级缓存默认是开启的。

<!--useCache="false"对当前sql的二级缓存的局部关闭 -->
 <select id=selectStudentById useCache="false" resultType="com.hcx.beans.Student">
  select * from student where id=#{id}
 </select>

5.二级缓存的使用原则

1.只能在一个命名空间下使用二级缓存

由于二级缓存中的数据是基于namespace的,即不同namespace中的数据互不干扰。在多个namespace中若均存在对同一个表的操作,那么这多个namespace中的数据可能就会出现不一致现象。

2.在单表上使用二级缓存

如果一个表与其它表有关联关系,那么久非常有可能存在多个namespace对同一数据的操作。而不同namespace中的数据互补干扰,所以就有可能出现多个namespace中的数据不一致现象。

3.查询多于修改时使用二级缓存

在查询操作远远多于增删改操作的情况下可以使用二级缓存。因为任何增删改操作都将刷新二级缓存,对二级缓存的频繁刷新将降低系统性能。

三、ehcache二级查询缓存

MyBatis允许使用第三方缓存产品。ehCache就是其中一种。

注意:使用ehcache二级缓存,实体类无需实现序列化接口。

1.导入jar包

2.添加ehcache.xml

解压ehcache的核心jar包ehcache-core-2.6.8.jar,将其中的一个配置文件ehcache-failsafe.xml直接放到项目的src目录下,并更名为ehcache.xml

(1)d72dee856555ebc5179da8bdfded1f5b标签

指定一个文件目录,当内存空间不够,需要将二级缓存中数据写到硬盘上时,会写到这个指定目录中。其值一般为java.io.tmpdir,表示当前系统的默认文件临时目录。

当前文件系统的默认文件临时目录,可以通过System.property()方法查看:

@Test
public void test(){
 String path = System.getProperty("java.io.tmpdir");
 System.out.println(path);
}

(2)730eca52cf3beed4d554a3843e167f2e标签

3.启用ehcache缓存机制

在映射文件的mapper中的e20c50086bb6799ec27a6a73752fc01b中通过type指定缓存机制为Ehcache缓存。默认为MyBatis内置的二级缓存org.apache.ibatis.cache.impl.PerpetualCache。

<mapper namespace="com.hcx.dao.IStudentDao">
 <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
  <select id=selectStudentById resultType="com.hcx.beans.Student">
   select * from student where id=#{id}
  </select>
</mapper>

4.ehcache在不同mapper中的个性化设置

在ehcache.xml中设置的属性值,会对该项目中所有使用ehcache缓存机制的缓存区域起作用。一个项目中可以有多个mapper,不同的mapper有不同的缓存区域。对于不同缓存区域也可进行专门针对于当前区域的个性设置,可通过指定不同mapper的62aecd17e676a41d3547c3bf97bb07b0属性值来设置。

62aecd17e676a41d3547c3bf97bb07b0属性值的优先级高于ehcache.xml中的属性值。

<mapper namespace="com.hcx.dao.IStudentDao">
 <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
   <property name="maxElementsInMemory" value="5000"/>
   <property name="timeToIdleSeconds" value="240"/>
 </cache>
</mapper>

위 내용은 Java의 MyBatis 쿼리 캐시에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.