Tag Archives: 자바캐싱

[Source | MyBatis] DB 부하를 줄이기 위한 쿼리캐싱 기능

거의 바뀌지 않거나 고정된 값을 디비에서 자주 쿼리해야할 경우가 있다. 이때 마이바티스에서 제공하는 쿼리캐싱 기능 사용법을 알아보자.


작성일 : 2021-04-06

https://mybatis.org/mybatis-3/ko/sqlmap-xml.html#cache
공식문서에 잘 설명이 되어있다.


1> 캐싱적용시 고려해야하는 사항들

* flush / set 하는 시점을 명확히 정의 해야한다.
--> 단순히 부하를 줄인다고 여기저기 사용했다가 갑자기 이상한 데이터로 에러가 발생할 수 있고 추적 또한 어렵다.
* 마이바티스의 경우 쿼리 단위로 캐싱을 해줘 문제가 되지 않지만 Redis나 Memcached, ehcache등을 사용한다면 값저장시 키값을 잘 정의해야한다.
--> 보통 키값에 prefix(객체명+'_')를 주어 구분한다.
* 데이터가 자주 변경되는 경우 사용여부를 다시한번 고려해보자.
--> 변경시 flush하고 확인시 다시 set해줘야 하기 때문에 불필요한 절차만 추가할 뿐이다.

2> 적용예제

<?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.opendocs.www.repository.categorys.CategorysDaoMapper">
    
   <!-- Configuration Caching -->
   <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>

   <!-- Insert Query Flush Cache --> 
   <insert id="insert" parameterType="categorys">
      INSERT INTO opendocs_www.categorys (uid, pid, name, status, created_at) 
         VALUES(#{uid}, #{pid}, #{name}, #{status}, now())
   </insert>

   <!-- Select Query Use Cache --> 
   <select id="select" parameterType="categorys" resultType="categorys">
      SELECT uid, pid, name, status, created_at, updated_at 
      FROM opendocs_www.categorys T1 
   </select>
	
   <!-- Update Query Flush Cache --> 
   <update id="update" parameterType="categorys">
      UPDATE opendocs_www.categorys
      <set>
         <if test="1 == 1">updated_at = now(),</if>
         <if test="pid != null">pid = #{pid},</if>
         <if test="name != null">name = #{name},</if>
         <if test="status != null">status = #{status},</if>
      </set> 
      WHERE uid = #{uid}
   </update>
	
   <!-- Delete Query Flush Cache --> 
   <delete id="delete" parameterType="categorys">
      DELETE FROM opendocs_www.categorys WHERE uid = #{uid}
   </delete>
	
</mapper>
위의 예제는 select시 캐싱하고 insert, update, delete시 캐싱된 데이터를 지운다.
<cache/>를 선언하게 되면 다음 속성을 선언한 것과 같으며 특정쿼리가 캐싱되지 않기를 원한다면 useCache="false"를 선언해주면 된다.
<select ... flushCache="false" useCache="true"/>
<insert ... flushCache="true"/>
<update ... flushCache="true"/>
<delete ... flushCache="true"/>

2-1> eviction : 캐싱전략

* LRU - 가장 오랜시간 사용하지 않는 객체를 제거
* FIFO - 캐시에 들어온 순서대로 객체를 제거
* SOFT - 가비지 컬렉터의 상태와 강하지 않은 참조(Soft References )의 규칙에 기초하여 객체를 제거
* WEAK - 가비지 컬렉터의 상태와 약한 참조(Weak References)의 규칙에 기초하여 점진적으로 객체 제거

2-2> flushInterval : 플러시 주기 (밀리세컨드 단위 1초 = 1000)

* 밀리세컨드 단위로 1초이면 1000
* 기본값없음 (미선언시 쿼리수행시만 동작)

2-3> size : 캐싱 사이즈

* 메모리 상황을 고려한 적정한 사이즈
* 기본값 1024

2-4> readOnly : 읽기전용 여부

* 모든 호출자에게 캐시된 객체의 같은 인스턴스(변경불가)를 리턴
* 사용시 성능 향상
* 기본값 false

3> 다른 캐싱방법

상황에 맞추어 아래 캐싱방법도 고려하여 보자.

  • Memcached
  • Redis
  • ehcache