서비스와 로드 밸런싱
RAC 서비스의 Preferred와 Available 배치, PDB 서비스와 Scheduler 작업 및 접속 부하 분산
#핵심 요약
서비스는 같은 업무를 하는 세션을 묶는 논리적 접속 단위이며, 애플리케이션은 인스턴스 이름 대신 SCAN과 서비스 이름으로 접속함
업무마다 주 실행 인스턴스(Preferred)를 나눠 두면 같은 블록을 노드끼리 주고받는 일이 줄어 Cache Fusion과 Interconnect 부담이 줄어듦
장애 때는 대체 인스턴스(Available)가 서비스를 넘겨받음
srvctl add service -pdb로 PDB에 연결된 업무용 서비스를 만들며, PDB 기본 서비스는 Preferred, Available, TAF, FAN 정책을 걸기 어려움
Scheduler 잡 클래스에 서비스를 지정하면 그 클래스의 잡은 서비스가 떠 있는 노드에서 실행됨
클라이언트 로드 밸런싱은 주소 선택을 분산하고 서버측 로드 밸런싱은 등록된 서비스, 인스턴스 상태와 부하를 이용함
#개념과 동작 원리
| 구분 | 역할 |
|---|---|
| 인스턴스 | <INSTANCE1>, <INSTANCE2>처럼 DB를 실제로 실행하는 단위 |
| 서비스 | 애플리케이션 접속에 쓰는 업무용 논리 이름, 하나 또는 여러 인스턴스에서 실행 가능 |
| SCAN | 개별 노드 주소 대신 쓰는 RAC 진입점 |
| SERVICE_NAME | 접속할 업무 서비스 지정 |
노드를 늘리면 CPU와 메모리는 늘지만, 같은 블록을 여러 노드가 번갈아 쓰면 노드 1 캐시의 블록을 노드 2가 Interconnect로 받아 가는 Cache Fusion 전송도 늘어남
그래서 노드 수가 두 배가 돼도 처리 능력이 단순히 두 배가 되지는 않음
같은 업무의 세션을 한 인스턴스에 모으면 그 업무 블록을 로컬 Buffer Cache에서 다시 쓸 가능성이 커지고, 다른 노드에서 블록을 받는 횟수가 줄어듦
수업 자료의 정리: RAC의 장점은 고가용성과 확장성, 단점은 Interconnect 부하로 인한 성능 저하 가능성이며, 서비스로 업무별 세션과 실행 위치를 나눠 단점을 줄임
| 구분 | 역할 |
|---|---|
| Preferred | 정상 상태의 주 실행 인스턴스, 캐시 친화도와 운영 정책 유지 |
| Available | Preferred 장애 때 서비스를 넘겨받는 대체 인스턴스 |
| 구분 | 특성 |
|---|---|
| PDB 기본 서비스 | PDB를 열면 자동 등록, Preferred, Available과 TAF, FAN 정책 적용에 제약 |
| 업무용 서비스 | srvctl로 따로 만들어 운영 접속과 서비스 정책 관리에 씀 |
| 관리 기능 | 서비스와의 관계 |
|---|---|
| Scheduler | 잡 클래스의 service 속성으로 서비스 지정, 잡은 job_class로 클래스 지정 |
| Resource Manager | 서비스나 세션 그룹별 CPU 우선순위 제어 CPU가 남으면 더 쓸 수 있고 경합할 때 설정한 소비자 그룹의 우선순위와 제한이 적용됨 |
| 부하 분산 | 설명 |
|---|---|
| 서비스 친화도 | 데이터 접근 패턴, 캐시 재사용, 응답시간과 부하 분포를 함께 고려 |
| 클라이언트 분산 | LOAD_BALANCE=ON으로 주소 목록의 접속 대상 선택 |
| 서버측 분산 | 리스너에 등록된 인스턴스와 서비스 정보를 이용해 신규 접속 분산 |
| LREG | 12c 이후 로컬, 원격 리스너에 서비스와 인스턴스 상태 동적 등록 |
| REMOTE_LISTENER | 잘못 설정되면 원격 리스너에 서비스가 등록되지 않아 분산과 장애 대응에 영향 |
서비스 배치 효과는 배치 전후 응답시간과 세션 분포, gc 대기와 원격 블록 수신량으로 비교함(Cache Fusion)
#필수 명령어 또는 SQL
#업무별 서비스 만들기
srvctl add service -db <DB_NAME> -service GL -preferred <INSTANCE1> -available <INSTANCE2>
srvctl add service -db <DB_NAME> -service AP -preferred <INSTANCE2> -available <INSTANCE1>
srvctl start service -db <DB_NAME> -service GL
srvctl start service -db <DB_NAME> -service AP
srvctl status service -db <DB_NAME>
lsnrctl status결과 해석:
수업 자료는 짧은 옵션(-d <DB_NAME> -s GL -r <INSTANCE1> -a <INSTANCE2>)으로도 적었으며 -s는 서비스 이름, -r은 Preferred, -a는 Available임
실습 기록: Service GL is running on instance(s) <INSTANCE1>, Service AP is running on instance(s) <INSTANCE2>로 나왔고, <NODE1> 리스너에는 GL, <NODE2> 리스너에는 AP가 등록됐음
GL 별칭으로 접속한 세션의 v$instance는 <INSTANCE1>이었음
접속 별칭은 HOST에 SCAN 이름, SERVICE_NAME에 업무 서비스를 적음
GL =
(DESCRIPTION =
(FAILOVER = on)
(LOAD_BALANCE = on)
(ADDRESS = (PROTOCOL = TCP)(HOST = <SCAN_NAME>)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = GL)
(FAILOVER_MODE = (TYPE = SELECT)(METHOD = BASIC)(RETRIES = 10)(DELAY = 1))))결과 해석:
실습에서는 기본 서비스 별칭으로 두 번 접속하자 <INSTANCE1>, <INSTANCE2>로 번갈아 붙었고, Preferred <INSTANCE1>, Available <INSTANCE2>로 만든 order 서비스로는 <INSTANCE1>에 붙었음
수업 자료의 "무조건 1번 노드 접속"은 정상 상태의 설명이며 <INSTANCE1> 장애 때는 Available인 <INSTANCE2>로 넘어감
#PDB에 업무용 서비스 붙이기
srvctl add service -db <DB_NAME> -service svc_hr -pdb <PDB_NAME> -preferred <INSTANCE1> -available <INSTANCE2>
srvctl add service -db <DB_NAME> -service svc_rpt -pdb <PDB_NAME> -preferred <INSTANCE2> -available <INSTANCE1>
srvctl start service -db <DB_NAME> -service svc_hr,svc_rpt
srvctl config service -db <DB_NAME> -service svc_hr
srvctl status service -db <DB_NAME>crsctl stat res -t | grep svc
lsnrctl status | grep -i svcSELECT inst_id, name, pdb FROM gv$services WHERE name LIKE 'svc%' ORDER BY name, inst_id;
-- 업무 서비스로 접속한 세션에서
SELECT SYS_CONTEXT('userenv', 'con_name') AS pdb,
SYS_CONTEXT('userenv', 'instance_name') AS inst,
SYS_CONTEXT('userenv', 'service_name') AS svc
FROM dual;결과 해석: srvctl config service의 Pluggable database name이 연결된 PDB임
실습 기록: svc_hr은 <INSTANCE1>, svc_rpt는 <INSTANCE2>에서 실행됐고, svc_hr로 접속한 세션은 PDB1 / <INSTANCE1> / svc_hr로 나왔음
이 서비스의 장애 전환은 접속 장애 전환에 있음
#Scheduler 잡을 서비스 노드에서 실행
| 서비스 | Preferred | 잡 클래스 | 잡 |
|---|---|---|---|
| HOT_BATCH_SERV | <INSTANCE1> |
HOT_BATCH_CLASS | JOB1, JOB2 |
| LOW_BATCH_SERV | <INSTANCE2> |
LOW_BATCH_CLASS | JOB3 |
srvctl add service -db <DB_NAME> -service HOT_BATCH_SERV -preferred <INSTANCE1> -available <INSTANCE2>
srvctl add service -db <DB_NAME> -service LOW_BATCH_SERV -preferred <INSTANCE2> -available <INSTANCE1>
srvctl start service -db <DB_NAME> -service HOT_BATCH_SERV
srvctl start service -db <DB_NAME> -service LOW_BATCH_SERV
lsnrctl status | grep -E "HOT_BATCH|LOW_BATCH"BEGIN
DBMS_SCHEDULER.CREATE_JOB_CLASS(
job_class_name => 'HOT_BATCH_CLASS',
service => 'HOT_BATCH_SERV',
comments => '무거운 배치 작업 클래스');
END;
/
BEGIN
DBMS_SCHEDULER.CREATE_JOB(
job_name => 'JOB1',
job_type => 'PLSQL_BLOCK',
job_action => 'BEGIN
INSERT INTO <USERNAME>.batch_log(job_name, inst_name, message)
VALUES (''JOB1'', SYS_CONTEXT(''USERENV'',''INSTANCE_NAME''), ''HOT_BATCH 작업 실행 완료'');
COMMIT;
END;',
job_class => 'HOT_BATCH_CLASS',
repeat_interval => 'FREQ=MINUTELY; INTERVAL=2',
enabled => TRUE);
END;
/
EXEC DBMS_SCHEDULER.RUN_JOB('JOB1', use_current_session => FALSE);SELECT inst_id, name, network_name, goal, clb_goal
FROM gv$active_services
WHERE name IN ('HOT_BATCH_SERV', 'LOW_BATCH_SERV')
ORDER BY name, inst_id;
SELECT job_class_name, service FROM dba_scheduler_job_classes
WHERE job_class_name IN ('HOT_BATCH_CLASS', 'LOW_BATCH_CLASS');
SELECT job_name, instance_id, status, actual_start_date, run_duration, error#
FROM dba_scheduler_job_run_details
WHERE job_name IN ('JOB1', 'JOB2', 'JOB3')
ORDER BY actual_start_date DESC;결과 해석:
실습은 batch_log 테이블에 잡이 실행된 인스턴스 이름을 남겨 확인함
잡 실행 이력의 INSTANCE_ID로도 볼 수 있음
실습에서는 srvctl stop instance -d <DB_NAME> -i <INSTANCE1>로 <INSTANCE1>을 내려(안 내려가면 SHUTDOWN ABORT) HOT_BATCH_SERV가 <INSTANCE2>로 옮겨 간 것을 srvctl status service로 확인했음
그 뒤 JOB1이 <INSTANCE2>에서 실행되는지 보는 단계의 결과는 기재 없음
원래대로: srvctl start instance -d <DB_NAME> -i <INSTANCE1> 뒤 srvctl relocate service -d <DB_NAME> -s HOT_BATCH_SERV -oldinst <INSTANCE2> -newinst <INSTANCE1>
정리: DBMS_SCHEDULER.DROP_JOB, DROP_JOB_CLASS, 로그 테이블 삭제, srvctl stop service, srvctl remove service
#서비스별 접속 분포
SHOW PARAMETER remote_listener
SELECT s.service_name, s.inst_id, COUNT(*) AS session_count
FROM gv$session s
WHERE s.service_name IN ('<SERVICE_NAME_1>', '<SERVICE_NAME_2>')
GROUP BY s.service_name, s.inst_id
ORDER BY s.service_name;결과 해석: 서비스별 인스턴스 접속 분포 확인이 목적이며 집계 결과 행은 기재 없음
#사용 조건과 주의사항
서비스 친화도 실습에서는 MMNL을 RAC 전용, 서비스 자동 배치 담당으로 설명하면서 같은 본문에서 그 주장에 근거 확인이 필요하다고 적었음
서버 분산 실습의 반복 스크립트는 10회인데 부하 전 분포는 노드별 25회라고 적어 서로 맞지 않음
정적 등록 항목만으로 로드 밸런싱 불가를 단정하는 설명과 GOAL_NONE을 모든 접속 분산의 비활성화로 보는 설명은 실습 자체의 주의와 함께 보류함
Clusterware가 관리하는 서비스는 srvctl 정의가 기준이며, DBMS_SERVICE로 바꾼 값은 재기동 때 덮어써질 수 있음
인스턴스를 내려 서비스 전환을 보는 단계는 그 인스턴스의 모든 세션을 끊으므로 실습 환경에서만 함