반응형

 Result Cache 메모리 영역은 Shared Pool 안에 존재를 한다. 컨셉 자체가 자주 사용되는 쿼리나 펑션의 수행 결과에 대해서 메모리에 저장 시켜 놓았다가, 이후 재 수행되는 쿼리나 펑션에 대하여 수행시간을 빠르게 해 준다는 것이므로 Shared Pool영역안에 존재 하는 것이 맞을 것이다.
 
 자주 사용되는 쿼리나 펑션에 대하여 버퍼 캐시까지 가지 않아도 되므로 cache buffer chains latch 이벤트, Recursive call 수가 많이 줄어 들지 않을까 생각을 해 본다.
 
 Result Cache영역의 크기는 result_cache_max_size 파라메터 값으로 지정이 가능하다. SGA를 Dynamic하게 사용을 하더라도 이 영역의 크기 만큼은 보장을 해 주기 위해서 따로 설정을 해 놓은 것이다.
 다른 컴포넌트들도 크기를 지정해 놓으면 최소 크기를 보장 받을 수 있는 것과 같은 맥락으로 보면 될 것이다.
 
 Result Cache는 필요 시 마다 32K (1K Block * 32)단위로 메모리를 늘려 가면서 수행결과를 저장한다.
저장되는 데이터로는 SQL 또는 Function에서 참조하는 Object 및 수행 결과가 저장이 된다.
 하나의 SQL 또는 Function에 의해서 저장되는 Result Set의 크기는 1블록 에서 부터  result_cache_max_result에 지정된 크기 까지 저장이 가능하다.


 그럼 어떤 방식으로 수행 결과가 메모리에 할당 되고 재 사용되는지 알아 보도록 하자.


1. 캐싱

  Cache 버퍼에 SQL/Function을 이용하여 결과를 저장하기 위해서는 최초 수행하는 세션에서는 Cache id를 할당 받으며 이 할당 받은 세션은 Result Cache Latch를 획득한 후 수행결과를 등록 할 Free블록을 찾은 후 해당 블록에 lock 모드 6으로 획득 후 SQL/Function을 수행하게 된다.
 이 때 다른 세션에서 동일 SQL/Function을 수행하는 세션이 있다면 Cache id값을 가지고 Result 블록을 찾아 와서 사용하기 위해 lock을 4 모드로 획득하려 한다. 그러나 아직 최초 세션에서 Fetch를 하지 않아서 아직 결과가 메모리에 로드 되었거나 되고 있는 상황이 아니라면 최초 세션의 Execution이 끝날때 까지 무한정 기다리는 경우도 발생이 된다.
 아마도 최초 세션에서 수행결과를 로드 할때 까지 기다리는 것이 다른 세션에서 동시에 시간이 오래 걸리는 작업을 수행하는 것보다 더 낫다고 판단하여 그런 로직을 사용하는 것으로 추정된다.
 최초 수행 세션은 Execution 단계가 끝나고 Fetch 단계에서 비로서 결과를 로드 하기 시작하는데 이때 결과가 하나의 블록 이상이 된다면 Result Cache latch를 획득하고 블록에 Lock을 거는 작업을 반복해서 수행하게 된다.
  데이터를 로드 하는 중에 v$result_cache_objects뷰의 status 컬럼의 상태는 New로 보인다.
  하지만 로드 하는 데이터가 많을 경우 다른 세션에서 해당 데이터를 사용하려다가 _result_cache_timeout 파라메터에 지정된 시간 이상이 될 경우 status 컬럼의 상태는 Bypass로 변경이 된다. 로드가 완료가 되면 status는 Published 로 된다.

 리모트 Object에 대하여 캐싱하기 위해서는 result_cache_remote_expiration 파라메터의 값을 0 이 아닌 다른 값으로 설정하면 된다. 이렇게 로드되어 있는 데이터가 result_cache_remote_expiration 설정시간을 초과한 후에는 status 컬럼의 값이 Expired로 표시 된다.
 참조하는 Object의 데이터 변화가 발생하면 v$result_cache_objects뷰에서 dependency object의 invalidation값이 1 증가 하며 이를 참조하는 SQL/Function의 status 컬럼은 Invalid로 변한다.
 또 max result 크기를 초과한 경우도 invalid로 보인다.

 위와 같이 결과를 Result Cache에 로드 하기 위해서는 latch -> 블록에 대한 lock을 획득 하는 메카니즘이 필요하다. 그렇기 때문에 많은 양의 데이터를 로드 할 경우 latch 요청 및 lock 획득 시간이 길어 지게 된다.


2. 프리 블록 찾기

 최초 Result Cache에 대한 등록 요청이 발생 할 경우 오라클은 32K (1K * 32) 만큼의 크기를 할당 받은 후
참조 Object를 로드 시키고, 수행 결과를 로드 시킨다.
 만약 32K 만큼의 공간을 사용중이라면 새로이 등록을 시도하는 세션은 다음과 같은 순서로 프리 블록을 찾은 후 자신의 수행 결과를 등록한다.
 
     1. status 컬럼이 invalid인 SQL또는 PL/SQL결과 영역을 사용.
        (type이 Dependency인 것은 절대 재 사용되지 않는다)
     2. status 컬럼이 invalid인 데이터가 없거나 그 크기가 부족 할 경우 새로이 32K를 할당 받은 후 사용.
     3. result_cache_max_size값에 도달하여서 더이상 프리 블록을 찾을 수 없을 경우 lru_number 순서대로
        블록을 재 사용 ( 0 -> 1 -> 2 ->  ..... )
        최근에 사용된 Result Set은 lru_number값이 제일 큰 값을 갖는다.


여기까지 간단히(?) 오라클이 Result Cache영역을 관리하고 사용하는 방식에 대해서 확인해 보았다.

테스트를 진행해 보면서 의아했던 점이, 아무리 공간이 부족해도 type이 Dependency인 것은 재 사용이 되질 않는다는 점이다.
 그리고 result cache에 대해서 flush를 수행 한 후, flush 전에 사용하던 쿼리를 수행하여 결과를 로드 시키려 하면, 최초 수행시는 Dependency만 등록이 되고 쿼리 수행 결과는 등록이 되질 않는다.
 두 번째 수행을 해야만 쿼리 수행결과가 등록이 된다. 그러나 flush전에 등록되어 있던 펑션을 수행 할 경우는 한 번만에 등록이 되어 사용이 된다.
 테스트를 여러번 수행해 보았는데 같은 결과가 나왔다.  Result Cache 영역이 작고 SQL/Function에서 참조 하는 Object가 많을 경우 Result Cache에 결과는 로드 되지 않고 Dependent Object정보만이 남아 있는 경우가 발생할 수 있다.
 

반응형

+ Recent posts