Tag Archives: Cache Model

[JAVA] iBATIS + OSCACHE 사용 시 Cache Model에 대한 이해

iBATIS는 공식적으로 캐시를 할때에 OSCache를 사용할 수 있도록 되어있습니다. 하지만 그 기능이 매우 자동적이며 제한적이고 세세한 설정을 개발자가 할수가 없습니다. 그래서 다음을 한번 알아 보기로 할까요. 다음의 예시는 [이곳]에 언급된 내용을 살짝 수정하였습니다.

<cacheModel type="OSCACHE" id="cacheModel" readOnly="true"> 
  <flushInterval hours="24"/> 
  <flushOnExecute statement="flushCache"/> 
</cacheModel> 
<resultMap class="kr.pe.theeye.Cache" id="CacheResult"> 
  ... 
</resultMap> 
  
<insert id="flushCache" resultClass="string"> 
  INSERT ... 
</insert>

<select id="makeCache1" resultMap="CacheResult" cacheModel="cacheModel"> 
  SELECT ... 
</select>

<select id="makeCache2" resultMap="CacheResult" cacheModel="cacheModel"> 
  SELECT ... 
</select>

<select id="makeCache3" resultMap="CacheResult" parameterClass="int" 
  cacheModel="cacheModel"> 
  SELECT ... WHERE PAGE = #value#
</select>

위의 SQL맵 예제에서는 INSERT문 한개와 SELECT문 3개가 존재합니다. 모두 cacheModel이라는 id의 캐시모델과 연관되어집니다. 이것을 간단하게 그림으로 그려보면 다음과 같은 모양을 가지고 있습니다.
사용자 삽입 이미지
그려놓고 보니깐 좀 말이 안되는 그림 같아 보이네요;; 아무튼 하나의 캐시 모델에 3가지의 캐시를 생성할 수 있는 조건이 있고 2가지 캐시를 삭제할 수 있는 조건이 있다고 봐주시면 되겠습니다. 둥근 사각형은 개발자가 임의로 호출을 해야만 하는 기능들이고 위의 동그라미는 캐시 유지 시간 설정으로 봐주시면 되겠습니다.

이제 다음의 몇가지 예시 상황들에 대한 캐시의 처리 과정에 대해 알아보겠습니다.

1. 한개의 캐시 처리 (makeCache1 → flushCache)
makeCache1이 수행되면 cacheModel에 하나의 캐시가 생성됩니다.  앞으로 makeCache1이 호출될때마다 캐시가 존재하는한 DB에 접근없이 캐시결과값을 제공하게 됩니다. flushCache를 수행하면 캐시가 삭제됩니다. 다시 makeCache1을 호출하면 DB에서 결과를 가져와서 반환함과 동시에 캐시를 생성하게 됩니다. 캐시가 생성된 시점에서 flushInterval에 설정된 시간이 경과하도록 flushCache가 호출되지 않는다면 시간 만료로 자동 삭제됩니다.

2. 두개의 캐시 처리 (makeCache1 → makeCache2 → flushCache)
makeCache1이 호출되면 cacheModel에 하나의 캐시가 생성됩니다. makeCache2가 호출되면 마찬가지로 cacheModel에 또다른 하나의 캐시가 생성됩니다. 이 두개의 캐시는 엄연히 다르며 각각의 makeCacheX가 호출될때 해당하는 만들어진 캐시값을 반환하게 됩니다. 하지만 둘다 모두 동일하게 cacheModel안에 소속됩니다. 이어서 flushCache를 호출하게 되면 두 캐시가 모두 삭제됩니다. 정확히는 flush에 대한 설정을 해두면 해당 캐시모델의 모든 캐시를 소거한다고 보시면 됩니다. 그러므로 이런 부분에 주의하여 캐시모델을 함께 사용할지 따로 다른 캐시모델을 만들지를 결정하셔야 합니다.

3. 인자값의 차이에 따른 처리 (makeCache3[1] -> makeCache3[2] -> flushCache)
makeCache3에는 parameterClass를 사용하여 동적인 쿼리를 수행하도록 되어있습니다. 예시로 간단하게 int값을 받도록 하였는데요. 캐시를 생성할때의 키값에는 이 인자값들이 모두 포함되어 키를 이룹니다. 그러므로 paramterClass로 넘어오는 값이 1일때와 2일때는 다른 쿼리(키)가 됩니다. 그러므로 1이라는 값의 인자를 받아 실행되는 makeCache3의 캐시와 2라는 값을 받아 실행되는 makeCache3는 각각 별개의 캐시가 생성됩니다. 마찬가지로 하나의 cacheModel안에서 호출되지만요. flushCache를 호출하면 이 두캐시가 모두 삭제됩니다.

결론을 내보자면 위와 같은 iBATIS에서 제공하는 기본적인 캐시모델로는 같은 쿼리지만 다른 결과가 나올 수 있는 부분에는 사용할 수 없습니다. SNS 서비스에서 볼 수 있을 다음을 생각해 봅시다.

  1. 친구들의 최근근황을 모아서 볼 수 있는 기능이 있다고 가정한다.
  2. 사용자가 접속하여 친구들의 최근근황을 확인하였다. [캐시 생성됨]
  3. 친구중 한명이 최근 근황을 업데이트 하였다.
  4. 사용자가 다시한번 친구들의 최근근황을 확인하였다. [캐시값 반환됨]

위를 수행하였을 때 친구들의 정보가 바뀌어도 사용자는 계속 캐시된 값을 받게됨을 알 수 있습니다. 그러므로 친구들의 업데이트 된 정보를 적시에 얻기가 힘듭니다. 하지만 그렇다고 친구의 정보가 업데이트 될때 다른 사용자의 캐시를 삭제하는데도 무리가 있습니다. 왜냐하면 캐시 키 값을 모르기 때문이죠. 이부분을 해결하려면 iBATIS의 SQL맵 캐시 기능을 사용하지 말고 자체적인 알고리즘으로 구현을 해야 할 것 같습니다.