상세 컨텐츠

본문 제목

2024년 3월 7일

카테고리 없음

by 병아리 엔지니어 2024. 3. 7. 17:16

본문

더보기

양쪽 노드로 접속하기

[oracle@rac1 ~]$ . oraenv
ORACLE_SID = [racdb1] ?
The Oracle base for ORACLE_HOME=/u01/app/oracle/product/11.2.0/dbhome_1 is /u01/app/oracle

 

[oracle@rac1 ~]$ cd $ORACLE_HOME/sqlplus/admin

 

[oracle@rac1 admin]$ ls

[oracle@rac1 admin]$ vi glogin.sql

 

문서 열어서

맨 아랫부분에 이 문장 추가하기

SET SQLPROMPT '&_USER.@&_CONNECT_IDENTIFIER.> '

 

[oracle@rac2 ~]$ . oraenv
ORACLE_SID = [racdb2] ?
The Oracle base for ORACLE_HOME=/u01/app/oracle/product/11.2.0/dbhome_1 is /u01/app/oracle

 

 

[oracle@rac2 ~]$ cd $ORACLE_HOME/sqlplus/admin

 

[oracle@rac2 admin]$ ls
glogin.sql  help  libsqlplus.def  plustrce.sql  pupbld.sql

[oracle@rac2 admin]$ vi glogin.sql

 

칠판 그림 (사진 9:51, 9:54)

 

★ GRD (Global Resource Directory)

- 어느 노드에 최신 데이터가 있는지에 대한 위치 정보와 락 정보를

  Shared Pool 에서 관리한다.

 

# GRD 가 관리하는 서비스

1. GCS (Global Cache Service): 노드 간의 데이터를 전송하는 서비스

    데몬 이름 (= 프로세스 이름) : LMS

    오라클 권장 값은 CPU 개수가 4개마다 1개 LMS 프로세스를 사용하는 것

 

양쪽 노드에서 SQL+ 로 접속하기

 

양쪽 노드 모두에서 프로세스 확인해보기

show parameter gcs_server_processes

 

2. GES (Global Equeue Service) : 노드 간에 발생하는 락을 관리하는 서비스

    데몬 (프로세스) : LMD, LCK

 

3. CGS (Cluster Grop Service) : 클러스터의 멤버십을 관리

                                                    클러스터를 모니터링하면서 노드의 가입이나 탈퇴에 따른 클러스터의 상태를 관리한다.

    데몬: LMON

 

데몬이 떠 있는지 확인해보자.

SYS@racdb1> ! ps -ef | grep racdb1

 

★ 캐시 퓨전 Cache Fusion

- 인터커넥트를 통한 효율적인 글로벌 버퍼 동기화 매커니즘

- 디스크가 아닌, 인터커넥트를 통한 블록 전송과 메모리 동기화가 캐시퓨전의 핵심이다.

- request node: master node 에게 블록 전송을 요청하는 노드

- master node: 블록의 최신 정보를 관리하는 노드 / 블록의 최신 정보는 master node 의 GRD 에 저장된다.

- holder node: 최신 블록 이미지 block image 를 가지고 있는 노드


 

HR 유저의 계정 상태를 확인해보면: EXPIRED & LOCKED 상태라고 나온다.

SYS@racdb1> select username, account_status from dba_users where username = 'HR';

USERNAME                       ACCOUNT_STATUS
-------------------------------------------------------------------
HR                                     EXPIRED & LOCKED

 

해결책: 패스워드 바꾸기 & 락 해제

(한쪽 노드에서만 하면 된다. 노드는 달라도 DB는 공통으로 사용하고 있기 때문)

 

패스워드 바꾸기 & 락 해제

SYS@racdb1> alter user hr identified by hr account unlock;
User altered.

 

락이 진짜로 해제되었는지 확인해보기

SYS@racdb1> select username, account_status from dba_users where username = 'HR';

USERNAME                       ACCOUNT_STATUS
-------------------------------------------------------------------
HR                                               OPEN

SYS@racdb1> select
dbms_rowid.rowid_relative_fno(rowid) as file_no,
dbms_rowid.rowid_block_number(rowid) as block_no
from hr.employees
where employee_id = 100;  2    3    4    5

   FILE_NO   BLOCK_NO
-----------------------------------
         5               207


칠판 그림 (사진 10:26)

 

★ 첫번째 시나리오 ★

 

# master node : rac2

# request node : rac1

 

1. rac1 노드에서 처음으로 (어느 노드에도 그 블록이 올라와 있지 않은 상태에서)

    블록을 select 요청했을 때

 

1) rac1 노드 사용자가 [5, 207] 블록을 읽기 요청한다.

    rac1 은 해당 블록을 null 모드로 획득한다.

2) rac1 노드 사용자가 [5, 207] 블록의 마스터 노드인 rac2 에 블록 전송을 요청한다.

    프로세스에는 요청 후 응답이 올 때까지 gc cr request 라는 대기이벤트가 발생한다.

3) 요청받은 rac2 쪽에서는 GRD 를 통해

    현재 어떤 인스턴스도 [5, 207] 블록 이미지를 버퍼 캐시에 가지고 있지 않다는 것을 확인한다.

    ra2 번은 rac1 번에게 블록을 공유 모드 shared mode 로 읽을 권한을 부여한다.

    gc cr / current grant 2-way (2-way 는 이중화)

    gc cr / current grant 는 마스터 노드로부터 권한을 받을 때까지 뜨는 대기 이벤트인데

    이게 너무 과도하게 발생하는 것 = 네트워크 성능이 떨어지는 것

4) rac1 번은 받은 응답에 대해 [5, 207] 블록을 디스크에서 읽어들이며

    디스크 작업 (올리는 작업) 이 완료될 때까지 DB file sequential read 이벤트로 대기한다.

5) rac2 번은 [5, 207] 블록에 대해서 rac1 번 S(shared) 모드로 가지고 있다는 정보를 GRD 에 갱신한다.

    (4번과 5번은 한꺼번에 돌아간다)


2교시

칠판 그림 (~11:27)

 

SYS@racdb1> select
dbms_rowid.rowid_relative_fno(rowid) as file_no,
dbms_rowid.rowid_block_number(rowid) as block_no
from hr.employees
where employee_id = 100;  2    3    4    5

   FILE_NO   BLOCK_NO
-----------------------------------
         5               207

 

현재 메모리에 올라와 있는 정보 확인 (양쪽 노드에서 모두 확인)

 

SYS@racdb1> select b.lock_element_addr, b.status, e.mode_held, e.local
from v$bh b, v$gc_element e
where b.lock_element_addr = e.gc_element_addr
and b.file#=5 and b.block#=207;  2    3    4
no rows selected

 

SYS@racdb2> select b.lock_element_addr, b.status, e.mode_held, e.local
from v$bh b, v$gc_element e
where b.lock_element_addr = e.gc_element_addr
and b.file#=5 and b.block#=207;  2    3    4
no rows selected

 

찾고자 하는 블록이 1번 노드에도 없고 2번 노드에도 없다고 나온다.

 

100번 사원의 월급 찾기 (1번 노드에서 수행)

SYS@racdb1> select salary from hr.employees where employee_id = 100;

 

    SALARY
----------
     24000

 

현재 메모리에 올라와 있는 정보 다시 확인 (양쪽 노드에서 모두 확인)

 

SYS@racdb1> select b.lock_element_addr, b.status, e.mode_held, e.local
from v$bh b, v$gc_element e
where b.lock_element_addr = e.gc_element_addr
and b.file#=5 and b.block#=207;  2    3    4

# mode_held: 다른 쪽에서 가져오기 위해 공간을 확보한 것

                       0 (null) 1 (shared (select)), 2(exclusive(dml))

# local: 1 (local), 0 (global - 다른 쪽에도 블록이 있는 것)

 

SYS@racdb2> select b.lock_element_addr, b.status, e.mode_held, e.local
from v$bh b, v$gc_element e
where b.lock_element_addr = e.gc_element_addr
and b.file#=5 and b.block#=207;  2    3    4
no rows selected

 

만약 다른 쪽 노드에 GRD가 있는데

다른 한 쪽 노드에서 자주 employees 테이블을 조회한다면

오라클은 다른 쪽 노드에 있는 GRD 를 자주 조회하는 쪽 노드에 넘겨주기도 한다.


★ 두번째 시나리오 ★

 

100번 사원의 정보를 조회하고자 하는 유저가 rac2 쪽에 접속했다고 하자.

그런데 rac2 쪽에는 그런 정보를 DBC 에 가지고 있지 않다.

(GRD 에서 찾아보니까 해당 정보는 rac1 에서 가지고 있다면?

rac2 는 rac1에게 자기 쪽으로 데이터를 전송해달라고 요청한다)

 

2. select - select

[5, 207] 블록이 rac1 번 노드의 buffer cache 에 있는 상태에서

rac2 번 노드에서 해당 블록에 select 하려고 한다.

(rac2 번 노드는 해당 블록을 DBC에 가지고 있지 않다)

 

1) rac2 노드는 100번 사원의 데이터 master node 인 rac2 node 에게 100번 사원의 데이터 블록 상태를 확인한다.

2) rac2 노드의 요청을 받은 master node 는 해당 블록이 rac1 node 의 DBC 에 있다는 것을 알고

    rac1 노드의 해당 블록을 rac2 노드에게 보낼 것을 지시한다.

    (이때도 마찬가지로 gc cr request 대기이벤트가 발생할 수 있다)

3) 요청받은 rac1 노드는 해당 블록을 interconnect 를 통해

     rac2 노드에게 전송하게 되며, 이 상황을 select (읽기) - select (읽기) 캐시 퓨전이라고 한다.

     (이때도 gc  cr / current grant 2-way,

      gc cr / current grant 2/3-way 대기이벤트가 발생할 수 있다.)

4) 원하는 블록을 전송받은 rac2 노드의 master node 에 정보를 갱신한다.

 

SYS@racdb1> select b.lock_element_addr, b.status, e.mode_held, e.local
from v$bh b, v$gc_element e
where b.lock_element_addr = e.gc_element_addr
and b.file#=5 and b.block#=207;   2    3    4

 

SYS@racdb2> select b.lock_element_addr, b.status, e.mode_held, e.local
from v$bh b, v$gc_element e
where b.lock_element_addr = e.gc_element_addr
and b.file#=5 and b.block#=207;  2    3    4

no rows selected

 

2번 노드에서 select 문장 던지기

SYS@racdb2> select salary from hr.employees where employee_id = 100;

    SALARY
----------
     24000

 

그리고 2번 노드에서 다시 확인해보면 : 메모리가 올라와 있다.

SYS@racdb2> select b.lock_element_addr, b.status, e.mode_held, e.local
from v$bh b, v$gc_element e
where b.lock_element_addr = e.gc_element_addr
and b.file#=5 and b.block#=207;  2    3    4


3교시

 

★ 세번째 시나리오 ★

 

3. select - write

마스터 노드 master node - rac1에

리퀘스트 노드 request node - rac2, X 모드

홀더 노드 holder node - rac1, rac2, S 모드

 

rac1 번 노드, rac2 번 노드 둘 다 100번 사원의 row 가 들어있는 block 을 DBC 에 가지고 있고

마스터 노드는 rac1 번에만 있다고 하자.

그런데 유저가 rac2 노드로 들어와서 update hr.emp set sal = 2000 where id = 100;

라는 업데이트문을 던졌다면?

rac1 에서도 rac2 에서도 100번 사원의 정보를 select 문으로 조회만 했기 때문에 둘다 Shared 모드인데

업데이트문이 던져지면 > 해당 블록은 null 모드로 다운그레이드되고, rac1 번 노드의 마스터 노드는

X (exclusive) 모드가 된다.

 

1) rac2 node 에서 100번 사원의 급여를 24000 에서 30000 으로 업데이트하기 위해서

    마스터노드에게 해당 블록을 변경할 수 있는 X (exclusive) 모드를 요청한다.

2) X 모드와 S 모드는 동시에 사용될 수 없기 때문에

    마스터 노드는 S 모드를 가지고 있는 rac1 노드에게 S 모드를 N (null) 모드로 다운그레이드하라고 지시한다.

3) rac1 번 노드는 S 모드를 N 모드로 다운그레이드한 후 그 결과를 rac2 노드에게 알려준다.

4) 응답받은 rac2 노드는 자신의 모드를 X 모드로 변경한 후 값을 수정하고

    마스터노드에 rac1 노드를 N 모드로 다운그레이드했고 rac2 는 X 모드로 변경되었다는 내용을 전달하고

    값을 수정한다. (마스터노드 갱신)

 

양쪽 노드 확인 : 두 개의 노드가 모두 100번 사원의 블록을 가지고 있다.

 

SYS@racdb1> select b.lock_element_addr, b.status, e.mode_held, e.local
from v$bh b, v$gc_element e
where b.lock_element_addr = e.gc_element_addr
and b.file#=5 and b.block#=207;  2    3    4

 

SYS@racdb2> select b.lock_element_addr, b.status, e.mode_held, e.local
from v$bh b, v$gc_element e
where b.lock_element_addr = e.gc_element_addr
and b.file#=5 and b.block#=207;  2    3    4

 

1) rac2 node 에서 100번 사원의 급여를 24000 에서 30000 으로 업데이트

 

SYS@racdb2> update hr.employees
set salary = 30000
where employee_id = 100;  2    3

1 row updated.

 

 

SYS@racdb2> select b.lock_element_addr, b.status, e.mode_held, e.local
from v$bh b, v$gc_element e
where b.lock_element_addr = e.gc_element_addr
and b.file#=5 and b.block#=207;  2    3    4

# MODE_HELD = 2: X 모드

 

SYS@racdb1> select b.lock_element_addr, b.status, e.mode_held, e.local
from v$bh b, v$gc_element e
where b.lock_element_addr = e.gc_element_addr
and b.file#=5 and b.block#=207;  2    3    4

 

null 모드로 바뀌면서 (null 모드 ≒ flush) 블록 정보가 사라졌다.

(사진 O)


3. write (DML) 는 했지만 commit 을 하지 않은 상태에서 read (select)

마스터 노드 master node - rac1

리퀘스트 노드 request node - rac2, S 모드

홀더 노드 holder node - rac2, X 모드

 

1) rac1 번 노드에서 100 번 사원이 있는 블록을 조회하기 위해서

    master node 인 rac1 노드에 요청한다.

2) 마스터 노드는 대상 블록이 rac1 노드 (자기 노드) 에 있는 것을 확인한 후

    rac1 번 노드에게 rac2 노드로 블록을 전송하도록 요청한다.

3) rac2 번 노드는 해당 블록이 변경된 후 커밋하지 않았기 때문에

    해당 블록을 CR 블록으로 만들어서 N 모드로 복사한 다음 rac1 노드에 전송한다.

4) N 모드로 전송받은 CR 블록의 이전값을 UNDO 에서 찾아 값을 수정한다.

    (업데이트 이전 값으로 블록을 수정)

    CR 블록을 유저에게 전달한 후 CR 블록은 N 모드 (null 모드이기 때문에 안 보임) 

 

(사진 2:28)

(2:31)

 

SYS@racdb2> select salary from hr.employees where employee_id = 100;

    SALARY
----------
     30000

 

SYS@racdb2> select b.lock_element_addr, b.status, e.mode_held, e.local
from v$bh b, v$gc_element e
where b.lock_element_addr = e.gc_element_addr
and b.file#=5 and b.block#=207;  2    3    4

 

rac1 쪽에서 조회하면: 이전값으로 보인다.

 

SYS@racdb1> select salary from hr.employees where employee_id = 100;

    SALARY
------------------
     24000

 

SYS@racdb1> select b.lock_element_addr, b.status, e.mode_held, e.local
from v$bh b, v$gc_element e
where b.lock_element_addr = e.gc_element_addr
and b.file#=5 and b.block#=207;  2    3    4

 

(하지만 아직 commit 하지 않은 상태)


4교시

 

DB는 하나지만 각 노드는 언두와 리두 등을 각각 따로 가지고 있다.

 

rac1 의 언두 확인

SYS@racdb1> show parameter undo

 

rac2 의 언두 확인

SYS@racdb2> show parameter undo

 

★ 네번째 시나리오 ★

 

4. write (DML) 는 했지만 commit 을 하지 않은 상태에서 read (select)

마스터 노드 master node - rac1

리퀘스트 노드 request node - rac2, S 모드 → N 모드 (대상 블록에 대해 다른 노드에서 X 모드로 수행하고 있어서)

홀더 노드 holder node - rac2, X 모드

 

1) rac1 번 노드에서 100 번 사원이 있는 블록을 조회하기 위해서

    master node 인 rac1 노드에 요청한다.

2) 마스터 노드는 대상 블록이 rac1 노드 (자기 노드) 에 있는 것을 확인한 후

    rac1 번 노드에게 rac2 노드로 블록을 전송하도록 요청한다.

3) rac2 번 노드는 해당 블록이 변경된 후 커밋하지 않았기 때문에

    해당 블록을 CR 블록으로 만들어서 N 모드로 복사한 다음 rac1 노드에 전송한다.

4) N 모드로 전송받은 CR 블록의 이전값을 UNDO 에서 찾아 값을 수정한다.

    (업데이트 이전 값으로 블록을 수정)

    CR 블록을 유저에게 전달한 후 CR 블록은 N 모드 (null 모드이기 때문에 안 보임) 


2번 노드에서 커밋

 

SYS@racdb2> commit;
Commit complete.

 

1번 노드에서 확인: 없다고 나온다.

SYS@racdb1> select b.lock_element_addr, b.status, e.mode_held, e.local
from v$bh b, v$gc_element e
where b.lock_element_addr = e.gc_element_addr
and b.file#=5 and b.block#=207;  2    3    4

 

2번 노드에서 확인

SYS@racdb2> select b.lock_element_addr, b.status, e.mode_held, e.local
from v$bh b, v$gc_element e
where b.lock_element_addr = e.gc_element_addr
and b.file#=5 and b.block#=207;  2    3    4

LOCK_ELE    STATUS      MODE_HELD      LOCAL
-----------------------------------------------------------------------
5CFFD680         xcur                  2                      1

 

5. write (DML) 한 다음 commit 까지 한 상태에서 read (select)

마스터 노드 master node - rac1

리퀘스트 노드 request node - rac2, S 모드

홀더 노드 holder node - rac2, X 모드 (commit 상태)

 

1) rac1 노드는 100번 사원의 급여 조회를 수행하기 위해서 마스터 노드에게 요청한다.

2) 마스터 노드는 해당 블록이 rac2 노드에 X 모드 (commit) 정보를 확인한 후

    rac2 노드에게 X 모드를 S 모드로 다운그레이드하라고 요청한다.

3) 요청받은 rac2 노드는 자기가 가지고 있는 블록을 rac1 노드로 전송한다.

4) rac1 노드는 전송받은 블록을 S 모드로 수정한 후 그 내용을 마스터 노드에게 갱신 요청한다.

 

1번 노드에서 조회

 

SYS@racdb1> select salary from hr.employees where employee_id = 100;

    SALARY
----------------
     30000

 

결과가 안 보인다. (모두가 다같이 똑같은 결과)

원래는 rac1 에서도 shared 모드로 나와야 하는데...

 

SYS@racdb1> select b.lock_element_addr, b.status, e.mode_held, e.local
from v$bh b, v$gc_element e
where b.lock_element_addr = e.gc_element_addr
and b.file#=5 and b.block#=207;  2    3    4

no rows selected

 

SYS@racdb2> select b.lock_element_addr, b.status, e.mode_held, e.local
from v$bh b, v$gc_element e
where b.lock_element_addr = e.gc_element_addr
and b.file#=5 and b.block#=207;  2    3    4

 

다시 rac1 에서 조회하기

(null 모드였던 잔재를 가지고 있어서 결과가 안나오는 걸 수도 있으니까 적어도 3번은 해보기)

더보기

SYS@racdb1> /

no rows selected

SYS@racdb1> /

no rows selected

SYS@racdb1> /

no rows selected

SYS@racdb1> /

no rows selected

SYS@racdb1> /

no rows selected

SYS@racdb1> /

no rows selected

 

.........


6. 같은 행에 대해서 write - write (18:30~)

(blocking session 발생)

 

1번 노드에서

SYS@racdb1> update hr.employees
set salary = 40000
where employee_id = 100;  2    3

1 row updated.

 

SYS@racdb1> select b.lock_element_addr, b.status, e.mode_held, e.local
from v$bh b, v$gc_element e
where b.lock_element_addr = e.gc_element_addr
and b.file#=5 and b.block#=207;  2    3    4

 

그러면 2번 모드는 null 로 바뀐다.

그리고 얘를 수행해보면 결과가 아래처럼 나오는데

 

SYS@racdb2> select b.lock_element_addr, b.status, e.mode_held, e.local
from v$bh b, v$gc_element e
where b.lock_element_addr = e.gc_element_addr
and b.file#=5 and b.block#=207;  2    3    4

 

LOCK_ELE    STATUS      MODE_HELD      LOCAL
-----------------------------------------------------------------------
5CFFD680           pi                    0                     0

 

# pi 는 과거 이미지 (Past Image) 라는 뜻

# rac2 노드 블록은 null 모드로 다운그레이드된 블록은 PI (Past Image, 과거 이미지) 가 되며

   이 블록을 전송하기 전에 블록의 내용을 리두로그에 기록한다.

   (pi 는 현재 null 모드로 다운그레이드되는 블록이면서 + redo 에 있는 내용도 내리고 있다는 뜻이라고 보면 된다)

 

그리고 다시 2번 노드에서 조회해보면: 어느 순간 null 모드로 바뀌어 있다,

더 이상 pi 라고 뜨지 않음. (pi 라고 뜨는 건 엄청 짧은 순간 동안만)

SYS@racdb2> select b.lock_element_addr, b.status, e.mode_held, e.local
from v$bh b, v$gc_element e
where b.lock_element_addr = e.gc_element_addr
and b.file#=5 and b.block#=207;  2    3    4

 

2번 노드에서 사원의 월급을 35000으로 수정하면:

창이 대기 상태로 빠진다.

SYS@racdb2> update hr.employees
set salary = 35000
where employee_id = 100;  2    3

같은 row 를 서로 다른 인스턴스에서 접근했기 때문에 대기상태로 빠진 것

언제까지?

업데이트를 걸고 있는 다른 세션에서 풀어줄 때까지...

 

다시 1번 노드에서 얘 수행하면

SYS@racdb1>

select inst_id, sid, serial#, event, blocking_instance, blocking_session
from gv$session
where event like 'enq: TX%';

 

INST_ID  SID   SERIAL#                   EVENT                    BLOCKING_INSTANCE   BLOCKING_SESSION
----------------------------------------------------------------------------------------------------------------------------------------------
      2        148       79        enq: TX - row lock contention                       1                                  140

 

SYS@racdb1> SELECT inst_id, sid, serial#, event, blocking_instance, blocking_session
FROM gv$session
WHERE sid IN (148, 140);

INST_ID    SID     SERIAL#                   EVENT                    BLOCKING_INSTANCE   BLOCKING_SESSION
----------------------------------------------------------------------------------------------------------------------------------------------
         1        140        160          PX Deq: Execute Reply
         2        148         79        enq: TX - row lock contention                              1                          140

 

kill 시키기 (SID, serial#, instance 번호 순서 - 인스턴스는 지금 1번 노드이므로 1 이라고 쓰면 됨)

 

SYS@racdb1> alter system kill session '140,160,@1';
alter system kill session '140,160,@1'
*
ERROR at line 1:
ORA-00027: cannot kill current session

(모두가 다 똑같은 오류 발생: 자기 세션에서 자기를 죽이려고 했기 때문에)

 

다시 새로운 세션에서 수행해보기 (세션이 다르므로 1번 노드든 2번 노드든 상관없다)

새로 퍼티 창을 열어서 SQL+ 로 접속한 다음 수행해보면

 

[oracle@rac2 ~]$ sqlplus / as sysdba
SYS@racdb2> alter system kill session '140,160,@1';
System altered.

수행하는 순간 > rac1 킬 > 원래 켜놓았던 rac2 창의 대기가 풀리면서

업데이트가 수행된다.

 

SYS@racdb2> update hr.employees
set salary = 35000
where employee_id = 100;
1 row updated.

 

 

rac2 노드에서 (방금 업데이트를 수행한 창에서) 롤백하기

SYS@racdb2> rollback;
Rollback complete.