데이터베이스 연구실.log

Interconnect와 RAC 대기

GV$ 뷰로 좁히는 RAC 대기 진단, 인터커넥트의 MTU와 오류 및 블록 수신 시간 점검

T_ORDER_SEQrow_wait_obj#reniceInterconnectGV$Cluster 대기 클래스gc cr requestgc current block busygc buffer busy acquireHAIPoifcfgMTUUDPLMSGV$CLUSTER_INTERCONNECTS

#핵심 요약

V$ 뷰는 접속한 인스턴스 정보만, GV$ 뷰는 INST_ID를 포함한 전체 인스턴스 정보를 보여 줌
한 노드 세션의 대기 원인이 다른 노드의 블록이나 락일 수 있어 RAC에서는 GV$로 비교함

추적 순서: 대기 이벤트와 클래스(GV$SYSTEM_EVENT, GV$SESSION.EVENT) → 인스턴스(INST_ID) → 세션과 SQL(GV$SESSION.SQL_ID → GV$SQL) → 경합 자원(ROW_WAIT_OBJ# → DBA_OBJECTS)

두 노드가 같은 블록의 서로 다른 행을 바꾸면 TX 락 대기는 없지만 블록을 서로 가져가야 해서 gc 계열 대기(Cluster 클래스)가 생김

인터커넥트는 노드 사이의 사설 네트워크로 Cache Fusion 블록, 글로벌 자원 메시지, 노드 상태 통신에 쓰임
패킷 지연이나 손실이 있으면 gc 대기가 음

실습 점검 순서: 대역폭, 프로토콜 → MTU(양쪽과 스위치 일치) → 오류, 지연(UDP 수신 오류, 블록 평균 수신 시간) → LMS 프로세스의 CPU 확보

#개념과 동작 원리

RAC 네트워크 구성(Public, Private, VIP, SCAN) 자체는 고가용성과 재해복구 분야에서 다룸

네트워크 오가는 데이터 쓰는 주소
Public 업무 트래픽(SQL 요청과 결과) Public IP, VIP, SCAN IP
Private 클러스터 트래픽(Cache Fusion, 하트비트) Private IP
점검 항목 내용
대역폭 1Gb, 10Gb Ethernet, InfiniBand 등 실제 링크 속도
프로토콜 설명: Linux RAC는 UDP, Exadata는 RDS
MTU 기본 1500, 점보 프레임 예 9000
양쪽 노드와 경유 스위치가 같아야 함
오류, 지연 UDP 수신 오류, 재조립 실패, 블록 평균 수신 시간
LMS CPU 네트워크가 정상이어도 LMS 스케줄링이 늦으면 gc 대기 증가 가능

실습에서는 11gR2 이후 Private NIC에 169.254.x.x 대역의 HAIP 주소가 자동으로 붙어 인터커넥트 통신에 쓰인다고 적었음
대기 이벤트와 대기 클래스 일반은 세션과 블로킹 진단, 블록 상태는 Cache Fusion과 블록 전송에 있음

#필수 명령어 또는 SQL

#V$와 GV$ 비교, 인스턴스별 대기

SQL*Plus (SYSDBA)조회만 함
SELECT inst_id, instance_name, host_name, status FROM gv$instance ORDER BY inst_id;

SELECT *
  FROM (SELECT inst_id, event, wait_class,
               ROUND(time_waited_micro / 1000000) AS wait_sec,
               RANK() OVER (PARTITION BY inst_id ORDER BY time_waited_micro DESC) AS rk
          FROM gv$system_event
         WHERE wait_class <> 'Idle')
 WHERE rk <= 5
 ORDER BY inst_id, rk;

SELECT inst_id, wait_class, ROUND(SUM(time_waited_micro) / 1000000) AS wait_sec
  FROM gv$system_event
 WHERE wait_class IN ('Cluster', 'User I/O', 'Concurrency', 'Commit')
 GROUP BY inst_id, wait_class
 ORDER BY inst_id, wait_sec DESC;

결과 해석: GV$SYSTEM_EVENT는 기동 후 누적값이라 현재 구간을 보려면 두 시점의 차이나 GV$SESSION의 현재 대기를 봄
같은 이벤트의 노드별 편차가 크면 그 노드의 부하와 경합을 확인함

#Hot block 경합 추적

실습에서는 두 행이 한 블록에 들어가는 t_hot을 만들고 1번 노드는 id = 1, 2번 노드는 id = 2를 20만 번씩 UPDATE, COMMIT했음

SQL*Plus (SYSDBA)조회만 함
SELECT inst_id, event, wait_class, COUNT(*)
  FROM gv$session
 WHERE username = '<SCHEMA>' AND state = 'WAITING'
 GROUP BY inst_id, event, wait_class
 ORDER BY inst_id;

SELECT s.inst_id, s.sql_id, q.sql_text, o.object_name
  FROM gv$session s
  JOIN gv$sql q
    ON q.inst_id = s.inst_id AND q.sql_id = s.sql_id AND q.child_number = s.sql_child_number
  LEFT JOIN dba_objects o ON o.object_id = s.row_wait_obj#
 WHERE s.username = '<SCHEMA>'
   AND s.wait_class = 'Cluster' AND s.state = 'WAITING';

결과 해석:

실습 결과: 1번 노드 gc current block busy, 2번 노드 gc buffer busy acquire(실행 시점마다 다를 수 있음), 원인 SQL은 양쪽 모두 UPDATE T_HOT..., 오브젝트 T_HOT

#인터커넥트 경로와 프로토콜

OS (oracle 사용자)조회만 함
oifcfg getif

결과 해석:

실습 결과: 공용 인터페이스는 public, 사설 인터페이스는 cluster_interconnect,asm으로 표시되었음

SQL*Plus (SYSDBA)조회만 함
SELECT inst_id, name, ip_address, is_public, source
  FROM gv$cluster_interconnects
 ORDER BY inst_id;

결과 해석:

실습 결과: 두 인스턴스 모두 사설 NIC의 별칭 인터페이스에 HAIP 주소(169.254 대역), IS_PUBLIC = NO
실습에서는 이어서 ORADEBUG SETMYPID, ORADEBUG IPC, ORADEBUG TRACEFILE_NAME으로 트레이스를 남기고 파일에서 udp를 찾아 프로토콜을 확인했음
ORADEBUG는 진단용 내부 명령이라 사용 조건은 기재 없음

#MTU, 오류, 블록 수신 시간

OS (oracle 사용자, 두 노드)조회만 함
ip link show <PRIVATE_NIC> | grep mtu
ping -c 3 -M do -s 1472 <PRIVATE_HOST>
netstat -su | grep -i -E "receive errors|buffer errors"

결과 해석: -M do는 조각화를 금지해 1472 + 헤더 28 = 1500바이트 패킷이 그대로 가는지 봄(점보 프레임이면 -s 8972)
실패하면 경로의 MTU와 ICMP 허용을 확인하고, UDP 오류 값이 계속 늘면 점검 대상임

SQL*Plus (SYSDBA)조회만 함
SELECT inst_id,
       ROUND(SUM(DECODE(name, 'gc cr block receive time', value)) * 10 /
             NULLIF(SUM(DECODE(name, 'gc cr blocks received', value)), 0), 2) AS avg_cr_ms,
       ROUND(SUM(DECODE(name, 'gc current block receive time', value)) * 10 /
             NULLIF(SUM(DECODE(name, 'gc current blocks received', value)), 0), 2) AS avg_cur_ms
  FROM gv$sysstat
 WHERE name IN ('gc cr block receive time', 'gc cr blocks received',
                'gc current block receive time', 'gc current blocks received')
 GROUP BY inst_id
 ORDER BY inst_id;

결과 해석:

주석: receive time 통계는 1/100초 단위라 10을 곱해 ms로 바꿈(확인 불가)

#LMS 개수

SQL*Plus (SYSDBA)조회만 함
SHOW PARAMETER cpu_count
SHOW PARAMETER gcs_server_processes
SELECT inst_id, pname FROM gv$process WHERE pname LIKE 'LMS%' ORDER BY 1, 2;

결과 해석:

SQL*Plus (SYSDBA)객체 또는 설정을 변경함
ALTER SYSTEM SET gcs_server_processes = 4 SCOPE = SPFILE SID = '*';
OS (oracle 사용자)인스턴스 또는 서비스 중단이 필요함
srvctl stop database -db <DB_NAME>
srvctl start database -db <DB_NAME>
SQL*Plus (SYSDBA)조회만 함
SELECT inst_id, COUNT(*) AS lms_count
  FROM gv$process WHERE pname LIKE 'LMS%'
 GROUP BY inst_id ORDER BY inst_id;

결과 해석:

자동 산정 값 복원은 SPFILE 설정을 지운 뒤 다시 재기동하고 실제 프로세스 개수를 확인해야 완료됨
인스턴스별로 별도 설정한 값이 있으면 그것도 확인함

SQL*Plus (SYSDBA)객체 또는 설정을 변경함
ALTER SYSTEM RESET gcs_server_processes SCOPE = SPFILE SID = '*';
OS (oracle 사용자)인스턴스 또는 서비스 중단이 필요함
srvctl stop database -db <DB_NAME>
srvctl start database -db <DB_NAME>
SQL*Plus (SYSDBA)조회만 함
SELECT inst_id, value FROM gv$parameter WHERE name = 'gcs_server_processes';
SELECT inst_id, COUNT(*) AS lms_count
  FROM gv$process WHERE pname LIKE 'LMS%'
 GROUP BY inst_id ORDER BY inst_id;

결과 해석:

gcs_server_processes를 정하지 않으면 CPU 수로 LMS 개수를 정함
수업 자료 기준: 13코어 1개, 415코어 2개, 16~127코어 2 + CPU_COUNT/32(소수점 버림), 128코어 이상이면서 SGA 100GB 이상이면 CPU_COUNT/6
예: 64코어는 2 + 64/32 = 4개

정적 파라미터라 SPFILE에만 쓰고 DB를 다시 시작해야 적용되며, SID = '*'로 두 인스턴스에 같이 줌

실습 기록: 4로 바꾸고 재기동하자 두 노드 모두 LMS0~`LMS3`이 떴음
자동 산정으로 돌아가도록 RESET했으나, 복원 재기동 뒤 실제 LMS 개수는 확인 불가

LMS를 늘리기 전에 gc 대기와 LMS의 CPU 사용률을 먼저 보며, 프로세스가 너무 많으면 CPU 경합이 생길 수 있음

#LMS 우선순위

OS (oracle 사용자)조회만 함
ps -ef | grep lms | grep -v grep
ps -o pid,cls,ni,pri,cmd -p <LMS_PID>
OS (root)객체 또는 설정을 변경함
renice -20 -p <LMS_PID>

결과 해석:

주의: nice 값이 낮을수록 우선순위가 높고 음수 조정에는 root가 필요함
renice는 현재 프로세스에만 적용되어 인스턴스를 재시작하면 원래대로 돌아감
LMS가 실시간 클래스(CLS=RR)로 도는 환경이면 스케줄링 클래스를 따로 확인함

#확인 문제: 주문 접수가 느려진 원인 찾기

상황: 주문 서버가 두 노드에 붙어 있고 CPU는 한가한데 주문 접수가 느려짐
어떤 대기 때문에, 어느 노드에서, 어떤 SQL과 테이블이 원인인지 밝혀야 함

SQL*Plus (실습 계정, 1번 노드 한 창에서 준비)객체 또는 설정을 변경함
CREATE TABLE t_order_seq (branch_id NUMBER PRIMARY KEY, last_no NUMBER);
INSERT INTO t_order_seq SELECT level, 0 FROM dual CONNECT BY level <= 4;
COMMIT;

결과 해석:

준비가 끝난 뒤 아래 갱신 블록만 두 노드에서 동시에 실행함

SQL*Plus (실습 계정, 두 노드에서 동시 갱신)객체 또는 설정을 변경함
-- 1번 노드: 지점 1, 2 / 2번 노드: mod(i,2) + 3 으로 지점 3, 4
BEGIN
  FOR i IN 1..200000 LOOP
    UPDATE t_order_seq SET last_no = last_no + 1 WHERE branch_id = mod(i,2) + 1;
    COMMIT;
  END LOOP;
END;
/
SQL*Plus (SYSDBA, 1번 노드)조회만 함
SELECT inst_id, wait_class, event, COUNT(*)
  FROM gv$session
 WHERE username = '<SCHEMA>' AND state = 'WAITING'
 GROUP BY inst_id, wait_class, event
 ORDER BY inst_id;

SELECT s.inst_id, s.sql_id, q.sql_text
  FROM gv$session s
  JOIN gv$sql q
    ON q.inst_id = s.inst_id AND q.sql_id = s.sql_id AND q.child_number = s.sql_child_number
 WHERE s.username = '<SCHEMA>' AND s.wait_class = 'Cluster' AND s.state = 'WAITING';

SELECT DISTINCT s.inst_id, o.owner, o.object_name
  FROM gv$session s
  JOIN dba_objects o ON o.object_id = s.row_wait_obj#
 WHERE s.username = '<SCHEMA>' AND s.wait_class = 'Cluster' AND s.state = 'WAITING';

결과 해석:

예상값: 대기 클래스가 Cluster인 gc 계열 대기, 원인 오브젝트 T_ORDER_SEQ

노드마다 갱신하는 행은 다르지만 네 행이 같은 블록에 있어 블록이 노드 사이를 계속 오감
같은 블록을 갱신하는 작업을 한 노드로 모으거나, 노드별 갱신 대상이 서로 다른 블록에 저장되도록 데이터 배치를 분리하는 방안을 검토함
지점별 접속 노드만 나누고 블록을 공유하면 블록 왕복이 남음

CPU가 한가해도 세션의 Cluster 대기 시간이 길면 RAC 경합을 의심하며, 2번 노드의 대기 세션과 SQL도 GV$로 함께 봄

실행 결과 확인 불가
실습이 끝나면 DROP TABLE t_order_seq PURGE로 지움

#확인 문제: 2번 노드 사용자만 조회가 느린 원인

상황: 2번 노드에 붙은 사용자만 느리고, 전날 서버 담당자가 2번 노드에서 프로세스 우선순위를 만졌으나 무엇을 바꿨는지 모름
실습에서는 root로 2번 노드의 모든 LMS를 renice 19(가장 낮은 우선순위)로 바꿔 이 상황을 재현함

OS (2번 노드, root)객체 또는 설정을 변경함
for p in $(pgrep -f "_lms"); do renice 19 -p $p; done

결과 해석:

점검 순서: 인터커넥트 경로와 프로토콜 → MTU와 조각화 → 노드별 평균 블록 수신 시간 → LMS 우선순위

OS (2번 노드, oracle)조회만 함
oifcfg getif
ip link show <PRIVATE_NIC> | grep mtu      # 1번 노드에서도 같은 명령으로 비교
ping -c 3 -M do -s 1472 <RAC1_PRIVATE_HOST>
ps -o pid,cls,ni,cmd -p $(pgrep -d, -f _lms)
SQL*Plus (SYSDBA, 2번 노드)조회만 함
SELECT inst_id, name, ip_address FROM gv$cluster_interconnects;

결과 해석:

노드별 평균 블록 수신 시간은 위 "MTU, 오류, 블록 수신 시간" 절의 GV$SYSSTAT 쿼리를 그대로 씀

OS (2번 노드, root)객체 또는 설정을 변경함
for p in $(pgrep -f "_lms"); do renice -20 -p $p; done
ps -o pid,cls,ni,cmd -p $(pgrep -d, -f _lms)

결과 해석:

예상값: 인터커넥트는 사설 대역을 쓰고 MTU도 같은데 2번 노드의 블록 수신 시간만 길며, LMS의 NI가 19로 나옴
복원 뒤 NI -20 확인

경로와 MTU가 정상인데 한 노드의 블록 수신만 느리면 LMS 스케줄링 지연을 의심함

renice로 바꾼 값은 인스턴스를 다시 시작하면 사라지므로, 재기동 뒤에도 같은 증상이 나오면 우선순위를 바꾸는 자동 실행 스크립트가 있는지 찾음

복원 값 -20은 수업 자료의 기대값이며, LMS가 실시간 스케줄링 클래스로 도는 환경에서 nice 값이 실제로 쓰이는지는 확인 불가

실행 결과 확인 불가

SEO_HKabsumveritas@gmail.comBuilt with Claude Code & Codex

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

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