데이터베이스 연구실.log

Cache Fusion과 블록 전송

Cache Fusion과 GRD의 블록 상태, 인스턴스 간 읽기 및 쓰기 전송과 V$BH 조회 실습

T_PAYGV$TRANSACTIONp_bonusCache FusionGCSGRDV$BHxcurscurpicrInterconnectgc cr blocks receivedgcs log flush sync

#핵심 요약

Cache Fusion은 필요한 블록이 다른 인스턴스의 버퍼 캐시에 있으면 공유 디스크에 쓰고 다시 읽지 않고 인터커넥트로 전달하는 방식임

GCS는 GRD(Global Resource Directory)로 각 블록이 어느 인스턴스에 어떤 모드로 있는지 관리함

쓰기용 현재 블록(xcur)은 클러스터에서 한 노드만 가짐
다른 노드가 쓰기를 요청하면 xcur가 넘어가고 이전 노드에는 과거 이미지(pi)가 남을 수 있음

미커밋 변경 블록을 다른 노드가 읽으면, 보유 노드가 Undo로 조회 시점의 CR 블록을 만들어 보냄
커밋을 빠뜨린 트랜잭션은 다른 노드의 조회마다 CR 전송을 늘림

같은 Hot block을 여러 노드가 번갈아 바꾸면 블록 왕복과 gc 대기가 늘어남
업무나 데이터를 노드별로 나누는 것을 검토함

#개념과 동작 원리

RAC 구조 일반은 고가용성과 재해복구 분야에서 다룸

V$BH.STATUS 의미
xcur 배타적 현재 블록, 쓰기 접근
scur 공유 현재 블록, 여러 인스턴스의 읽기
null 블록 자원 권한 없음
pi 과거 이미지
다른 노드가 갱신한 뒤 복구 목적으로 남는 이미지
cr 읽기 일관성용 사본
전송 상황 동작 노드별 상태
① 최초 읽기, 전송 없음 어느 인스턴스에도 없는 블록을 디스크에서 읽음 조회 노드 scur
② Read → Read 다른 노드의 scur 블록을 인터커넥트로 받음 양쪽 scur
③ Read → Write 읽기 상태 블록을 한 노드가 변경 갱신 노드 xcur, 다른 노드 권한 해제
④ Write → Read(커밋 후) 처음 요청 때는 보유 노드 xcur 유지 가능, 반복 요청 후 양쪽 scur
⑤ Write → Read(커밋 전) 보유 노드가 Undo로 CR 블록을 만들어 전송 보유 노드 xcur 유지, 요청 노드 cr
⑥ Write → Write 보유 노드가 리두를 먼저 기록하고 블록 전송 요청 노드 xcur, 이전 노드 pi

실습의 "4가지 경우" 설명에는 세 가지만 나열되어 있어 나머지 한 가지는 확인 불가
scur 전환 시점은 버전과 환경에 따라 다를 수 있음

⑥에서 블록을 보내기 전에 리두를 기록해야 하므로 리두 쓰기 성능이 블록 전송 속도에 영향을 줌
실습에서는 gcs log flush sync가 늘면 리두 저장소의 쓰기 성능을 확인하라고 적었음

#필수 명령어 또는 SQL

#블록 위치와 락 모드 조회 스크립트

SQL*Plus (실습 계정)객체 또는 설정을 변경함
CREATE TABLE rac_test (id NUMBER);
INSERT INTO rac_test VALUES (1);
COMMIT;
SQL*Plus (실습 계정)조회만 함
SELECT DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) AS fno,
       DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) AS blkno
  FROM rac_test;

-- lock_mode.sql로 저장해 두 노드에서 사용
SELECT b.lock_element_addr, b.status, e.mode_held, e.local
  FROM v$bh b, v$gc_element e
 WHERE b.file# = <FNO> AND b.block# = <BLKNO>
   AND b.lock_element_addr = e.gc_element_addr;

결과 해석:

실습 결과(순서대로): DB 재기동 직후 두 노드 모두 no rows selected → 1번 노드 조회 후 1번 scur(MODE_HELD 1) → 2번 노드 조회 후 양쪽 scur → 2번 노드가 UPDATE, COMMIT하자 2번 xcur(MODE_HELD 2), 1번은 no rows selected(남아 있다면 V$BH에서 cr)
실습에서는 flush buffer_cache 뒤에도 GRD 정보가 남을 수 있어 재기동으로 초기 상태를 만들었다고 적었음

#커밋 전 읽기와 CR 블록

SQL*Plus (1번 노드, 실습 계정)객체 또는 설정을 변경함
-- 커밋하지 않음
UPDATE rac_test SET id = id + 1;
SQL*Plus (2번 노드, 실습 계정)조회만 함
SELECT name, value
  FROM v$mystat m JOIN v$statname n USING (statistic#)
 WHERE name = 'gc cr blocks received';

-- 여러 번 반복 후 위 조회 재실행
SELECT * FROM rac_test;
SQL*Plus (SYSDBA)조회만 함
SELECT inst_id, status, COUNT(*)
  FROM gv$bh
 WHERE file# = <FNO> AND block# = <BLKNO>
 GROUP BY inst_id, status
 ORDER BY inst_id;

결과 해석: gc cr blocks received가 조회할 때마다 늘고, GV$BH에는 1번 노드 xcur, 2번 노드 cr 여러 개가 보임
실습에서는 실습 뒤 1번 노드에서 ROLLBACK했음

#Write → Write와 pi

같은 블록에 행을 하나 더 넣고, 1번 노드가 첫 행을, 2번 노드가 두 번째 행을 각각 UPDATE, COMMIT함

SQL*Plus (SYSDBA)조회만 함
SELECT inst_id, status, dirty
  FROM gv$bh
 WHERE file# = <FNO> AND block# = <BLKNO>
   AND status IN ('xcur', 'pi')
 ORDER BY inst_id;

SELECT inst_id, event, total_waits
  FROM gv$system_event
 WHERE event LIKE 'gcs log flush%';

결과 해석:

실습 결과: INST_ID 1, pi, Y(이전에 고친 노드), INST_ID 2, xcur, Y(마지막에 고친 노드)
행이 달라도 같은 블록이면 전송이 일어남

#확인 문제: 번갈아 고친 결제 블록이 두 노드에 남은 상태

상황: 결제 승인 테이블을 두 노드가 번갈아 고칠 때 블록이 디스크를 거치는지, 두 노드가 차례로 고친 뒤 각 노드에 그 블록이 어떤 상태로 남는지 한 번의 조회로 보여 달라는 요청

SQL*Plus (실습 계정)객체 또는 설정을 변경함
-- 1번 노드
CREATE TABLE t_pay (pay_id NUMBER, status VARCHAR2(10));
INSERT INTO t_pay VALUES (100, 'READY');
COMMIT;
SELECT dbms_rowid.rowid_relative_fno(rowid) AS fno,
       dbms_rowid.rowid_block_number(rowid) AS blkno
  FROM t_pay;
-- 2번 노드
UPDATE t_pay SET status = 'APPROVED' WHERE pay_id = 100;
COMMIT;
-- 1번 노드
UPDATE t_pay SET status = 'SETTLED' WHERE pay_id = 100;
COMMIT;
SQL*Plus (SYSDBA, 1번 노드)조회만 함
SELECT inst_id, status, dirty
  FROM gv$bh
 WHERE file# = &fno AND block# = &blkno
 ORDER BY inst_id;

결과 해석:

예상값: INST_ID 1은 xcur(마지막에 고친 노드), INST_ID 2는 pi(이전에 고친 노드), 둘 다 DIRTY = Y

1번 노드는 최신 블록을 디스크가 아니라 2번 노드에게서 인터커넥트로 받아 고침

pi는 인스턴스 복구에 쓰일 수 있는 이전 이미지이며, 최신 블록이 디스크에 기록되면 복구 조건에 따라 지워짐

실습이 끝나면 DROP TABLE <SCHEMA>.t_pay PURGE로 지움
실행 결과 확인 불가

#확인 문제: 커밋을 빠뜨린 프로시저 때문에 2번 노드 조회가 느려짐

상황: 배포 뒤 2번 노드의 조회 화면이 느려졌지만 락에 걸린 것처럼 멈추지는 않음
배포한 것은 1번 노드에서 도는 보너스 계산 프로시저 하나뿐임

SQL*Plus (실습 계정, 1번 노드)객체 또는 설정을 변경함
CREATE OR REPLACE PROCEDURE p_bonus IS
BEGIN
  UPDATE emp SET comm = nvl(comm, 0) + 100 WHERE deptno = 30;
  -- COMMIT 누락
END;
/
EXEC p_bonus

결과 해석:

PL/SQL procedure successfully completed가 나와 끝난 것처럼 보여도 트랜잭션은 열려 있음

SQL*Plus (실습 계정, 2번 노드, 조회를 여러 번 반복)조회만 함
SELECT empno, ename, comm FROM emp WHERE deptno = 30;

SELECT name, value
  FROM v$mystat m JOIN v$statname n USING (statistic#)
 WHERE name = 'gc cr blocks received';
SQL*Plus (SYSDBA, 1번 노드)조회만 함
SELECT s.inst_id, s.sid, s.serial#, s.username, s.prev_sql_id, t.start_time
  FROM gv$transaction t
  JOIN gv$session s ON s.inst_id = t.inst_id AND s.taddr = t.addr
 ORDER BY t.start_time;

결과 해석:

업무 담당자가 변경 확정을 승인한 경우 해당 트랜잭션을 연 재현 세션에서 커밋함

SQL*Plus (1번 노드, 트랜잭션을 연 실습 세션)객체 또는 설정을 변경함
COMMIT;

결과 해석:

SQL*Plus (SYSDBA, 1번 노드)인스턴스 또는 서비스 중단이 필요함
ALTER SYSTEM KILL SESSION '<SID>,<SERIAL#>,@1' IMMEDIATE;

결과 해석:

점검과 조치 기준
TX 락 대기 없이 조회만 느림 미커밋 Write → Read로 CR 블록 전송이 반복되는지 확인
미커밋 트랜잭션 추적 GV$TRANSACTION으로 클러스터 전체 조회
트랜잭션 종료와 프로시저 수정 세션을 끊으면 그 변경은 롤백됨
커밋해도 되는지 업무 확인 뒤 판단하고 프로시저에 COMMIT을 넣음

예상값: 2번 노드 세션의 gc cr blocks received가 조회할 때마다 늘고, GV$TRANSACTION에 INST_ID 1, <SCHEMA>의 오래 열린 트랜잭션이 나옴

수업 자료의 정리 단계는 UPDATE emp SET comm = comm - 100 WHERE deptno = 30 후 커밋인데, 세션을 끊어 변경이 롤백된 경우에는 이 문장이 원래 값을 100씩 줄이고, 커밋한 경우에도 원래 NULL이던 comm은 0으로 남음
정리 전에 실제로 커밋됐는지와 원래 값을 확인함

실행 결과 확인 불가

SEO_HKabsumveritas@gmail.comBuilt with Claude Code & Codex

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

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