2010년 12월 26일 일요일

MySQL Query cache 의 구조



작동 방식
  • Query cache는 system-wide한 글로벌 메모리 공간
  • Query cache는 Full scan등과 같이 큰 공간이 필요한 결과는 캐시하지 않도록 
    캐시 최대 사이즈를 제한(query_cache_limit)
  • Query의 결과를 캐시하기 위해서 메모리의 공간을 할당 받을 때, 
    query_cache_min_res_unit 단위로 할당 받으며 (필요시 더 추가적으로), 
    캐시 작업이 완료된 이후 남은 미사용 공간은 반납하게 된다.
  • Query cache는 테이블 단위로 Invalidate 되기 때문에, 
    테이블이 상당히 자주 변경된다면 cache의 효율이 떨어질 수 있다.
  • 일반적으로는 Query cache의 매치 기준은 Query 문장이 동일한지(대소문자 및 공백까지) 비교하는 방식이며
    InnoDB의 경우에는 레코드 기반의 락을 사용하며 MVCC의 제어가 필요하기 때문에 
    재사용 가능한지 판단은 Query 문장뿐만 아니라 Transaction Id로 레코드 접근성까지 비교해야 함
  • Query cache의 관리 비용은 얻는 효과에 비하면 아주 미미하지만, 
    가끔은 캐시 내용을 invalidate 하는데 상당히 많은 비용이 필요할 수도 있음
  • 일반적으로 Query cache는 아래의 경우 상당히 도움이 된다.
     - 테이블이 자주 변경되지 않는 경우
     - 쿼리의 실행 과정은 복잡하고 많은 처리가 필요하지만 결과 셋의 사이즈가 작은 경우
     - 동일 쿼리가 자주 실행되는 경우
  • Query cache의 Hit-Ratio는 계산하는 MySQL의 Status 값 Key_reads를 Status값 Key_read_requests로 
    나누는 방법으로 계산하지만, 이 값이 90%면 좋고 20%면 나쁘다는 단순한 판단은 힘듬 
      -> Query cache의 효율성 판단은 실제 운영 시스템에서 활성화/비활성화를 비교해보는 것이 제일 좋을 듯 하지만, 
          운영 시스템이므로 주의가 필요
      -> query_cache_size 설정 변수는 전역이면서 동적 변수이기 때문에 실시간으로 변경이 가능하므로 
          서비스 영향 없이 설정 변경 후 비교 가능 
          (주의해야 할 것은 기존과 동일하든지 다른 값이든지 일단 한번 설정이 되면 지금까지의 캐시된 내용은 모두 제거됨)

메모리 할당 방식


  • 1) 그림의 아래 부분 처럼 각 색깔별로 A,B,C,D,E 쿼리들이 실행되어서, 1) 번과 같은 상태의 Query cache가 있다고 가정해보자
    - 그림에서 하나의 영역은 Query cache block 으로 일반적으로 "query_cache_min_res_unit"로 정의된 사이즈이며, 
    - <1>번이라고 적힌 영역은 캐시될 ResultSet 을 저장하기 위해서 "query_cache_min_res_unit" 크기의 메모리 블럭을 할당 받아서
       사용하다가 남는 공간은 다시 반납하게 되는데, 이 때문에 발생한 빈 공간임 
       (Fragmentation이라고도 하며, 이런 공간들은 쉽게 재활용되지 못함)
    - 뒷 부분의 흰색 블럭들은 아직 미사용된 블럭들을 표시함 
  • 2) 이 상태에서 아래와 같이 tab2와 tab4를 변경하는 쿼리가 실행되면, 해당 테이블을 참조하는 모든 Query cache는 모두 제거됨
    - UPDATE tab2 SET ... WHERE ...
    - UPDATE tab4 SET ... WHERE ... 
    이런 공간들은 주위의 미사용 영역들과 병합되어서, 나중에 재활용될 수 있음 
    (이런 공간들도 모두 일반적으로 Fragmentation 이라고 표현함) 
  • 3) 아래 명령을 이용하여 이렇게 발생한 Query cache의 Fragmentation을 제거하고, 
    미 사용 영역을 모두 연속된 공간으로 만들어줄 수 있음
    - FLUSH QUERY CACHE;
    이 명령은 Query cache 전체에 대해서 변경되지 않도록 락을 걸기 때문에 조심해서 실행해야 함 
  • 이러한 Query cache 의 block 할당에 관련된 정보는 MySQL의 상태값으로 확인 가능함
    - Qcache_total_blocks   : 무조건 할당된 공간까지의 모든 block들(사용중이든 아니든)의 수를 보여줌
    - Qcache_free_blocks    : 미사용 block들 (Fragmentation이라고 표현한 영역들)의 수를 보여줌
    - 1)번 그림 : Qcache_total_blocks -> 16,  Qcache_free_blocks -> 2
    - 2)번 그림 : Qcache_total_blocks -> 16,  Qcache_free_blocks -> 3
    - 3)번 그림 : Qcache_total_blocks -> 11,  Qcache_free_blocks -> 1

제약 사항 
  • 아래와 같은 형태로 실행되는 쿼리는 Query cache를 사용하지 못함
    - PreparedStatement로 실행되는 쿼리 (MySQL 5.1.17 이후 부터는 Query cache를 사용 가능)
    - Stored Procedure, Function, Trigger 내부에서 실행되는 쿼리
    - Sub Query 형태로 실행되는 쿼리

관련 설정 변수
  • query_cache_limit 
    이 값으로 설정된 사이즈 이상의 결과 셋을 가지는 경우에는 Query cache에 캐시하지 않도록 설정
  • query_cache_min_res_unit 
    Query cache에서 결과 셋을 캐시하기 위한 메모리 공간을 할당 받을 때 사용하는 메모리 할당 최소 단위 사이즈
  • query_cache_size 
    Query cache의 전체 사이즈를 설정하며 1024Byte의 배수로 설정, 
    Query cache를 완전히 비활성화하기 위해서는 이 변수의 값을 0으로 설정해야 한다.
  • query_cache_type 
    Query 의 결과 셋을 어떻게 저장할지를 결정함, 
    - OFF는 캐시하지 않음, 
    - ON은 SQL_NO_CACHE 힌트가 없는 SELECT 문장의 결과 셋은 캐시 대상으로 가정, 
    - DEMAND 는 SQL_CACHE 힌트가 SELECT 문장에 있는 결과 셋만 캐시 대상으로 가정
  • query_cache_wlock_invalidate 
    어떤 Client가 MyISAM 테이블에 Write lock을 가지고 있는 경우, 
    다른 Client가 Query cache에서 결과를 가져갈 수 있는 SELECT문장을 실행하는 것은 Block되지 않는데, 
    이 값을 TRUE로 설정하면 결과를 Query cache에서 가져갈 수 있다 하더라도, 다른 Client는 대기해야 하도록 만든다.

댓글 없음:

댓글 쓰기