데이터베이스 동시 사용자가 많을 때 부하를 분산할 목적으로 시스템마다 다양한 데이터 분산 전략을 사용한다.
여러 대의 데이터베이스 서버를 두고 각 서버에서 발생한 트랜잭션 데이터를 상호 복제하는 방식이다.
=> 실시간 동기화가 필요할 때는 복제 과정에서 발생하는 부하 때문에 실제 부하 분산 효과를 얻기 힘들다.
업무영역별로 데이터베이스를 따로 두고 각각 다른 테이블을 관리하며, 다른 영역의 데이터는 분산 쿼리를 이용해 조회하는 방식이다.
=> 분산 쿼리로 자주 액세스되는 공통 영역의 범위에 따라 부하 분산 효과가 갈린다.
스키마는 같지만 데이터 구분에 따라 데이터베이스를 따로 가져가는 방식이다.
=> 분할된 데이터 간 의존성이 낮을 때 효과가 좋지만, 서버 간 데이터 이동이 발생할 때 어떻게 처리할지에 대한 모델 관점에서의 방안 마련이 필요하다.
오라클 RAC 모델은 공유 디스크 방식에 기반을 두면서 인스턴스 간에 버퍼 캐시까지 공유하는 캐시 퓨전 기술로 발전하였다.
오라클 RAC는 캐시 퓨전 기술을 통해 현재 가장 진일보한 데이터베이스 클러스터링 기술로 평가 받고 있을 뿐 아니라 고가용성, 확장성, 부하 분산 측면에서 이미 성공적인 모델이다.
특히, 데이터를 하나의 데이터베이스에 통합 모델로 관리함으로써 높은 정합성을 유지할 수 있다는 것은 가장 큰 장점이다.
RAC는 글로벌 캐시라는 개념을 사용한다.
즉, 클러스터링 돼 있는 모든 인스턴스 노드의 버퍼 캐시를 하나의 버퍼 캐시로 간주한다.
따라서 필요한 데이터 블록이 로컬 캐시에 없더라도 다른 노드에 캐싱돼 있다면 디스크 I/O를 일으키지 않고 읽거나 쓸 수 있다.
모든 데이터 블록에 대해 마스터 노드가 각각 정해져 있고, 그 노드를 통해 글로벌 캐시에 캐싱돼 있는 블록의 상태와 Lock 정보를 관리한다.
마스터 노드는 각 블록 주소의 해시 값에 의해 인스턴스가 기동되는 시점에 동적으로 정해진다.
캐시 퓨전의 원리는 읽고자 하는 블록이 로컬 캐시에 없을 때 마스터 노드에 전송 요청을 하고, 마스터 노드는 해당 블록을 캐싱하고 있는 노드에 메시지를 보내 그 블록을 요청했던 노드에 전송하도록 지시하는 방식이다.
만약 어느 노드에도 캐싱돼 있지 않다면 직접 디스크에서 읽도록 권한을 부여한다.
RAC 환경에서의 Current 블록은 Shared 모드 Current(SCur)와 Exclusive 모드 Current(XCur)로 나뉜다.
SCur 상태의 블록은 동시에 여러 노드에 캐싱될 수 있지만 XCur 상태의 블록은 단 하나의 노드에만 존재할 수 있다.
=> 한 노드가 XCur 모드로 업그레이드를 마스터 노드에게 요청하면 다른 노드에 캐싱돼 있던 SCur 블록들은 모두 Null 모드로 다운그레이드 된다.
RAC 노드간 버퍼 캐시를 공유하면서 블록을 주고받는 전송 매커니즘은 5개로 나뉜다.
전송 없는 읽기 : Read with No Transfer
읽기/읽기 전송 : Read to Read Transfer
읽기/쓰기 전송 : Read to Write Transfer
쓰기/쓰기 전송 : Write to Write Transfer
쓰기/읽기 전송 : Write to Read Transfer
오라클은 11g 부터, 한 번 실행한 쿼리 또는 PL/SQL 함수의 결과값을 Result 캐시에 저장하는 기능을 제공한다.
DML이 거의 발생하지 않는 테이블을 참조하면서, 반복 수행 요청이 많은 쿼리에 이 기능을 사용하면 I/O 발생량을 현격히 감소시킬 수 있다.
Result 캐시의 캐시 영역
SQL Query Result 캐시 : SQL 쿼리 결과를 저장
PL/SQL 함수 Result 캐시 : PL/SQL 함수 결과값을 저장
Result 캐시의 파라미터들
| 구분 | 기본값 | 설명 |
|---|---|---|
| result_cache_mode | manual | Result 캐시 등록 방식을 결정 |
| 1. manual : result_cache 힌트를 명시한 SQL만 등록 | ||
| 2. force : no_result_cache 힌트를 명시하지 않은 모든 SQL을 등록 | ||
| result_cache_max_size | N/A | SGA 내에서 result_cache가 사용할 메모리 총량을 바이트로 지정 |
| 0으로 설정하면 작동 x | ||
| result_cache_max_result | 5 | 하나의 SQL결과집합이 전체 캐시 영역에서 차지할 수 있는 최대 크기를 %로 지정 |
| result_cache_remote_expiration | 0 | remote 객체의 결과를 얼마 동안 보관할지를 분 단위로 지정. remote 객체는 result 캐시에 저장하지 않도록 하려면 0 으로 설정 |
result_cache_max_size 값을 지정하지 않으면?
memory_target으로 설정된 값의 0.25%를 Result 캐시를 위해 사용
그 값의 0.5%를 Result 캐시를 위해 사용
그 값의 1%를 Result 캐시를 위해 사용
=> 어떤 방식을 사용하든 Result 캐시가 사용할 수 있는 최대 크기는 Shared Pool의 75%를 넘지 않도록 오라클이 관리한다.
Result 캐시는 SGA의 Shared Pool에 저장된다.
SGA 영역이므로 모든 세션에서 공유할 수 있고, 인스턴스를 재기동하면 당연히 초기화 된다.
공유영역에 존재하므로 래치가 필요한데, 11g에서는 두 가지의 래치가 추가됐다.
Result 캐시 동작
result_cache 힌트가 있는 쿼리를 수행할 때, 오라클은 Result 캐시 메모리를 먼저 찾아보고, 캐싱돼 있다면 그것을 가져다 결과 집합을 리턴하고 캐시에 없으면 쿼리를 수행해 결과를 리턴하고, Result 캐시에도 결과를 저장한다.
=> Result 캐시에서 결과 집합을 찾은 경우는 실제 쿼리를 수행하지 않기 때문에 블록 I/O가 전혀 발생하지 않음
Dictionary 오브젝트를 참조할 때
Temporary 테이블을 참조할 때
시퀀스로부터 CURRVAL, NEXTVAL Pseudo 컬럼을 조회할 때
쿼리에서 SQL 실시간 날짜 함수를 사용할 때
바인드 변수는 어떻게 결과집합을 캐싱할까?
각 바인드 변수 값에 따라 개별적으로 캐싱이 이루어진다.
바인드 변수의 Distinct value가 매우 다양(OLTP 환경)하면 캐시에서 계속 밀리기 때문에 수행 빈도가 높은 쿼리에서는 Result 캐시에 등록하는 것은 삼가해야 한다.
오라클은 캐싱된 쿼리가 참조하는 테이블에 변경이 발생하면 해당 캐시 엔트리를 무효화 시킴으로써 쿼리 결과에 대한 정합성을 보장한다.
만약 두 테이블을 참조한다면, 둘 중 하나에 DML이 발생하는 순간 캐싱된 결과 집합이 무효화된다.
파티션 테이블에 DML이 발생할 때나, 변경이 발생한 파티션과 무관한 파티션을 참조하는 쿼리 결과 집합까지 무효화시킨다.
인라인뷰에만 result_cache 힌트를 주어 캐싱할 수 있고, union all 에서는 DML 발생 여부에 따라 각 집합별로 result_cache 힌트를 주어캐싱 여부를 선택할 수 있다.
where 절 서브쿼리에만 result_cache하는 기능은 없다.
Result 캐시는 DW 뿐 아니라 OLTP 환경에서도 잘 활용하면 반복적인 I/O 요청횟수를 줄일 수 있다.
이 기능의 효과를 얻으려면 기본적으로 쿼리 사용빈도가 높아야한다.
추가적으로 효과를 얻는 부분
Result 캐시를 사용하면 안될 때
쿼리가 참조하는 테이블에 DML이 자주 발생할 때
함수 또는 바인드 변수를 가진 쿼리에서 입력되는 값의 종류가 많고, 그 값들이 골고루 입력될 떄
=> Distinct Value 값이 많을 때
데이터베이스 성능은 I/O 효율에 달려있고, 이를 달성하려면 동일한 레코드를 반복적으로 읽지 않고, 필요한 최소 블록만 읽도록 쿼리를 달성해야 한다.
SQL 명령을 던지는 사용자 스스로 최소 일량을 요구하는 형태로 논리적인 집합을 정의하고, 효율적인 처리가 가능하도록 쿼리를 작성하는 것이 중요하다,
사용자 의도대로 블록 액세스를 최소화하면서 효율적인 쿼리 프로세싱을 할 수 있도록 최적의 옵티마이징 팩터를 제공해야 한다.
정렬된 전체 집합을 Fetch 하려면
alter session set optimizer_mode = 'ALL_ROWS';
옵티마이저는 Full Scan을 시도하고 정렬하는 방식을 선택한다.
전체 결과 집합에서 일부 레코드만 Fetch 하다가 멈추려면
alter session set optimizer_mode = 'FIRST_ROWS';
옵티마이저는 정렬된 인덱스를 사용해 정렬 작업을 따로 수행하지 않는다.
오브젝트 통계 수집 전에 시스템 통계도 미리 수집돼 있어야 한다.
dbms_stats.gather_system_stats 프로시저로 구한 통계 항목들
CPU 속도
평균적인 Single Block 읽기 속도
평균적인 Multiblock 읽기 속도
평균적인 Mulitblock I/O 개수