ORA-01591 : distributed transaction 에러
분산 트랜잭션의경우 2PC(2 Phase commit) 수행 중 Fail이 발생하면
관여된 일부 DB에서는 rollback or commit되고
일부는 distributed lock이 걸린 상태로 지속 될 수 있다.
이런 pending 된 transaction은 RECO가 자동으로 정리해 주나 정리가 안되면
ORA-01591에러가 발생하므로 Pending transaction을 정리해야 한다
1) DBA_2PC_PENDING을 조회
sqlplus system/manager
SQL> select local_tran_id, global_tran_id, state, mixed, host, commit#
from dba_2pc_pending ;
조회 후 여러 row가 나오면 ora-01591에러에 나오는 local_transaction_id 값과
조회 결과의 local_tran_id값을 비교하여 일치하는 row를 확인한다
이때 local_tran_id 와 global_tran_id의 뒷부분 숫자가 동일하면
이 node가 global coordinator임을 의미한다
이런 경우
sqlplus system/manager
SQL > select local_id, in_out, database, dbuser_owner, interface
from dba_2pc_neighbors ;
=> 여기 나오는 row들이 분산 트랜잭션에 관여한 db 정보이다
database 항목이 null -> 현재 조회하고 있는 local DB를 의미
IN_OUT = OUT : 참조하는 NODE 정보로 database에 나오는 값이 db link name이다
이를 이용해 remote db의 dba_2pc_pending을 조회하여 관련 note 상태를 확인한다
SQL> select local_tran_id, global_tran_id, state, mixed, host, commit#
from dba_2pc_pending@db_link;
row를 확인하여 같은 분산 트랜잭션에 포함된 정보인지
global_tran_id를 이용해 확인한다
2) commit point site 확인
일반적으로
commit point site는 global coordinator의 DBA_2PC_NEIGHBORS의
IN_OUT = OUT으로 나타나고
INTERFACE 부분이 C로 나타나게 된다.
commit point site가 중요한 이유는
이 node의 local transaction 부분은 prepared 상태를 거치지 않아 in-doubt 상태가 안되므로
distribute lock에 의해 조회나 DML 오류가 발생하지 않는다
=> 중요한 데이터를 포함하는 중심 node를 commit point site로 지정하는 것이 바람직 하다
3) dba_2pc_pending의 mixed 컬럼 확인
NO : commit point site가 commit을 수행하기 전에 분산 트랜잭션이 fail되어
commit point site가 rollback된 경우
non-commit point site에서 prepared상태의 트랜잭션을 commit force 하면
이것도 논리적으로
consistency가 지켜지지 않은 것은 동일하나 이때는 mixed 컬럼이 no가 된다
commit point site가 rollback되어 dba_2pc_pending에 row가 남지 않기 때문에
RECO가 mixed 상태를 인식하는 것이 불가능 하기 때문
YES : commit point site가 이미 commit을 수행한 상태에서 분산 트랜잭션이 fail된 경우
non-commit point site에서
prepared상태의 transaction을 rollback force 시켜
분산 트랜잭션의 consistency가 깨진 상태
state 칼럼 : commit point site는 COMMITTED
non-commit point site는 FORCED ROLLBACK으로 표시
① state = COMMITED
local DB 트랜잭션은 성공적으로 commit 됨, 어떤 작업도 수행할 필요 없음
RECO에 의해 해당 node 자동 삭제됨. 지우지 못했다면 직접 삭제 가능
sqlplus sys/manager
SQL> exec dbms_transaction.purge_lost_db_entry(local_tran_id값);
state=commit이면 commit point site가 commit된 후 임을 나타내므로
state=COMMITTED로 나타나거나
commit 후 정보다 지워져 dba_2pc_pending에 정보가 나타나지 않을 수 있다
필요한 경우 dba_2pc_pending의 내용만 정리해 준다
② state = PREPARED
이 node에서 변경된 data가 속한 block에 distributed lock이 걸린 상태를 의미,
이런 경우 data가 있는 block에 대한 모든 read/write가 ora-01591을 발생시킴
관련 node의 dba_2pc_pending을 조회해서 정보가 없으면 commit point site이고
이미 rollback되었음을 의미함.
prepared 상태의 transaction을 rollback force 시켜준다
SQL> rollback force ‘local_tran_id’ ;
SQL> commit force ‘local_tran_id’;
node에 대한 정보가 있고 상태가 COMMITED면 commit 해주어야 한다
SQL> commit force ‘tran_local_id’, ‘SCN’;
local_tran_id 뒤에 SCN를 지정할 수 있는데 제일 큰 SCN을 지정한다.
SCN값은 dba_2pc_pending의 commit# 에서 확인
-> 분산DB중 한 DB에서 incomplete recovery 필요한 경우
다른 DB도 일관성을 맞추기 위해 incomplete recovery를 이용할 수 있게 하기 위한 것
③ state = COLLECTING
distribute lock을 걸리 전단계에서 transaction이 비정상 종료 됨을 의미
lock이 걸리기 전이므로 변경된 데이터는 rollback된 상태임
이 경우엔 dba_2pc_pending에서 내용을 삭제 해주면 됨
sqlplus sys/manager
SQL>exec dbms_transaction.purge_lost_db_entry(‘tran_local_id’);
④ state = FORCED ROLLBACK or FORCED COMMIT인 경우
이미 RECO or 디비 어드민이 rollback force or commit force를 하여 추가 수행 작업은 없음.
RECO가 자동으로 ENTRY를 삭제해 줄 것이나 기다리지 않고 직접 삭제 할 수 있다
sqlplus sys/manager
SQL>exec dbms_transaction.purge_lost_db_entry(‘tran_local_id’);
4) 불일치 사항을 파악하고 dba_2pc_pending을 정리한다
분산 트랜잭션의 consistency가 무엇보다 중요한 경우라면 관계된
node의 database를 모두 문제의 분산 트랜잭션이 수행되기 이전 상태로
incomplete recovery를 수행하거나 할 수 있다.
분산 트랜잭션의 commit시 이용하는 commit SCN을 관계된
모든 node들의 최대 SCN으로 이용하는 것이 바로 이러한 recovery를 위한 것이다
이렇게 일부 database에서 SCN값이 이전 SCN에서 1씩 증가하는 것이 아니라
큰 값으로 건너뛰어 다른 database와 같은 SCN을 유지하게 함으로써,
이후에 incomplete recovery시에 관계된 node들이
서로 동일한 SCN으로 recovery를 수행하면,
모두 분산 트랜잭션 적용 이전이 되거나 or
모두 이후가 되어 일관성을 유지할 수 있도록 해준다
MIXED=YES인 상태에서
inconsistency를 받아들이고 DBA_2PC_PENDING view를 정리하려면
다음과 같이 수행한다.
sqlplus sys/manager
SQL>exec dbms_transaction.purge_mixed('local_tran_id');
Automatic Undo mode에선 DBMS_TRANSACTION.PURGE_LOST_DB_ENTRY 사용 전에
"_smu_debug_mode"를 기술해 주어야 한다.
alter session set "_smu_debug_mode" = 4;
execute DBMS_TRANSACTION.PURGE_LOST_DB_ENTRY('local_tran_id');
출처: https://pat98.tistory.com/265 [pat98's always & forever:티스토리]