데이터베이스 연구실.log

버퍼와 Shared Pool 경합

buffer busy와 RAC gc buffer busy, CBC 래치 및 Shared Pool 경합의 원인과 SQL 및 바인드 변수 대응

buffer busy waitsgc buffer busyPCTFREElatch: cache buffers chainsLatchHot blocklatch: shared pool하드 파싱바인드 변수cursor_sharingFORCE_MATCHING_SIGNATURE

#핵심 요약

메모리와 I/O의 처리 단위는 블록임
서로 다른 행이라도 같은 블록에 있으면 동시 변경 때 버퍼 락을 기다림(buffer busy waits)
TX는 같은 행, buffer busy는 같은 블록 경합임

RAC에서는 gc buffer busy acquire(다른 노드에서 블록을 받는 동안), gc buffer busy release(다른 노드로 보내기 전) 대기가 따로 있음

래치는 SGA 메모리 구조를 아주 짧게 보호하는 장치이며, 기다릴 때 재시도(spin)하므로 경합이 늘면 CPU가 오름
Enqueue는 논리적 자원을 보호하고 큐에 줄을 섬

CBC 래치 경합의 원인은 실행당 버퍼를 많이 읽는 비효율 SQL이나 소수 블록에 몰리는 Hot block임
원인 SQL의 buffer gets를 줄이는 것이 중심임

리터럴 SQL은 값마다 다른 SQL로 하드 파싱되어 Shared Pool 구조를 계속 바꾸고 latch: shared pool과 CPU를 늘림
바인드 변수가 근본 대책이고, 소스를 고칠 수 없으면 cursor_sharing = force를 검토함
Shared Pool 증설로는 해결되지 않음

#개념과 동작 원리

실습은 강사가 제공한 OWI 부하 시나리오(@exec로 대기 이벤트 이름, 세션 수, 실행 시간을 입력)를 썼는데, 이 스크립트는 기재 없음

구분 Enqueue Latch
보호 대상 TX, TM, SQ 등 논리적 DB 자원 버퍼 캐시 체인, 공유 풀 등 메모리 구조
보유 시간 트랜잭션이 끝날 때까지 길 수 있음 아주 짧음
기다리는 방식 큐에서 순서대로 재시도(spin), 경합 시 CPU 상승

버퍼 캐시 블록은 블록 주소의 해시 값에 따라 해시 체인에 연결되고, 체인을 탐색하는 동안 구조가 바뀌지 않게 CBC 래치를 잡음(논리적 I/O와 물리적 I/O)
하드 파싱과 소프트 파싱의 차이는 SQL 공유와 재사용에 있음

buffer busy 완화 방향 비용
PCTFREE 증가 블록당 행 수를 줄여 동시 변경 행 분산 앞으로 채울 블록에만 적용
기존 데이터는 재적재나 MOVE 필요, 공간 증가
파티셔닝, 키 분산 동시 작업의 블록 집중 완화 작업과 데이터 분포에 따름
SQL 튜닝 인덱스로 UPDATE가 접근하는 블록 감소 인덱스 없는 Full Scan은 모든 블록에 접근해 경합 범위가 넓어짐

#필수 명령어 또는 SQL

#buffer busy waits

SQL*Plus (SYSDBA)조회만 함
SELECT s.inst_id, s.event, o.object_name, COUNT(*)
  FROM gv$session s
  LEFT JOIN dba_objects o ON o.object_id = s.row_wait_obj#
 WHERE s.event LIKE '%buffer busy%'
 GROUP BY s.inst_id, s.event, o.object_name;
SQL*Plus (실습 계정)조회만 함
SELECT COUNT(DISTINCT DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)) AS blocks, COUNT(*) AS cnt
  FROM t_buffer_busy_waits;
SQL*Plus (실습 계정)데이터 또는 파일을 삭제함, 되돌릴 수 없음
ALTER TABLE t_buffer_busy_waits PCTFREE 99;
CREATE TABLE t_buffer_busy_waits_backup AS SELECT * FROM t_buffer_busy_waits;
TRUNCATE TABLE t_buffer_busy_waits;
INSERT INTO t_buffer_busy_waits SELECT * FROM t_buffer_busy_waits_backup;
COMMIT;

결과 해석: 재적재 뒤 블록 수가 크게 늘어 블록당 행 수가 줄었고, 같은 부하를 다시 주자 대기 없이 빨리 끝났음
PCTFREE 99는 효과 확인용 극단값이며 운영에서는 행 크기, 동시성, 공간을 고려함
실습에서는 이어서 UPDATE 조건 컬럼 id에 인덱스를 만들어 consistent gets가 크게 줄어드는 것을 AUTOTRACE로 비교했음

#CBC 래치

SQL*Plus (SYSDBA)조회만 함
SELECT inst_id, event, COUNT(*)
  FROM gv$session_wait
 WHERE event LIKE 'latch%'
 GROUP BY inst_id, event;

SELECT *
  FROM (SELECT inst_id, sql_id, executions,
               ROUND(buffer_gets / NULLIF(executions, 0)) AS gets_per_exec,
               SUBSTR(sql_text, 1, 60) AS sql_text
          FROM gv$sql
         WHERE parsing_schema_name = '<SCHEMA>'
         ORDER BY buffer_gets DESC)
 WHERE ROWNUM <= 5;

결과 해석:

실습 분석 대상 SQL은 선택도가 낮은 단일 컬럼 인덱스를 힌트로 강제했음
실습 기록상 type = '4', name = 'KIM', id = '4' 조건은 각각 약 15만, 16만, 15만 건이었음

SQL*Plus (실습 계정)객체 또는 설정을 변경함
CREATE INDEX t_cache_indx1 ON t_cache_buffers_chains_latch (type, name, id);
SQL*Plus (실습 계정)조회만 함
SELECT /*+ index(t t_cache_indx1) */ COUNT(*)
  FROM t_cache_buffers_chains_latch t
 WHERE t.type = '4' AND t.name = 'KIM' AND t.id = '4';

결과 해석: 결합 인덱스로 바꾸자 consistent gets가 크게 줄었음(주석)
읽는 버퍼가 줄면 래치 획득 빈도와 CPU도 줌
실습에서는 OS의 top으로 Oracle 프로세스의 CPU 사용률이 높은 것도 함께 확인했음

#Shared Pool과 하드 파싱

SQL*Plus (SYSDBA)조회만 함
SELECT name, bytes FROM v$sgainfo;

SELECT inst_id, name, value
  FROM gv$sysstat
 WHERE name IN ('parse count (hard)', 'parse count (total)')
 ORDER BY inst_id, name;

결과 해석:

실습 기록: Shared Pool Size 738197504, Granule Size 16777216(44 그래뉼, VM마다 다름)
SGA 메모리 할당 단위는 그래뉼임
실습에서는 SQL 공유와 재사용의 리터럴 PL/SQL 블록을 1만 회로 늘려 실행했고 parse count (hard)가 약 1만 늘었음
바인드 변수 버전은 하드 파싱이 거의 늘지 않았음

SQL*Plus (SYSDBA)객체 또는 설정을 변경함
EXEC DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT;

결과 해석:

부하 전후 스냅샷을 찍고 @?/rdbms/admin/awrrpt.sql, @?/rdbms/admin/addmrpt.sql로 보고서를 만들면 ADDM이 "Hard Parse Due to Literal Usage"를 찾아 바인드 변수 사용과 cursor_sharing = force를 권고했음(출력 텍스트, ADDM과 진단 도구)

#소스를 고칠 수 없을 때

SQL*Plus (SYSDBA)조회만 함
SELECT inst_id, force_matching_signature, COUNT(*) AS cnt, MIN(sql_text)
  FROM gv$sql
 GROUP BY inst_id, force_matching_signature
HAVING COUNT(*) > 100
 ORDER BY cnt DESC;
SQL*Plus (SYSDBA)객체 또는 설정을 변경함
ALTER SYSTEM SET cursor_sharing = force SCOPE = BOTH SID = '*';
-- 비교 후 원복
ALTER SYSTEM SET cursor_sharing = exact SCOPE = BOTH SID = '*';

결과 해석: 같은 FORCE_MATCHING_SIGNATURE에 수천 개 SQL이 있으면 값만 다른 리터럴 SQL임
주의: cursor_sharing은 재시작 없이 바뀌지만 실행계획이 바뀔 수 있어 전체 적용 전에 해당 솔루션 세션 단위 적용을 검토하고, 근본 개선은 업체에 바인드 변수 적용을 요청하는 것임

SEO_HKabsumveritas@gmail.comBuilt with Claude Code & Codex

마지막 갱신 개인 학습 기록이며 운영 환경 적용 전 공식 문서 확인 필요

두 글자 이상 입력, 위아래 화살표로 이동하고 Enter로 열기