忘れかけのIT備忘録

今まで学んできた知識や小技、なるほど!と思ったことをメモするブログです。

トランザクションのACID特性と分離レベルの概要

今回はトランザクションのACID特性と分離レベルについて調査しました。

ACID特性とは、トランザクションで処理するデータの整合性を保つ(データの信頼性を保証する)ために必要な4つの特性です。
Atomicity(原子性)、Consistency(一貫性)、Isolation(独立性)、Durability(耐久性)の頭文字を取ってACID特性と呼ばれています。

Atomicity(原子性)
トランザクションは、そのすべてを反映させるか、そのすべてを取り消すかの状態であること

Consistency(一貫性)
トランザクションは、その前後でデータの整合性が保たれ、矛盾のない状態が継続されること

Isolation(独立性)
トランザクションでは、同時並行処理結果(Parallel)も逐次処理結果(Serial)も同じであること
処理中の過程が外部から隠蔽されており、他の処理から影響を受けない/他の処理に影響を与えないこと

Durability(耐久性)
トランザクションは、処理が完了したら、その結果が障害などで影響を受けず、不変に保たれること

トランザクション(ANSI/ISO SQL規格)の分離レベルとは、複数のトランザクションの処理を同時に実行した際、各トランザクションの処理の一貫性をどこまで保証するかトランザクション同士の影響度を4段階で定義したものです。
一般的に複数の処理を実行する際、並列度が高いほど処理は速くなります。
ただし、データベースのように共通のデータを複数のトランザクションが処理する場合、並列度を高くして処理を速めたからと言って処理対象のデータが必ずしも一貫性がある(正しいデータの状態が保証されている)とは限りません。
・処理性能を上げる代わりにデータの一貫性を下げるか
・データの一貫性を上げる代わりに処理性能を下げるか
というようにトレードオフになっています。
このトレードオフの関係を4段階で定義したものが分離レベルです。

READ UNCOMMITTED (非コミット読取り)
確定していないデータまで読み取る
他のトランザクションの処理による更新途中のデータまで読み取る
データの一貫性は低いが処理性能は高い

READ COMMITTED (コミット読取り)
確定した最新データを常に読み取る
他のトランザクションの処理は常にコミット済みのデータのみ読み取る

REPEATABLE READ (リピータブル・リード)
読み取り対象のデータを常に読み取る
特定のトランザクションの処理が実行中の間、読み取り対象のデータは読み取り途中で他のトランザクションによって変更されない
同じトランザクション中では同じデータは毎回同じ値を読むことができる

SERIALIZABLE (シリアライズ可能)
直列化可能
並列で実行している複数のトランザクションの処理の結果が、逐次実行した場合と同じ結果となる
データの処理性能は低いが一貫性は高い

データの一貫性を下げる振る舞いとして、3種類のデータリード現象が定義されています。

ダーティリード (Dirty Read)
特定のトランザクションで変更したデータをCOMMITしていないにもかかわらず、別のトランザクションが読込めてしまう現象

ノンリピータブルリード (Non-repeatable Read) / ファジーリード (Fuzzy Read)
特定のトランザクション実行中に読み込んだデータが、別のトランザクションでデータ変更・COMMITしたことにより、1回目の読込みデータと2回目の読込みデータに差異が発生する現象

ファントムリード (Phantom Read)
特定のトランザクション実行中に読み込んだデータが、別のトランザクションでデータ挿入/削除・COMMITしたことにより、1回目の読込みデータと2回目の読込みデータに差異が発生する現象

分離レベルとリード現象の関係性をまとめました。


Oracleの場合、READ COMMITTEDとSERIALIZABLEを提供しています

■分離レベルの設定
SET TRANSACTION句で設定できます

■参考資料
データの同時実行性と整合性
トランザクション分離レベル - Wikipedia
SET TRANSACTION

Oracleのロック検証(デッドロック編)

今回はOracleデッドロックについて検証しました。

デッドロックとは、2つ以上のセッションのトランザクションがお互いにロック解除待ち状態に陥る状態です。
お互いのセッションのトランザクションが相手のロック解除を待っているため、いずれのセッションもトランザクションを終了できず、ロック競合を解消できなくなります。

デッドロックが発生した場合、デッドロックが検出されたセッション(ORA-00060が発生したセッション)のトランザクションが自動的にロールバックされます。
ロールバック後、トランザクション制御できるようになるため、そのトランザクション全体をコミットまたはロールバックしてトランザクションを終了し、ロック競合を解除します。

■検証環境
OS:Oracle Linux 6.5
DB/GI:Oracle Database 12c Release 1 (12.1.0.2.0) Enterprise Edition
※2ノードRAC(管理者管理型DB)

■前提
・非CDB

■検証パターン
①別々のセッションから同じリソースに対して更新

■検証
①別々のセッションから同じリソースに対して更新
セッション1、セッション2で同じ表の別々のレコードに対してロック取得後、セッション1からセッション2がロック取得中のレコードに対して更新、セッション2からセッション1がロック取得中のレコードに対して更新しようとしたときデッドロック検出されるか検証します

【検証手順】
1. セッション1(SCOTT)で特定のレコードに対してロック取得
2. セッション2(HR)で同じ表の別レコードに対してロック取得
3. セッション1(SCOTT)からセッション2(HR)がロック取得中のレコードに対して更新
4. セッション2(HR)からセッション1(SCOTT)がロック取得中のレコードに対して更新

【作業ログ】

1. セッション1(SCOTT)で特定のレコードに対してロック取得
SQL> select * from emp;

     EMPNO ENAME      JOB               MGR HIREDATE        SAL       COMM     DEPTNO
---------- ---------- ---------- ---------- -------- ---------- ---------- ----------
      7369 SMITH      CLERK            7902 80-12-17        800                    20
      7499 ALLEN      SALESMAN         7698 81-02-20       1600        300         30
      7521 WARD       SALESMAN         7698 81-02-22       1250        500         30
      7566 JONES      MANAGER          7839 81-04-02       2975                    20
      7654 MARTIN     SALESMAN         7698 81-09-28       1250       1400         30
      7698 BLAKE      MANAGER          7839 81-05-01       2850                    30
      7782 CLARK      MANAGER          7839 81-06-09       2450                    10
      7788 SCOTT      ANALYST          7566 87-04-19       3000                    20
      7839 KING       PRESIDENT             81-11-17       5000                    10
      7844 TURNER     SALESMAN         7698 81-09-08       1500          0         30
      7876 ADAMS      CLERK            7788 87-05-23       1100                    20
      7900 JAMES      CLERK            7698 81-12-03        950                    30
      7902 FORD       ANALYST          7566 81-12-03       3000                    20
      7934 MILLER     CLERK            7782 82-01-23       1300                    10

14行が選択されました。

SQL> update emp set SAL = 1000 where EMPNO = 7369;

1行が更新されました。

2. セッション2(HR)で同じ表の別レコードに対してロック取得
SQL> select * from scott.emp;

     EMPNO ENAME      JOB               MGR HIREDATE        SAL       COMM     DEPTNO
---------- ---------- ---------- ---------- -------- ---------- ---------- ----------
      7369 SMITH      CLERK            7902 80-12-17        800                    20
      7499 ALLEN      SALESMAN         7698 81-02-20       1600        300         30
      7521 WARD       SALESMAN         7698 81-02-22       1250        500         30
      7566 JONES      MANAGER          7839 81-04-02       2975                    20
      7654 MARTIN     SALESMAN         7698 81-09-28       1250       1400         30
      7698 BLAKE      MANAGER          7839 81-05-01       2850                    30
      7782 CLARK      MANAGER          7839 81-06-09       2450                    10
      7788 SCOTT      ANALYST          7566 87-04-19       3000                    20
      7839 KING       PRESIDENT             81-11-17       5000                    10
      7844 TURNER     SALESMAN         7698 81-09-08       1500          0         30
      7876 ADAMS      CLERK            7788 87-05-23       1100                    20
      7900 JAMES      CLERK            7698 81-12-03        950                    30
      7902 FORD       ANALYST          7566 81-12-03       3000                    20
      7934 MILLER     CLERK            7782 82-01-23       1300                    10

14行が選択されました。

SQL> update scott.emp set SAL = 2000 where EMPNO = 7521;

1行が更新されました。

3. セッション1(SCOTT)からセッション2(HR)がロック取得中のレコードに対して更新
SQL> update emp set SAL = 5000 where EMPNO = 7521;
★応答なし

update emp set SAL = 5000 where EMPNO = 7521
       *
行1でエラーが発生しました。:
ORA-00060: リソース待機の間にデッドロックが検出されました。
★「4」実行後、数秒経ってからORA-00060発生

4. セッション2(HR)からセッション1(SCOTT)がロック取得中のレコードに対して更新
SQL> update scott.emp set SAL = 6000 where EMPNO = 7369;
★応答なし
セッション1(SCOTT)でトランザクション全体をコミットまたはロールバック後、制御が戻った(更新できた)

デッドロック発生後のロック状態
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
        16      31780 HR                           372
       372      65257 SCOTT

SQL> select SID, TYPE, decode(LMODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode, BLOCK from v$lock where type in('TX', 'TM');

       SID TYPE   LCK_MODE                            BLOCK
---------- ------ ------------------------------ ----------
       372 TM     RX/SX(行排他ロック)                     2
        16 TM     RX/SX(行排他ロック)                     2
        16 TX     X(排他ロック)                           2
       372 TX     X(排他ロック)                           1
        16 TX     NONE(ロックなし)                        0

SQL> select o.owner, o.object_name, o.object_type, lo.SESSION_ID, decode(lo.LOCKED_MODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode from v$locked_object lo, dba_objects o where lo.OBJECT_ID = o.OBJECT_ID;

OWNER           OBJECT_NAME     OBJECT_TYPE     SESSION_ID LCK_MODE
--------------- --------------- --------------- ---------- ------------------------------
SCOTT           EMP             TABLE                  372 RX/SX(行排他ロック)
SCOTT           EMP             TABLE                   16 RX/SX(行排他ロック)

 

【参考】

●DBアラートログ
Sun Jan 01 10:47:40 2023
Global Enqueue Services Deadlock detected (DID = 3_0_1). More information in file
 /u01/app/oracle/diag/rdbms/orcl/orcl1/trace/orcl1_lmd0_5951.trc.
デッドロックが検出されたノード(本検証ではノード1)のDBアラートログに出力された。ノード2のDBアラートログには何も出力されなかった

●トレースファイル
*** 2023-01-01 10:47:40.171
DUMP LOCAL BLOCKER/HOLDER: block level 5 res [0x1d0020][0x133],[TX][ext 0x0,0x2]
----------resource 0xa6636938----------------------
resname       : [0x1d0020][0x133],[TX][ext 0x0,0x2]
lmdid         : 0
rht group     : 0
rht ptr       : 0xa62830d8
rht bucket idx: 1860
hash mask     : x3
Local inst    : 1
dir_inst      : 1
master_inst   : 1
hv idx        : 39
hv last r.inc : 3
current inc   : 3
hv status     : 0
hv master inst: 1
open options  : deadlock detection=Y, cached=N, varvblk=N, slock=N
grant_bits    : KJUSERNL KJUSEREX 
grant mode    : KJUSERNL  KJUSERCR  KJUSERCW  KJUSERPR  KJUSERPW  KJUSEREX
count         : 1         0         0         0         0         1
val_state     : KJUSERVS_NOVALUE
valblk        : 0x0300000000000000419bbd6000000000 .A`
access_inst   : 1
vbreq_state   : 0
state         : x0
resp          : 0xa6636938
entry         : DIR=Y, MASTER=Y
On Scan_q?    : N
On Cache?     : N
On Remote_q?  : N
history       : x11
frozen        : 0
Total accesses: 29
Imm.  accesses: 16
Granted_locks : 1 
Cvting_locks  : 1 
value_block:  03 00 00 00 00 00 00 00 41 9b bd 60 00 00 00 00
GRANTED_Q: 
lp 0xa52578c8 gl KJUSEREX rp 0xa6636938 [0x1d0020][0x133],[TX][ext 0x0,0x2]
  master 1 gl owner 0xa9c0e6e8 possible pid 19700 xid 38000-0001-0000001D rseq 1 mseq 0 bast none
  history REF_RES > LOC_AST > CLOSE > FREE > REF_RES > LOC_AST
  open opt KJUSERDEADLOCK  sec since grant 10
CONVERT_Q: 
lp 0xa5257e98 gl KJUSERNL rl KJUSEREX rp 0xa6636938 [0x1d0020][0x133],[TX][ext 0x0,0x2]
  master 1 gl owner 0xa9ee04e8 possible pid 19535 xid 33000-0001-7FF300000025 rseq 1 mseq 0 bast none
  history REF_RES > LOC_AST > CLOSE > FREE > REF_RES > GR2CVT
  convert opt KJUSERGETVALUE 
----------enqueue 0xa52578c8------------------------
lock version     : 2073
Owner inst       : 1
grant_level      : KJUSEREX
req_level        : KJUSEREX
bast_level       : KJUSERNL
notify_func      : none
resp             : 0xa6636938
procp            : 0xa76d49a8
pid              : 19535
proc version     : 19
oprocp           : (nil)
opid             : 19535
group lock owner : 0xa9c0e6e8
possible pid     : 19700
xid              : 38000-0001-0000001D
dd_time          : 0.0 secs
dd_count         : 0
timeout          : 0.0 secs
On_timer_q?      : N
On_dd_q?         : N
sec since grant  : 10
lock_state       : GRANTED
ast_flag         : 0x0
Open Options     : KJUSERDEADLOCK 
Convert options  : KJUSERNOQUEUE KJUSERNODEADLOCKWAIT 
History          : REF_RES > LOC_AST > CLOSE > FREE > REF_RES > LOC_AST
Msg_Seq          : 0x0
res_seq          : 1
valblk           : 0x0008000000000000a0cd8c56f37f0000 .V
user session for deadlock lock 0xa52578c8
  sid: 16 ser: 31780 audsid: 2930374 user: 102/HR
    flags: (0x41) USR/- flags_idl: (0x1) BSY/-/-/-/-/-
    flags2: (0x40009) -/-/INC
  pid: 56 O/S info: user: oracle, term: UNKNOWN, ospid: 19700
    image: oracle@node1.oracle12c.jp (TNS V1-V3)
  client details:
    O/S info: user: oracle, term: pts/1, ospid: 19699
    machine: node1.oracle12c.jp program: sqlplus@node1.oracle12c.jp (TNS V1-V3)
    application name: SQL*Plus, hash value=3669949024
  current SQL:
  update scott.emp set SAL = 6000 where EMPNO = 7369
DUMP LOCAL BLOCKER: initiate state dump for DEADLOCK
  possible owner[56.19700] on resource TX-001D0020-00000133-00000000-00000002
2023-01-01 10:47:40.183766 :kjzddmp(): Submitting asynchronized dump request [1c]. summary=[ges process stack dump (kjdglblkrdm1)].
----------enqueue 0xa5257e98------------------------
lock version     : 1337
Owner inst       : 1
grant_level      : KJUSERNL
req_level        : KJUSEREX
bast_level       : KJUSERNL
notify_func      : none
resp             : 0xa6636938
procp            : 0xa76d49a8
pid              : 19535
proc version     : 19
oprocp           : (nil)
opid             : 19535
group lock owner : 0xa9ee04e8
possible pid     : 19535
xid              : 33000-0001-00000025
dd_time          : 10.0 secs
dd_count         : 1
timeout          : 0.0 secs
On_timer_q?      : N
On_dd_q?         : Y
sec since grant  : N/A
lock_state       : OPENING CONVERTING 
ast_flag         : 0x0
Open Options     : KJUSERDEADLOCK 
Convert options  : KJUSERGETVALUE 
History          : REF_RES > LOC_AST > CLOSE > FREE > REF_RES > GR2CVT
Msg_Seq          : 0x0
res_seq          : 1
valblk           : 0x17000000000000000000000000000000 .
user session for deadlock lock 0xa5257e98
  sid: 372 ser: 65257 audsid: 2930373 user: 110/SCOTT
    flags: (0x41) USR/- flags_idl: (0x1) BSY/-/-/-/-/-
    flags2: (0x40009) -/-/INC
  pid: 51 O/S info: user: oracle, term: UNKNOWN, ospid: 19535
    image: oracle@node1.oracle12c.jp (TNS V1-V3)
  client details:
    O/S info: user: oracle, term: pts/0, ospid: 19534
    machine: node1.oracle12c.jp program: sqlplus@node1.oracle12c.jp (TNS V1-V3)
    application name: SQL*Plus, hash value=3669949024
  current SQL:
  update emp set SAL = 5000 where EMPNO = 7521 ★デッドロックの原因となったSQL
DUMP LOCAL BLOCKER: initiate state dump for DEADLOCK
  possible owner[51.19535] on resource TX-001D0020-00000133-00000000-00000002
2023-01-01 10:47:40.185572 :kjzddmp(): Submitting asynchronized dump request [1c]. summary=[ges process stack dump (kjdglblkrdm1)].
DUMP LOCAL BLOCKER/HOLDER: block level 5 res [0x1b0012][0x147],[TX][ext 0x0,0x2]
----------resource 0xa6a6e638----------------------
resname       : [0x1b0012][0x147],[TX][ext 0x0,0x2]
lmdid         : 1
rht group     : 0
rht ptr       : 0xa62832b0
rht bucket idx: 717
hash mask     : x3
Local inst    : 1
dir_inst      : 1
master_inst   : 1
hv idx        : 45
hv last r.inc : 3
current inc   : 3
hv status     : 0
hv master inst: 1
open options  : deadlock detection=Y, cached=N, varvblk=N, slock=N
grant_bits    : KJUSERNL KJUSEREX 
grant mode    : KJUSERNL  KJUSERCR  KJUSERCW  KJUSERPR  KJUSERPW  KJUSEREX
count         : 1         0         0         0         0         1
val_state     : KJUSERVS_NOVALUE
valblk        : 0x01000000000000006f8025a500000000 .o%
access_inst   : 1
vbreq_state   : 0
state         : x0
resp          : 0xa6a6e638
entry         : DIR=Y, MASTER=Y
On Scan_q?    : N
On Cache?     : N
On Remote_q?  : N
history       : x11
frozen        : 0
Total accesses: 44
Imm.  accesses: 31
Granted_locks : 1 
Cvting_locks  : 1 
value_block:  01 00 00 00 00 00 00 00 6f 80 25 a5 00 00 00 00
GRANTED_Q: 
lp 0xa5095dd8 gl KJUSEREX rp 0xa6a6e638 [0x1b0012][0x147],[TX][ext 0x0,0x2]
  master 1 gl owner 0xa9ee04e8 possible pid 19535 xid 33000-0001-00000025 rseq 5 mseq 0 bast none
  history REF_RES > LOC_AST > CLOSE > FREE > REF_RES > LOC_AST
  open opt KJUSERDEADLOCK  sec since grant 6
CONVERT_Q: 
lp 0xa50963a8 gl KJUSERNL rl KJUSEREX rp 0xa6a6e638 [0x1b0012][0x147],[TX][ext 0x0,0x2]
  master 1 gl owner 0xa9c0e6e8 possible pid 19700 xid 38000-0001-7FF30000001D rseq 5 mseq 0 bast none
  history REF_RES > LOC_AST > CLOSE > FREE > REF_RES > GR2CVT
  convert opt KJUSERGETVALUE 
----------enqueue 0xa5095dd8------------------------
lock version     : 991
Owner inst       : 1
grant_level      : KJUSEREX
req_level        : KJUSEREX
bast_level       : KJUSERNL
notify_func      : none
resp             : 0xa6a6e638
procp            : 0xa76dad80
pid              : 19700
proc version     : 11
oprocp           : (nil)
opid             : 19700
group lock owner : 0xa9ee04e8
possible pid     : 19535
xid              : 33000-0001-00000025
dd_time          : 0.0 secs
dd_count         : 0
timeout          : 0.0 secs
On_timer_q?      : N
On_dd_q?         : N
sec since grant  : 6
lock_state       : GRANTED
ast_flag         : 0x0
Open Options     : KJUSERDEADLOCK 
Convert options  : KJUSERNOQUEUE KJUSERNODEADLOCKWAIT 
History          : REF_RES > LOC_AST > CLOSE > FREE > REF_RES > LOC_AST
Msg_Seq          : 0x0
res_seq          : 5
valblk           : 0x01000000000000009c91260000000000 .&
user session for deadlock lock 0xa5095dd8
  sid: 372 ser: 65257 audsid: 2930373 user: 110/SCOTT
    flags: (0x41) USR/- flags_idl: (0x1) BSY/-/-/-/-/-
    flags2: (0x40009) -/-/INC
  pid: 51 O/S info: user: oracle, term: UNKNOWN, ospid: 19535
    image: oracle@node1.oracle12c.jp (TNS V1-V3)
  client details:
    O/S info: user: oracle, term: pts/0, ospid: 19534
    machine: node1.oracle12c.jp program: sqlplus@node1.oracle12c.jp (TNS V1-V3)
    application name: SQL*Plus, hash value=3669949024
  current SQL:
  update emp set SAL = 5000 where EMPNO = 7521 ★デッドロックの原因となったSQL
DUMP LOCAL BLOCKER: initiate state dump for DEADLOCK
  possible owner[51.19535] on resource TX-001B0012-00000147-00000000-00000002
2023-01-01 10:47:40.186533 :kjzddmp(): Submitting asynchronized dump request [1c]. summary=[ges process stack dump (kjdglblkrdm1)].
----------enqueue 0xa50963a8------------------------
lock version     : 645
Owner inst       : 1
grant_level      : KJUSERNL
req_level        : KJUSEREX
bast_level       : KJUSERNL
notify_func      : none
resp             : 0xa6a6e638
procp            : 0xa76dad80
pid              : 19700
proc version     : 11
oprocp           : (nil)
opid             : 19700
group lock owner : 0xa9c0e6e8
possible pid     : 19700
xid              : 38000-0001-0000001D
dd_time          : 4.0 secs
dd_count         : 0
timeout          : 0.0 secs
On_timer_q?      : N
On_dd_q?         : Y
sec since grant  : N/A
lock_state       : OPENING CONVERTING 
ast_flag         : 0x0
Open Options     : KJUSERDEADLOCK 
Convert options  : KJUSERGETVALUE 
History          : REF_RES > LOC_AST > CLOSE > FREE > REF_RES > GR2CVT
Msg_Seq          : 0x0
res_seq          : 5
valblk           : 0x17000000000000000000000000000000 .
user session for deadlock lock 0xa50963a8
  sid: 16 ser: 31780 audsid: 2930374 user: 102/HR
    flags: (0x41) USR/- flags_idl: (0x1) BSY/-/-/-/-/-
    flags2: (0x40009) -/-/INC
  pid: 56 O/S info: user: oracle, term: UNKNOWN, ospid: 19700
    image: oracle@node1.oracle12c.jp (TNS V1-V3)
  client details:
    O/S info: user: oracle, term: pts/1, ospid: 19699
    machine: node1.oracle12c.jp program: sqlplus@node1.oracle12c.jp (TNS V1-V3)
    application name: SQL*Plus, hash value=3669949024
  current SQL:
  update scott.emp set SAL = 6000 where EMPNO = 7369
DUMP LOCAL BLOCKER: initiate state dump for DEADLOCK
  possible owner[56.19700] on resource TX-001B0012-00000147-00000000-00000002
2023-01-01 10:47:40.186987 :kjzddmp(): Submitting asynchronized dump request [1c]. summary=[ges process stack dump (kjdglblkrdm1)].
Global blockers dump end:-----------------------------------

========================================================================
  Global Wait-For-Graph(WFG) for GES Deadlock ID=[3_0_1]
------------------------------------------------------------------------
                       Victim : (instance=1, lock=0xa5257e98)
      Start (master) Instance : 1
     Number of Locks involved : 4
  Number of Sessions involved : 2

User session identified by:
{
                User Name : oracle
             User Machine : node1.oracle12c.jp
         OS Terminal Name : pts/0
            OS Process ID : 19534
          OS Program Name : sqlplus@node1.oracle12c.jp (TNS V1-V3)
         Application Name : SQL*Plusnode1.oracle12c.jp (TNS V1-V3)
              Action Name : AQ LB CoordinatorCLeaNupholdst
              Current SQL : update emp set SAL = 5000 where EMPNO = 7521 ★デッドロックの原因となったSQL
           Session Number : 372
    Session Serial Number : 65257
    Server Process ORAPID : 51
     Server Process OSPID : 19535
                 Instance : 1
}
waiting for Lock 0xa5257e98 (Transaction):
{
            Lock Level : KJUSEREX
         Resource Name : TX 0x1d0020.0x133(ext 0x0,0x2)
    GES Transaction ID : 33000-0001-00000025
}
which is blocked by Lock 0xa52578c8 (Transaction):
{
            Lock Level : KJUSEREX
         Resource Name : TX 0x1d0020.0x133(ext 0x0,0x2)
    GES Transaction ID : 38000-0001-0000001D
}
owned by the
User session identified by:
{
                User Name : oracle
             User Machine : node1.oracle12c.jp
         OS Terminal Name : pts/1
            OS Process ID : 19699
          OS Program Name : sqlplus@node1.oracle12c.jp (TNS V1-V3)
         Application Name : SQL*Plusnode1.oracle12c.jp (TNS V1-V3)
              Action Name : KDILM background CLeaNupholds
              Current SQL : update scott.emp set SAL = 6000 where EMPNO = 7369
           Session Number : 16
    Session Serial Number : 31780
    Server Process ORAPID : 56
     Server Process OSPID : 19700
                 Instance : 1
}
waiting for Lock 0xa50963a8 (Transaction):
{
            Lock Level : KJUSEREX
         Resource Name : TX 0x1b0012.0x147(ext 0x0,0x2)
    GES Transaction ID : 38000-0001-0000001D
}
which is blocked by Lock 0xa5095dd8 (Transaction):
{
            Lock Level : KJUSEREX
         Resource Name : TX 0x1b0012.0x147(ext 0x0,0x2)
    GES Transaction ID : 33000-0001-00000025
}
owned by the first user session of the WFG.
------------------------------------------------------------------------
      End of Global WFG for GES Deadlock ID=[3_0_1]
========================================================================

kjddprg: Transaction Deadlock added to the fixed table.

*** 2023-01-01 10:47:40.976
* Cancel deadlock victim lockp 0xa5257e98 
Global blockers dump start:---------------------------------

 

■ロック解除方法
トランザクション制御を戻すためのロック解除方法は主に2種類あります

トランザクションのコミットまたはロールバック
②セッション切断

トランザクションのコミットまたはロールバック
--1.セッション1(SCOTT)でデータ更新
SQL> update scott.emp set SAL = 1000 where EMPNO = 7369;

1行が更新されました。

--2.セッション2(HR)でデータ更新
SQL> update scott.emp set SAL = 2000 where EMPNO = 7369;
★応答なし
セッション1(SCOTT)でトランザクション全体をコミットまたはロールバック後、制御が戻った(更新できた)

②セッション切断
--1.セッション1(SCOTT)でデータ更新
SQL> update scott.emp set SAL = 1000 where EMPNO = 7369;

1行が更新されました。
★「3」でセッション切断後、exitでDBログアウト時に下記メッセージが出力されました
SQL> exit
ERROR:
ORA-00028: セッションは強制終了されました。


Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
With the Partitioning, Real Application Clusters, Automatic Storage Management, OLAP,
Advanced Analytics and Real Application Testing options (障害を含んでいます。)との接続が切断されました。

--2.セッション2(HR)でデータ更新
SQL> update scott.emp set SAL = 2000 where EMPNO = 7369;
★応答なし

--3.別セッションでセッション切断
--セッションID、シリアルナンバー確認
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
        24      16459 HR                           251
       251       9114 SCOTT

--ロック解除待ちの原因となっているセッションを切断
SQL> alter system kill session '251, 9114';

システムが変更されました。

SQL> --セッション切断後
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
        24      16459 HR
       251       9114 SCOTT

 

Oracleのロック検証(TXロック、TMロック編)

今回はOracleのロックについて検証しました。

ロックとは、複数のセッションでデータを同時に変更できないようにする仕組み(排他制御)です。
Oracleに限らず、たいていのRDBMSはロックを使用してデータの整合性を確保します。
セッションが1つしか存在しないシステムならあまりロックを意識しなくても良いかもしれませんが、セッションが複数存在するシステムならロックを意識してアプリ設計しないとロック取得待ちやデッドロックの原因になります。

ロックモード
DBリソースへのアクセスを制御するロックの種類
共有ロック(読取りロック)
同じリソースを複数のセッションで同時にアクセスできるようにするためのロック
共有ロック取得後も別セッションから対象のリソースへアクセスできる
ただし、別セッションは表定義やデータを変更できない(共有ロックは取得可能、排他ロックは取得不可)
PostreSQLなどはSELECT時に共有ロックを取得しますが、Oracleは読取り一貫性(CR)を採用しているため、SELECT時に共有ロックは取得しません
排他ロック(占有ロック)
同じリソースを複数のセッションで同時にアクセスできない(共有できない)ようにするためのロック
排他ロック取得後は別セッションはリソースへアクセスできない(共有ロック・排他ロックともに取得不可)

ロック範囲
リソースをロックする範囲
表レベル
特定の表(テーブル)全体をロックする
行レベル
表(テーブル)の特定の行(レコード)をロックする

エンキュー(Oracleエンキュー)
データの変更をシリアライズするため、共有メモリ構造体(ロック)を提供するサービス
ロックはキューで管理される(FIFO型のロック)

自動ロック
OracleDDL文やDML文でトランザクションを発行すると自動的にロックを取得する
DMLロック(データ・ロック)
DML文を実行した場合、自動的に取得されるロック
行レベルと表レベルの2種類のロックが自動的に取得される
データ保護が目的

・行レベルのロック(行ロック、TXロック、トランザクション)
表の特定の行に対するロック
INSERT/UPDATE/DELETE/MERGE/SELECT FOR UPDATE文を実行した場合、自動的に取得される
ロック取得タイミング:トランザクション開始時
ロック解放タイミング:トランザクションのコミット/ロールバック
※特定の行に対する表の表ロックも取得する(データ更新中のDDL操作(表定義変更など)を防ぐため)
・表レベルのロック(表ロック、TMロック、DMLエンキュー)
特定の表に対するロック
INSERT/UPDATE/DELETE/MERGE/SELECT FOR UPDATE文を実行した場合、自動的に取得される
ロック取得タイミング:トランザクション開始時
ロック解放タイミング:トランザクションのコミット/ロールバック
なお、表ロックには下記のモードがある

[ロックモード2]行共有ロック(RS/SS:ROW SHARE、SSは何の略か分かりません)
表のロックを保持しているトランザクションが該当行をロックしており、該当行を更新する予定であることを示すモード
[ロックモード3]行排他ロック(RX/SX:ROW EXCLUSIVE、SXは何の略か分かりません)
表のロックを保持しているトランザクションが該当行を更新/SELECT FOR UPDATEしたことを示すモード
[ロックモード4]共有表ロック(S:SHARE)
表のロックを保持しているトランザクションがいても別のトランザクションも同じ表へ問合せできることを示すモード
1トランザクションが共有表ロックを保持する場合、表更新できるが、複数トランザクションが共有表ロックを保持する場合、表更新できない
[ロックモード5]共有行排他表ロック(SRX:SHARE ROW EXCLUSIVE)
表のロックを保持しているトランザクションがいても別のトランザクションも同じ表へ問合せできることを示すモード
共有表ロックを保持するトランザクションは表更新できるが、別トランザクションは表更新できない
[ロックモード6]排他表ロック(X:EXCLUSIVE)
表のロックを保持しているトランザクション以外、表への問合せ・更新できないことを示すモード

※LOCK TABLE文で手動で明示的に表ロックを取得できる

DDLロック(データディクショナリ・ロック)
DDL文を実行した場合、自動的に取得されるロック
変更対象のオブジェクトに対するDMLロックも自動的に取得される
スキーマ・オブジェクトの構造(表定義やビュー定義など)保護が目的
※明示的にDDLロックを取得できない
DDL文のロック解除待ち時間(秒)はDDL_LOCK_TIMEOUT初期化パラメータで設定できる(デフォルト(0)は待機しない=NOWAIT)
・排他DDLロック
トランザクションDDLロック/DMLロックを取得できないようにするロック
トランザクションスキーマ・オブジェクト定義に対するDDL操作(ALTER TABLE、DROP TABLEなど)による変更(破壊)を防ぐ
・共有DDLロック
トランザクションが同じスキーマ・オブジェクト定義を変更(破壊)しない場合、同じスキーマ・オブジェクトにアクセスできるようにするロック
スキーマ・オブジェクト定義を変更(破壊)しないDDL文としてCREATE PROCEDUREなどがある
・ブレーク可能解析ロック
ライブラリキャッシュ(共有SQL領域)のSQL文がスキーマ・オブジェクトを参照する場合、対応するSQL文に対して取得されるロック
参照しているスキーマ・オブジェクト定義が変更/削除された場合、対応する共有SQL領域のSQL文を無効化する
SQL文の解析フェーズ中に共有プール内で取得される

システムロック
データファイルなどの内部構造の保護が目的
・ラッチ
SGA内の共有データの保護(排他制御)に使用される
共有メモリ領域へのアクセス開始~終了まで保持されるロック
マイクロ秒~ミリ秒レベルで獲得と解放を繰り返すが獲得に失敗した場合、スピンして獲得できるのを待機する
※スピン後でも獲得できずスリープする場合、待機イベント「latch free」などで待機する
・共有プール上でのメモリの割り当てや解放
・バッファキャッシュ上でのバッファの探索
・ライブラリキャッシュ上でのオブジェクトの探索
・セッションの開始
・チェックポイントの開始
・ログバッファの割り当て
など
mutex
ラッチに似ているが、ラッチは複数オブジェクトをグルーピングして保護するが、mutexは単一オブジェクトを保護する
・内部ロック
ディクショナリキャッシュ内のデータ保護
制御ファイル、REDOログファイル、アーカイブログファイル、データファイルなどのファイル保護
表領域とUNDOセグメントの保護

今回は特定のデータに対してロックモード1~6のロックを取得し、同じデータに対して別セッションからロックモード1~6のロックが取得できるか検証しました。
結果は下記のようになりました。

■検証環境
OS:Oracle Linux 6.5
DB/GI:Oracle Database 12c Release 1 (12.1.0.2.0) Enterprise Edition
※2ノードRAC(管理者管理型DB)

■前提
・非CDB

■設定内容
DDLロックタイムアウト時間

SQL> select inst_id, name, value from gv$parameter where name = 'ddl_lock_timeout';

INST_ID NAME                           VALUE
------- ------------------------------ ----------------------------------------
      1 ddl_lock_timeout               0
      2 ddl_lock_timeout               0

■検証パターン
①ロックモード1に対するロック取得検証
②ロックモード2に対するロック取得検証
③ロックモード3に対するロック取得検証
④ロックモード4に対するロック取得検証
⑤ロックモード5に対するロック取得検証
⑥ロックモード6に対するロック取得検証

■検証 ※作業ログは全パターンを載せると膨大な量になるため、セッション2(HR)から一部DML(insert・update)、一部DDL(DROP TABLE)を実行したログのみ載せます
①ロックモード1に対するロック取得検証
特定のデータに対してロックモード1のロックを取得し、同じデータに対して別セッションからロックモード1~6のロックが取得できるか検証します

【検証手順】
1. セッション1(SCOTT)で特定のデータに対してロックモード1のロック取得
2. セッション2(HR)で同じデータに対してロックモード1~6のロック取得

【作業ログ】
別セッションからロックモード1~6のロックが取得できたため作業ログは割愛します

②ロックモード2に対するロック取得検証
特定のデータに対してロックモード2のロックを取得し、同じデータに対して別セッションからロックモード1~6のロックが取得できるか検証します

【検証手順】
1. セッション1(SCOTT)で特定のデータに対してロックモード2のロック取得(select for update)
2. セッション2(HR)で同じデータに対してロックモード1~6のロック取得
3. セッション1(SCOTT)で特定のデータに対してロックモード2のロック取得(LOCK TABLE)
4. セッション2(HR)で同じデータに対してロックモード1~6のロック取得

【作業ログ】

1. セッション1(SCOTT)で特定のデータに対してロックモード2のロック取得
SQL> select * from emp where EMPNO = 7369 for update;

     EMPNO ENAME      JOB               MGR HIREDATE        SAL       COMM     DEPTNO
---------- ---------- ---------- ---------- -------- ---------- ---------- ----------
      7369 SMITH      CLERK            7902 80-12-17        800                    20

2. セッション2(HR)で同じデータに対してロックモード1~6のロック取得
--ロックモード3ロック取得(insert)
SQL> insert into scott.emp(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) values(9999, 'TEST', 'DBA', 7369, sysdate, 2000, 500, 10);

1行が作成されました。

※別セッションでロック状況確認
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
       134      63853 HR
       141      35627 SCOTT

SQL> select SID, TYPE, decode(LMODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode, BLOCK from v$lock where type in('TX', 'TM');

       SID TYPE   LCK_MODE                            BLOCK
---------- ------ ------------------------------ ----------
       134 TX     X(排他ロック)                           2
       134 TM     RX/SX(行排他ロック)                     2
       141 TX     X(排他ロック)                           2
       141 TM     RX/SX(行排他ロック)                     2

※下記SQLでオブジェクトレベルでロック取得状況を確認できます
SQL> select o.owner, o.object_name, o.object_type, lo.SESSION_ID, decode(lo.LOCKED_MODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode from v$locked_object lo, dba_objects o where lo.OBJECT_ID = o.OBJECT_ID;

OWNER           OBJECT_NAME     OBJECT_TYPE     SESSION_ID LCK_MODE
--------------- --------------- --------------- ---------- ------------------------------
SCOTT           EMP             TABLE                  141 RX/SX(行排他ロック)
SCOTT           EMP             TABLE                  134 RX/SX(行排他ロック)

--ロックモード3ロック取得(update)
SQL> update scott.emp set SAL = 1000 where EMPNO = 7369;
★応答なし

※別セッションでロック状況確認
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
       134      63853 HR                           141
       141      35627 SCOTT

SQL> select SID, TYPE, decode(LMODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode, BLOCK from v$lock where type in('TX', 'TM');

       SID TYPE   LCK_MODE                            BLOCK
---------- ------ ------------------------------ ----------
       134 TM     RX/SX(行排他ロック)                     2
       134 TX     NONE(ロックなし)                        0
       141 TX     X(排他ロック)                           1
       141 TM     RX/SX(行排他ロック)                     2

--DDL(DROP TABLE)
SQL> drop table scott.emp;
drop table scott.emp
                 *
行1でエラーが発生しました。:
ORA-00054: リソース・ビジー。NOWAITが指定されているか、タイムアウトしました

※別セッションでロック状況確認
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
       134      63853 HR
       141      35627 SCOTT

SQL> select SID, TYPE, decode(LMODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode, BLOCK from v$lock where type in('TX', 'TM');

       SID TYPE   LCK_MODE                            BLOCK
---------- ------ ------------------------------ ----------
       141 TX     X(排他ロック)                           2
       141 TM     RX/SX(行排他ロック)                     2

3. セッション1(SCOTT)で特定のデータに対してロックモード2のロック取得(LOCK TABLE)
SQL> LOCK TABLE emp IN ROW SHARE MODE nowait;

表がロックされました。

4. セッション2(HR)で同じデータに対してロックモード1~6のロック取得
--ロックモード3ロック取得(insert)
SQL> insert into scott.emp(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) values(9999, 'TEST', 'DBA', 7369, sysdate, 2000, 500, 10);

1行が作成されました。

※別セッションでロック状況確認
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
        12       3652 HR
       128       9275 SCOTT

SQL> select SID, TYPE, decode(LMODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode, BLOCK from v$lock where type in('TX', 'TM');

       SID TYPE   LCK_MODE                            BLOCK
---------- ------ ------------------------------ ----------
        12 TX     X(排他ロック)                           2
        12 TM     RX/SX(行排他ロック)                     2
       128 TM     RS/SS(行共有ロック)                     2

--ロックモード3ロック取得(update)
SQL> update scott.emp set SAL = 1000 where EMPNO = 7369;

1行が更新されました。

※別セッションでロック状況確認
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
        12       3652 HR
       128       9275 SCOTT

SQL> select SID, TYPE, decode(LMODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode, BLOCK from v$lock where type in('TX', 'TM');

       SID TYPE   LCK_MODE                            BLOCK
---------- ------ ------------------------------ ----------
        12 TX     X(排他ロック)                           2
        12 TM     RX/SX(行排他ロック)                     2
       128 TM     RS/SS(行共有ロック)                     2

--DDL(DROP TABLE)
SQL> drop table scott.emp;
drop table scott.emp
                 *
行1でエラーが発生しました。:
ORA-00054: リソース・ビジー。NOWAITが指定されているか、タイムアウトしました

※別セッションでロック状況確認
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
        12       3652 HR
       128       9275 SCOTT

SQL> select SID, TYPE, decode(LMODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode, BLOCK from v$lock where type in('TX', 'TM');

       SID TYPE   LCK_MODE                            BLOCK
---------- ------ ------------------------------ ----------
       128 TM     RS/SS(行共有ロック)                     2

 

③ロックモード3に対するロック取得検証
特定のデータに対してロックモード3のロックを取得し、同じデータに対して別セッションからロックモード1~6のロックが取得できるか検証します

【検証手順】
1. セッション1(SCOTT)で特定のデータに対してロックモード3のロック取得(insert)
2. セッション2(HR)で同じデータに対してロックモード1~6のロック取得
3. セッション1(SCOTT)で特定のデータに対してロックモード3のロック取得(update)
4. セッション2(HR)で同じデータに対してロックモード1~6のロック取得
5. セッション1(SCOTT)で特定のデータに対してロックモード3のロック取得(delete)
6. セッション2(HR)で同じデータに対してロックモード1~6のロック取得
7. セッション1(SCOTT)で特定のデータに対してロックモード3のロック取得(LOCK TABLE)
8. セッション2(HR)で同じデータに対してロックモード1~6のロック取得

【作業ログ】

1. セッション1(SCOTT)で特定のデータに対してロックモード3のロック取得(insert)
SQL> insert into scott.emp(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) values(9999, 'TEST', 'DBA', 7369, sysdate, 2000, 500, 10);

1行が作成されました。

2. セッション2(HR)で同じデータに対してロックモード1~6のロック取得
--ロックモード3ロック取得(insert)
SQL> insert into scott.emp(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) values(9999, 'TEST', 'DBA', 7369, sysdate, 2000, 500, 10);
★応答なし

※別セッションでロック状況確認
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
       257      48116 SCOTT
       372      65105 HR                           257

SQL> select SID, TYPE, decode(LMODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode, BLOCK from v$lock where type in('TX', 'TM');

       SID TYPE   LCK_MODE                            BLOCK
---------- ------ ------------------------------ ----------
       257 TX     X(排他ロック)                           1
       257 TM     RX/SX(行排他ロック)                     2
       372 TX     X(排他ロック)                           2
       372 TM     RX/SX(行排他ロック)                     2
       372 TX     NONE(ロックなし)                        0

3. セッション1(SCOTT)で特定のデータに対してロックモード3のロック取得(update)
SQL> update scott.emp set SAL = 1000 where EMPNO = 7369;

1行が更新されました。

4. セッション2(HR)で同じデータに対してロックモード1~6のロック取得
--ロックモード3ロック取得(insert)
SQL> insert into scott.emp(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) values(9999, 'TEST', 'DBA', 7369, sysdate, 2000, 500, 10);

1行が作成されました。

※別セッションでロック状況確認
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
       257      48116 SCOTT
       372      65105 HR

SQL> select SID, TYPE, decode(LMODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode, BLOCK from v$lock where type in('TX', 'TM');

       SID TYPE   LCK_MODE                            BLOCK
---------- ------ ------------------------------ ----------
       257 TX     X(排他ロック)                           2
       257 TM     RX/SX(行排他ロック)                     2
       372 TX     X(排他ロック)                           2
       372 TM     RX/SX(行排他ロック)                     2

--ロックモード3ロック取得(update)
SQL> update scott.emp set SAL = 1000 where EMPNO = 7369;
★応答なし

※別セッションでロック状況確認
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
       257      48116 SCOTT
       372      65105 HR

SQL> select SID, TYPE, decode(LMODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode, BLOCK from v$lock where type in('TX', 'TM');

       SID TYPE   LCK_MODE                            BLOCK
---------- ------ ------------------------------ ----------
       257 TX     X(排他ロック)                           1
       257 TM     RX/SX(行排他ロック)                     2
       372 TM     RX/SX(行排他ロック)                     2
       372 TX     NONE(ロックなし)                        0

--DDL(DROP TABLE)
SQL> drop table scott.emp;
drop table scott.emp
                 *
行1でエラーが発生しました。:
ORA-00054: リソース・ビジー。NOWAITが指定されているか、タイムアウトしました

※別セッションでロック状況確認
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
       257      48116 SCOTT
       372      65105 HR

SQL> select SID, TYPE, decode(LMODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode, BLOCK from v$lock where type in('TX', 'TM');

       SID TYPE   LCK_MODE                            BLOCK
---------- ------ ------------------------------ ----------
       257 TX     X(排他ロック)                           2
       257 TM     RX/SX(行排他ロック)                     2

5. セッション1(SCOTT)で特定のデータに対してロックモード3のロック取得(delete)
SQL> delete from scott.emp where EMPNO = 7369;

1行が削除されました。

6. セッション2(HR)で同じデータに対してロックモード1~6のロック取得
--ロックモード3ロック取得(insert)
SQL> insert into scott.emp(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) values(9999, 'TEST', 'DBA', 7369, sysdate, 2000, 500, 10);

1行が作成されました。

※別セッションでロック状況確認
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
       257      48116 SCOTT
       372      65105 HR

SQL> select SID, TYPE, decode(LMODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode, BLOCK from v$lock where type in('TX', 'TM');

       SID TYPE   LCK_MODE                            BLOCK
---------- ------ ------------------------------ ----------
       257 TX     X(排他ロック)                           2
       257 TM     RX/SX(行排他ロック)                     2
       372 TX     X(排他ロック)                           2
       372 TM     RX/SX(行排他ロック)                     2

--ロックモード3ロック取得(update)
SQL> update scott.emp set SAL = 1000 where EMPNO = 7369;
★応答なし

※別セッションでロック状況確認
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
       257      48116 SCOTT
       372      65105 HR                           257

SQL> select SID, TYPE, decode(LMODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode, BLOCK from v$lock where type in('TX', 'TM');

       SID TYPE   LCK_MODE                            BLOCK
---------- ------ ------------------------------ ----------
       257 TX     X(排他ロック)                           1
       257 TM     RX/SX(行排他ロック)                     2
       372 TM     RX/SX(行排他ロック)                     2
       372 TX     NONE(ロックなし)                        0

--DDL(DROP TABLE)
SQL> drop table scott.emp;
drop table scott.emp
                 *
行1でエラーが発生しました。:
ORA-00054: リソース・ビジー。NOWAITが指定されているか、タイムアウトしました

※別セッションでロック状況確認
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
       257      48116 SCOTT
       372      65105 HR

SQL> select SID, TYPE, decode(LMODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode, BLOCK from v$lock where type in('TX', 'TM');

       SID TYPE   LCK_MODE                            BLOCK
---------- ------ ------------------------------ ----------
       257 TX     X(排他ロック)                           2
       257 TM     RX/SX(行排他ロック)                     2

7. セッション1(SCOTT)で特定のデータに対してロックモード3のロック取得(LOCK TABLE)
SQL> LOCK TABLE emp IN ROW EXCLUSIVE MODE nowait;

表がロックされました。

8. セッション2(HR)で同じデータに対してロックモード1~6のロック取得
--ロックモード3ロック取得(insert)
SQL> insert into scott.emp(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) values(9999, 'TEST', 'DBA', 7369, sysdate, 2000, 500, 10);

1行が作成されました。

※別セッションでロック状況確認
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
        17      25120 HR
       355      64736 SCOTT

SQL> select SID, TYPE, decode(LMODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode, BLOCK from v$lock where type in('TX', 'TM');

       SID TYPE   LCK_MODE                            BLOCK
---------- ------ ------------------------------ ----------
        17 TX     X(排他ロック)                           2
        17 TM     RX/SX(行排他ロック)                     2
       355 TM     RX/SX(行排他ロック)                     2

--ロックモード3ロック取得(update)
SQL> update scott.emp set SAL = 1000 where EMPNO = 7369;

1行が更新されました。

※別セッションでロック状況確認
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
        17      25120 HR
       355      64736 SCOTT

SQL> select SID, TYPE, decode(LMODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode, BLOCK from v$lock where type in('TX', 'TM');

       SID TYPE   LCK_MODE                            BLOCK
---------- ------ ------------------------------ ----------
        17 TX     X(排他ロック)                           2
        17 TM     RX/SX(行排他ロック)                     2
       355 TM     RX/SX(行排他ロック)                     2

--DDL(DROP TABLE)
SQL> drop table scott.emp;
drop table scott.emp
                 *
行1でエラーが発生しました。:
ORA-00054: リソース・ビジー。NOWAITが指定されているか、タイムアウトしました

※別セッションでロック状況確認
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
        17      25120 HR
       355      64736 SCOTT

SQL> select SID, TYPE, decode(LMODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode, BLOCK from v$lock where type in('TX', 'TM');

       SID TYPE   LCK_MODE                            BLOCK
---------- ------ ------------------------------ ----------
       355 TM     RX/SX(行排他ロック)                     2

 

④ロックモード4に対するロック取得検証

特定のデータに対してロックモード4のロックを取得し、同じデータに対して別セッションからロックモード1~6のロックが取得できるか検証します

【検証手順】
1. セッション1(SCOTT)で特定のデータに対してロックモード4のロック取得(LOCK TABLE)
2. セッション2(HR)で同じデータに対してロックモード1~6のロック取得

【作業ログ】

1. セッション1(SCOTT)で特定のデータに対してロックモード4のロック取得(LOCK TABLE)
SQL> LOCK TABLE emp IN SHARE MODE nowait;

表がロックされました。

2. セッション2(HR)で同じデータに対してロックモード1~6のロック取得
--ロックモード3ロック取得(insert)
SQL> insert into scott.emp(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) values(9999, 'TEST', 'DBA', 7369, sysdate, 2000, 500, 10);
★応答なし

※別セッションでロック状況確認
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
       137       7809 SCOTT
       371      20677 HR

SQL> select SID, TYPE, decode(LMODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode, BLOCK from v$lock where type in('TX', 'TM');

       SID TYPE   LCK_MODE                            BLOCK
---------- ------ ------------------------------ ----------
       137 TM     S(共有ロック)                           1
       371 TM     NONE(ロックなし)                        0

--ロックモード3ロック取得(update)
SQL> update scott.emp set SAL = 1000 where EMPNO = 7369;
★応答なし

※別セッションでロック状況確認
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
       137       7809 SCOTT
       371      20677 HR                           137

SQL> select SID, TYPE, decode(LMODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode, BLOCK from v$lock where type in('TX', 'TM');

       SID TYPE   LCK_MODE                            BLOCK
---------- ------ ------------------------------ ----------
       137 TM     S(共有ロック)                           1
       371 TM     NONE(ロックなし)                        0

--DDL(DROP TABLE)
SQL> drop table scott.emp;
drop table scott.emp
                 *
行1でエラーが発生しました。:
ORA-00054: リソース・ビジー。NOWAITが指定されているか、タイムアウトしました

※別セッションでロック状況確認
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
       137       7809 SCOTT
       371      20677 HR

SQL> select SID, TYPE, decode(LMODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode, BLOCK from v$lock where type in('TX', 'TM');

       SID TYPE   LCK_MODE                            BLOCK
---------- ------ ------------------------------ ----------
       137 TM     S(共有ロック)                           2

 

⑤ロックモード5に対するロック取得検証
特定のデータに対してロックモード5のロックを取得し、同じデータに対して別セッションからロックモード1~6のロックが取得できるか検証します

【検証手順】
1. セッション1(SCOTT)で特定のデータに対してロックモード5のロック取得(LOCK TABLE)
2. セッション2(HR)で同じデータに対してロックモード1~6のロック取得

【作業ログ】

1. セッション1(SCOTT)で特定のデータに対してロックモード5のロック取得(LOCK TABLE)
SQL> LOCK TABLE emp IN SHARE ROW EXCLUSIVE MODE nowait;

表がロックされました。

2. セッション2(HR)で同じデータに対してロックモード1~6のロック取得
--ロックモード3ロック取得(insert)
SQL> insert into scott.emp(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) values(9999, 'TEST', 'DBA', 7369, sysdate, 2000, 500, 10);
★応答なし

※別セッションでロック状況確認
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
       137      61707 SCOTT
       371      28633 HR                           137

SQL> select SID, TYPE, decode(LMODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode, BLOCK from v$lock where type in('TX', 'TM');

       SID TYPE   LCK_MODE                            BLOCK
---------- ------ ------------------------------ ----------
       137 TM     SRX/SSX(共有行排他ロック)               1
       371 TM     NONE(ロックなし)                        0

--ロックモード3ロック取得(update)
SQL> update scott.emp set SAL = 1000 where EMPNO = 7369;
★応答なし

※別セッションでロック状況確認
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
       137      61707 SCOTT
       371      28633 HR                           137

SQL> select SID, TYPE, decode(LMODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode, BLOCK from v$lock where type in('TX', 'TM');

       SID TYPE   LCK_MODE                            BLOCK
---------- ------ ------------------------------ ----------
       137 TM     SRX/SSX(共有行排他ロック)               1
       371 TM     NONE(ロックなし)                        0

--DDL(DROP TABLE)
SQL> drop table scott.emp;
drop table scott.emp
                 *
行1でエラーが発生しました。:
ORA-00054: リソース・ビジー。NOWAITが指定されているか、タイムアウトしました

※別セッションでロック状況確認
SQL> --#ついでに
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
       137      61707 SCOTT
       371      28633 HR

SQL> select SID, TYPE, decode(LMODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode, BLOCK from v$lock where type in('TX', 'TM');

       SID TYPE   LCK_MODE                            BLOCK
---------- ------ ------------------------------ ----------
       137 TM     SRX/SSX(共有行排他ロック)               2

 

⑥ロックモード6に対するロック取得検証
特定のデータに対してロックモード6のロックを取得し、同じデータに対して別セッションからロックモード1~6のロックが取得できるか検証します

【検証手順】
1. セッション1(SCOTT)で特定のデータに対してロックモード6のロック取得(LOCK TABLE)
2. セッション2(HR)で同じデータに対してロックモード1~6のロック取得

【作業ログ】

1. セッション1(SCOTT)で特定のデータに対してロックモード6のロック取得(LOCK TABLE)
SQL> LOCK TABLE emp IN EXCLUSIVE MODE nowait;

表がロックされました。

2. セッション2(HR)で同じデータに対してロックモード1~6のロック取得
--ロックモード3ロック取得(insert)
SQL> insert into scott.emp(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) values(9999, 'TEST', 'DBA', 7369, sysdate, 2000, 500, 10);
★応答なし

※別セッションでロック状況確認
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
        12      12189 HR                           377
       377      62284 SCOTT

SQL> select SID, TYPE, decode(LMODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode, BLOCK from v$lock where type in('TX', 'TM');

       SID TYPE   LCK_MODE                            BLOCK
---------- ------ ------------------------------ ----------
        12 TM     NONE(ロックなし)                        0
       377 TM     X(排他ロック)                           1

--ロックモード3ロック取得(update)
SQL> update scott.emp set SAL = 1000 where EMPNO = 7369;
★応答なし

※別セッションでロック状況確認
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
        12      12189 HR                           377
       377      62284 SCOTT

SQL> select SID, TYPE, decode(LMODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode, BLOCK from v$lock where type in('TX', 'TM');

       SID TYPE   LCK_MODE                            BLOCK
---------- ------ ------------------------------ ----------
        12 TM     NONE(ロックなし)                        0
       377 TM     X(排他ロック)                           1

--DDL(DROP TABLE)
SQL> drop table scott.emp;
drop table scott.emp
                 *
行1でエラーが発生しました。:
ORA-00054: リソース・ビジー。NOWAITが指定されているか、タイムアウトしました

※別セッションでロック状況確認
SQL> select sid, serial#, username, blocking_session from v$session where username in('SCOTT', 'HR');

       SID    SERIAL# USERNAME        BLOCKING_SESSION
---------- ---------- --------------- ----------------
        12      12189 HR
       377      62284 SCOTT

SQL> select SID, TYPE, decode(LMODE, 0, 'NONE(ロックなし)',1 ,'NULL',2, 'RS/SS(行共有ロック)',3 , 'RX/SX(行排他ロック)',4, 'S(共有ロック)',5, 'SRX/SSX(共有行排他ロック)',6 , 'X(排他ロック)', 'Unknown') as lck_mode, BLOCK from v$lock where type in('TX', 'TM');

       SID TYPE   LCK_MODE                            BLOCK
---------- ------ ------------------------------ ----------
       377 TM     X(排他ロック)                           2

 

■参考資料
データの同時実行性と整合性

Oracleの監査の検証(統合監査編)

前回の従来型監査で検証した項目を統合監査で検証した場合、実際の動きにどのような違いがあるのか検証しました。

統合監査についてざっくり復習します。

概要
12cから登場した新しい監査機能で、従来型監査(必須監査、DBA監査、標準監査(標準データベース監査)、ファイングレイン監査(FGA監査))の監査証跡(監査ログ)をDBの1つのビューに集約して管理します

メリット
・監査ログを1つのビューにしたことで監査ログを探さなくて済む
 →従来型監査は監査によって監査ログをOSファイルに出力したり、DBに出力したり管理が複雑になりやすいという管理上のデメリットがありました
・監査対象のDB操作に「OSユーザが○○だったら~」、「ホスト名が○○だったら~」など細かい条件を指定可能
 →例えば従来型監査はログインを監査するという設定はできますが、「OSユーザがoracleだったらログインを監査する」という設定はできませんでした
  統合監査は「OSユーザがoracleだったらログインを監査する」という設定ができます
・Data PumpやRMANなどのツールも監査可能
・キュー書込み方式によるディスクI/O軽減(12.1)
 →従来型監査は監査が発生するたび、監査ログを実表に書き込んでいたため、その都度ディスクI/Oが発生していました
  キュー書込み方式は監査ログをSGAに溜めておき、一定のタイミングで実表へ書き込むため、ディスクI/Oが減りパフォーマンス向上が期待できます
  ※ただし、インスタンス(メモリ)障害等で情報が欠落する可能性があるという理由で12.2で非推奨になりました (参考) 監査証跡の管理
・監査管理用ロール登場

  →監査設定や参照は管理者権限(SYSDBA権限やDBAロール)が必要でしたが、監査管理用ロール(AUDIT_ADMIN、AUDIT_VIEWER)が登場したことで管理者じゃなくても監査設定や参照ができます
   AUDIT_ADMIN:監査ポリシー参照/作成/変更/削除/監査ログ参照など
   AUDIT_VIEWER:監査ログ参照

デメリット
・キュー書込み方式の場合、インスタンス(メモリ)障害等で情報が欠落する可能性がある
・DB破損してバックアップがない場合、監査ログを復旧できない可能性がある

【監査対象】
従来型監査の監査項目に加え、Data PumpやRMANなどのツールの操作(※1)
(※1) 監査の概要
【出力先】
UNIFIED_AUDIT_TRAILビュー(AUDSYS.CLI_SWP$xxx表)

■検証環境
OS:Oracle Linux 6.5
GI:Oracle Grid Infrastructure 12c Release 1 (12.1.0.2.0) Enterprise Edition

DB:Oracle Database 12c Release 1 (12.1.0.2.0) Enterprise Edition
※2ノードRAC(管理者管理型DB)

■前提
・非CDB
・統合監査のみ

■設定情報
事前に下記のとおり設定しています

統合監査設定
SQL> select inst_id, parameter, value from gv$option where parameter = 'Unified Auditing';

INST_ID PARAMETER            VALUE
------- -------------------- --------------------
      1 Unified Auditing     TRUE
      2 Unified Auditing     TRUE

【参考】統合監査有効化
1. 統合監査設定確認
SQL> select inst_id, parameter, value from gv$option where parameter = 'Unified Auditing';

INST_ID PARAMETER            VALUE
------- -------------------- --------------------
      1 Unified Auditing     FALSE
      2 Unified Auditing     FALSE

2. DB停止
[oracle@node1 ~]$ srvctl stop database -db orcl

3. 統合監査実行可能ファイル有効化
[oracle@node1 ~]$ cd $ORACLE_HOME/rdbms/lib
[oracle@node1 lib]$ pwd
/u01/app/oracle/product/12.1.0/dbhome_1/rdbms/lib
[oracle@node1 lib]$ ls -l libknlopt.a
-rw-r--r-- 1 oracle oinstall  1771642 11月 18 18:24 2022 libknlopt.a
[oracle@node1 lib]$ make -f ins_rdbms.mk uniaud_on ioracle
-rw-r--r-- 1 oracle oinstall  1771858 11月 26 14:03 2022 libknlopt.a
RACの場合、全ノードでmakeが必要です(Doc ID 2371837.1)

4. DB起動
[oracle@node1 lib]$ srvctl start database -db orcl

5. 統合監査設定確認
SQL> select inst_id, parameter, value from gv$option where parameter = 'Unified Auditing';

INST_ID PARAMETER            VALUE
------- -------------------- --------------------
      1 Unified Auditing     TRUE
      2 Unified Auditing     TRUE

ASMの監査設定
SQL> --監査ログ(OSファイル)の出力先(デフォルト)
SQL> select inst_id, name, value from gv$parameter where name = 'audit_file_dest';

INST_ID NAME                           VALUE
------- ------------------------------ ----------------------------------------
      1 audit_file_dest                /u01/app/12.1.0/grid/rdbms/audit
      2 audit_file_dest                /u01/app/12.1.0/grid/rdbms/audit

DBの監査設定
SQL> --監査ログ(OSファイル)の出力先(デフォルト)
SQL> select inst_id, name, value from gv$parameter where name = 'audit_file_dest';

INST_ID NAME                           VALUE
------- ------------------------------ ----------------------------------------
      1 audit_file_dest                /u01/app/oracle/admin/orcl/adump
      2 audit_file_dest                /u01/app/oracle/admin/orcl/adump

SQL> --DBA監査設定(デフォルト)
SQL> select inst_id, name, value from gv$parameter where name = 'audit_sys_operations';

INST_ID NAME                           VALUE
------- ------------------------------ ----------------------------------------
      1 audit_sys_operations           TRUE
      2 audit_sys_operations           TRUE

SQL> --標準監査設定(前回の従来型監査検証の設定値)
SQL> select inst_id, name, value from gv$parameter where name = 'audit_trail';

INST_ID NAME                           VALUE
------- ------------------------------ ----------------------------------------
      1 audit_trail                    DB, EXTENDED
      2 audit_trail                    DB, EXTENDED

※統合監査が有効の場合、audit_file_dest、audit_sys_operations、audit_trail初期化パラメータは無効です
 DBインスタンス停止中の監査ログ(OSファイル)は「/u01/app/oracle/admin/orcl/adump」ではなく、「/u01/app/oracle/audit/<インスタンス名>」配下に出力されます

 

■検証パターン
①必須監査
②DBA監査
③標準監査(標準データベース監査)
ファイングレイン監査(FGA監査)
※従来型監査で検証した値ベース監査は事前に定義された機能ではなく、統合監査との動きを比較できないため検証は割愛

■検証
①必須監査
特権ユーザの必須監査対象の操作が監査されるか検証します
検証手順は従来型監査の検証と同じです

【検証手順】
1. ASMインスタンス停止
2. ASMインスタンス起動
3. ASMインスタンス接続
4. DBインスタンス停止
5. DBインスタンス起動
6. DBインスタンス接続

【想定】
必須監査対象の操作が監査されること

【検証結果】
・必須監査対象の操作のうち、下記は監査された
- ASMインスタンス起動/停止、接続の監査ログはOSファイルに出力された(ビューには出力されなかった)
- DBインスタンス起動/停止の監査ログはビューに出力された
・必須監査対象の操作のうち、下記は監査されなかった
- DBインスタンス接続の監査ログはOSファイル、ビューどちらにも出力されなかった(※1)

(※1)
(DBインスタンス起動/停止以外の)SYSDBA権限の操作は監査されないとのことでした(Doc ID 2024280.1)
全操作を監査する監査ポリシー作成後、SYSDBA権限を持つ特権ユーザ(特権アカウント)に監査ポリシーを適用して対応します

【作業ログ】

1. ASMインスタンス停止
[root@node1 ~]# /u01/app/12.1.0/grid/bin/crsctl stop crs

#監査ログ確認
[root@node1 ~]# cat /u01/app/12.1.0/grid/rdbms/audit/+ASM1_ora_667_20221126152211413052143795.aud
Sat Nov 26 15:22:14 2022 +09:00
LENGTH : '154'
ACTION :[18] 'SHUTDOWN IMMEDIATE'
DATABASE USER:[1] '/'
PRIVILEGE :[6] 'SYSASM'
CLIENT USER:[4] 'grid'
CLIENT TERMINAL:[0] ''
STATUS:[1] '0'
DBID:[0] ''
★SYSASM権限でASMインスタンス停止した監査ログが出力された

2. ASMインスタンス起動
[root@node1 ~]# /u01/app/12.1.0/grid/bin/crsctl start crs -wait

#監査ログ確認
[root@node1 ~]# cat /u01/app/12.1.0/grid/rdbms/audit/+ASM1_ora_6003_20221126152518149265143795.aud
Sat Nov 26 15:25:18 2022 +09:00
LENGTH : '142'
ACTION :[7] 'STARTUP'
DATABASE USER:[1] '/'
PRIVILEGE :[6] 'SYSASM'
CLIENT USER:[4] 'grid'
CLIENT TERMINAL:[0] ''
STATUS:[1] '0'
DBID:[0] ''
★SYSASM権限でASMインスタンス起動した監査ログが出力された

3. ASMインスタンス接続
[grid@node1 ~]$ sqlplus / as sysasm

#監査ログ確認
[root@node1 ~]# cat /u01/app/12.1.0/grid/rdbms/audit/+ASM1_ora_9982_20221126153242132738143795.aud
Sat Nov 26 15:32:42 2022 +09:00
LENGTH : '147'
ACTION :[7] 'CONNECT'
DATABASE USER:[1] '/'
PRIVILEGE :[6] 'SYSASM'
CLIENT USER:[4] 'grid'
CLIENT TERMINAL:[5] 'pts/0'
STATUS:[1] '0'
DBID:[0] ''
★SYSASM権限でASMインスタンス接続した監査ログが出力された

4. DBインスタンス停止
[oracle@node1 ~]$ srvctl stop database -db orcl

#監査ログ確認
[root@node1 ~]# ls -ltr /u01/app/oracle/audit/orcl1
-rw-r----- 1 oracle asmadmin 1536 11月 26 15:37 2022 ora_audit_1138.bin
-rw-r----- 1 oracle asmadmin 1536 11月 26 15:37 2022 ora_audit_0138.bin
★中身は見れないがSYSDBA権限でDBインスタンス停止と同時刻の監査ログが出力された

5. DBインスタンス起動
[oracle@node1 ~]$ srvctl start database -db orcl

#監査ログ確認(OSファイル)
[root@node1 ~]# ls -ltr /u01/app/oracle/audit/orcl1
-rw-r----- 1 oracle asmadmin  2560 11月 26 15:42 2022 ora_audit_00.bin
-rw-r----- 1 oracle asmadmin 10752 11月 26 15:42 2022 ora_audit_1355.bin
-rw-r----- 1 oracle asmadmin  1536 11月 26 15:42 2022 ora_audit_116.bin
★中身は見れないがSYSDBA権限でDBインスタンス起動と同時刻の監査ログが出力された

#監査ログ確認(ビュー)
SQL> select TO_CHAR(EVENT_TIMESTAMP, 'YYYY/MM/DD HH24:MI:SS') as EVT_TS,AUDIT_TYPE,USERHOST,DBUSERNAME,ACTION_NAME,OBJECT_SCHEMA,OBJECT_NAME,SQL_TEXT from UNIFIED_AUDIT_TRAIL order by EVENT_TIMESTAMP desc;

EVT_TS               AUDIT_TYPE           USERHOST             DBUSERNAME      ACTION_NAME     OBJECT_SCHEMA   OBJECT_NAME     SQL_TEXT
-------------------- -------------------- -------------------- --------------- --------------- --------------- --------------- --------------------------------------------------------------------------------
2022/11/26 15:42:21  Standard             Not Available        SYS             STARTUP                                         STARTUP
2022/11/26 15:42:21  Standard             Not Available        SYS             STARTUP                                         STARTUP
2022/11/26 15:38:11  Standard             node2.oracle12c.jp   SYS             SHUTDOWN                                        SHUTDOWN IMMEDIATE
2022/11/26 15:37:49  Standard             node1.oracle12c.jp   SYS             SHUTDOWN                                        SHUTDOWN IMMEDIATE
★「4. DBインスタンス停止」、「5. DBインスタンス起動」の監査ログが出力された

6. DBインスタンス接続
[oracle@node1 ~]$ sqlplus / as sysdba

#監査ログ確認
★監査ログはOSファイル、ビューどちらにも出力されなかった
理由は上記「【検証結果】」で書いた通りです

【参考】全操作を監査する監査ポリシー作成後、SYS(SYSDBA権限)に監査ポリシー適用
1. 全操作を監査する監査ポリシー作成
SQL> CREATE AUDIT POLICY POL_ALL_ACTIONS
  2  ACTIONS ALL
  3  WHEN 'SYS_CONTEXT(''USERENV'', ''OS_USER'') = ''oracle'' AND (SYS_CONTEXT(''USERENV'', ''HOST'') = ''node1.oracle12c.jp'' OR SYS_CONTEXT(''USERENV'', ''HOST'') = ''node2.oracle12c.jp'')'
  4  EVALUATE PER STATEMENT;

監査ポリシーが作成されました。

★WHEN句で監査ポリシーに対する監査条件を指定できます
 本検証では「OSユーザがoracle」で「ホスト名がnode1.oracle12c.jpかnode2.oracle12c.jp」の場合、全操作を監査するという条件を指定しています

SQL> select POLICY_NAME,AUDIT_CONDITION,AUDIT_OPTION,OBJECT_SCHEMA,OBJECT_NAME from audit_unified_policies where POLICY_NAME like 'POL%' order by POLICY_NAME,AUDIT_OPTION,OBJECT_SCHEMA,OBJECT_NAME;

POLICY_NAME          AUDIT_CONDITION                                                   AUDIT_OPTION    OBJECT_SCHEMA   OBJECT_NAME
-------------------- ----------------------------------------------------------------- --------------- --------------- ---------------
POL_ALL_ACTIONS      SYS_CONTEXT('USERENV', 'OS_USER') = 'oracle' AND (SYS_CONTEXT('US ALL             NONE            NONE
                     ERENV', 'HOST') = 'node1.oracle12c.jp' OR SYS_CONTEXT('USERENV',
                     'HOST') = 'node2.oracle12c.jp')

2. 全操作を監査する監査ポリシー適用
SQL> audit policy POL_ALL_ACTIONS by sys;

監査が成功しました。

SQL> select * from audit_unified_enabled_policies;

USER_NAME       POLICY_NAME          ENABLED_OPT              SUCCESS   FAILURE
--------------- -------------------- ------------------------ --------- ---------
SYS             POL_ALL_ACTIONS      BY                       YES       YES

★全操作を監査する監査ポリシー適用後、SYS(SYSDBA権限)でDBインスタンス接続したら監査ログが出力された

SQL> select TO_CHAR(EVENT_TIMESTAMP, 'YYYY/MM/DD HH24:MI:SS') as EVT_TS,AUDIT_TYPE,USERHOST,DBUSERNAME,ACTION_NAME,OBJECT_SCHEMA,OBJECT_NAME,SQL_TEXT from UNIFIED_AUDIT_TRAIL order by EVENT_TIMESTAMP desc;

EVT_TS               AUDIT_TYPE           USERHOST             DBUSERNAME      ACTION_NAME     OBJECT_SCHEMA   OBJECT_NAME     SQL_TEXT
-------------------- -------------------- -------------------- --------------- --------------- --------------- --------------- --------------------------------------------------------------------------------
2022/11/26 16:04:47  Standard             node1.oracle12c.jp   SYS             LOGON

 

②DBA監査
特権ユーザのDBA監査対象の操作が監査されるか検証します
検証手順は従来型監査の検証と同じです

【検証手順】
1. データディクショナリ問合せ
2. データ更新

【想定】
DBA監査対象の操作が監査されること

【検証結果】
DBA監査対象の操作が監査された

【作業ログ】

1. データディクショナリ問合せ
SQL> select count(*) from dba_users;

  COUNT(*)
----------
        42

#監査ログ確認
SQL> select TO_CHAR(EVENT_TIMESTAMP, 'YYYY/MM/DD HH24:MI:SS') as EVT_TS,AUDIT_TYPE,USERHOST,DBUSERNAME,ACTION_NAME,OBJECT_SCHEMA,OBJECT_NAME,SQL_TEXT from UNIFIED_AUDIT_TRAIL order by EVENT_TIMESTAMP desc;

EVT_TS               AUDIT_TYPE           USERHOST             DBUSERNAME      ACTION_NAME     OBJECT_SCHEMA   OBJECT_NAME          SQL_TEXT
-------------------- -------------------- -------------------- --------------- --------------- --------------- -------------------- --------------------------------------------------------------------------------
2022/11/26 16:09:15  Standard             node1.oracle12c.jp   SYS             SELECT          SYS             from$_subquery$_004  select count(*) from dba_users
★SYSDBA権限でデータディクショナリアクセスした監査ログが出力された

2. データ更新
SQL> update scott.emp set SAL = 800 where EMPNO = 7369;

1行が更新されました。

#監査ログ確認
SQL> select TO_CHAR(EVENT_TIMESTAMP, 'YYYY/MM/DD HH24:MI:SS') as EVT_TS,AUDIT_TYPE,USERHOST,DBUSERNAME,ACTION_NAME,OBJECT_SCHEMA,OBJECT_NAME,SQL_TEXT from UNIFIED_AUDIT_TRAIL order by EVENT_TIMESTAMP desc;

EVT_TS               AUDIT_TYPE           USERHOST             DBUSERNAME      ACTION_NAME     OBJECT_SCHEMA   OBJECT_NAME     SQL_TEXT
-------------------- -------------------- -------------------- --------------- --------------- --------------- --------------- --------------------------------------------------------------------------------
2022/11/26 16:10:43  Standard             node1.oracle12c.jp   SYS             UPDATE          SCOTT           EMP             update scott.emp set SAL = 800 where EMPNO = 7369
★SYSDBA権限でデータ更新した監査ログが出力された

 

③標準監査(標準データベース監査)
一般ユーザの標準監査対象の操作が監査されるか検証します
検証手順は従来型監査の検証と同じです

【検証手順】
1. 監査ポリシー作成
2. 監査ポリシー適用
3. ノード1からSCOTTでDBログイン(間違ったパスワード)
4. ノード2からSCOTTでDBログイン(間違ったパスワード)
5. SCOTTでテーブル作成
6. SCOTTでテーブルにデータ挿入

【想定】
標準監査対象の操作が監査されること

【検証結果】
標準監査対象の操作が監査された

【作業ログ】

1. 監査ポリシー作成
SQL> --ログイン失敗検証用
SQL> CREATE AUDIT POLICY POL_LOGON_FAIL
  2  ACTIONS LOGON
  3  WHEN 'SYS_CONTEXT(''USERENV'', ''OS_USER'') = ''oracle'' AND (SYS_CONTEXT(''USERENV'', ''HOST'') = ''node1.oracle12c.jp'' OR SYS_CONTEXT(''USERENV'', ''HOST'') = ''node2.oracle12c.jp'')'
  4  EVALUATE PER STATEMENT;

監査ポリシーが作成されました。

SQL> --テーブル作成検証用
SQL> CREATE AUDIT POLICY POL_CRE_TBL
  2  ACTIONS CREATE TABLE
  3  WHEN 'SYS_CONTEXT(''USERENV'', ''OS_USER'') = ''oracle'' AND (SYS_CONTEXT(''USERENV'', ''HOST'') = ''node1.oracle12c.jp'' OR SYS_CONTEXT(''USERENV'', ''HOST'') = ''node2.oracle12c.jp'')'
  4  EVALUATE PER STATEMENT;

監査ポリシーが作成されました。

SQL> --データ操作検証用
SQL> CREATE AUDIT POLICY POL_OPERATION
  2  ACTIONS INSERT on scott.emp
  3  WHEN 'SYS_CONTEXT(''USERENV'', ''OS_USER'') = ''oracle'' AND (SYS_CONTEXT(''USERENV'', ''HOST'') = ''node1.oracle12c.jp'' OR SYS_CONTEXT(''USERENV'', ''HOST'') = ''node2.oracle12c.jp'')'
  4  EVALUATE PER STATEMENT;

監査ポリシーが作成されました。

SQL> select POLICY_NAME,AUDIT_CONDITION,AUDIT_OPTION,OBJECT_SCHEMA,OBJECT_NAME from audit_unified_policies where POLICY_NAME like 'POL%' order by POLICY_NAME,AUDIT_OPTION,OBJECT_SCHEMA,OBJECT_NAME;

POLICY_NAME          AUDIT_CONDITION                                                   AUDIT_OPTION    OBJECT_SCHEMA   OBJECT_NAME
-------------------- ----------------------------------------------------------------- --------------- --------------- ---------------
POL_ALL_ACTIONS      SYS_CONTEXT('USERENV', 'OS_USER') = 'oracle' AND (SYS_CONTEXT('US ALL             NONE            NONE
                     ERENV', 'HOST') = 'node1.oracle12c.jp' OR SYS_CONTEXT('USERENV',
                     'HOST') = 'node2.oracle12c.jp')

POL_CRE_TBL          SYS_CONTEXT('USERENV', 'OS_USER') = 'oracle' AND (SYS_CONTEXT('US CREATE TABLE    NONE            NONE
                     ERENV', 'HOST') = 'node1.oracle12c.jp' OR SYS_CONTEXT('USERENV',
                     'HOST') = 'node2.oracle12c.jp')

POL_LOGON_FAIL       SYS_CONTEXT('USERENV', 'OS_USER') = 'oracle' AND (SYS_CONTEXT('US LOGON           NONE            NONE
                     ERENV', 'HOST') = 'node1.oracle12c.jp' OR SYS_CONTEXT('USERENV',
                     'HOST') = 'node2.oracle12c.jp')

POL_OPERATION        SYS_CONTEXT('USERENV', 'OS_USER') = 'oracle' AND (SYS_CONTEXT('US INSERT          SCOTT           EMP
                     ERENV', 'HOST') = 'node1.oracle12c.jp' OR SYS_CONTEXT('USERENV',
                     'HOST') = 'node2.oracle12c.jp')

2. 監査ポリシー適用
SQL> --ログイン失敗検証用
SQL> audit policy POL_LOGON_FAIL by scott whenever not successful;

監査が成功しました。

SQL> --テーブル作成検証用
SQL> audit policy POL_CRE_TBL by scott;

監査が成功しました。

SQL> --データ操作検証用
SQL> audit policy POL_OPERATION;

監査が成功しました。

SQL> select * from audit_unified_enabled_policies;

USER_NAME       POLICY_NAME          ENABLED_OPT              SUCCESS   FAILURE
--------------- -------------------- ------------------------ --------- ---------
SYS             POL_ALL_ACTIONS      BY                       YES       YES
SCOTT           POL_LOGON_FAIL       BY                       NO        YES
SCOTT           POL_CRE_TBL          BY                       YES       YES
ALL USERS       POL_OPERATION        BY                       YES       YES

3. ノード1からSCOTTでDBログイン(間違ったパスワード)
[oracle@node1 ~]$ sqlplus scott/machigai

SQL*Plus: Release 12.1.0.2.0 Production on 土 11月 26 16:17:57 2022

Copyright (c) 1982, 2014, Oracle.  All rights reserved.

ERROR:
ORA-01017: ユーザー名/パスワードが無効です。ログオンは拒否されました。

#監査ログ確認
SQL> select TO_CHAR(EVENT_TIMESTAMP, 'YYYY/MM/DD HH24:MI:SS') as EVT_TS,AUDIT_TYPE,USERHOST,DBUSERNAME,ACTION_NAME,OBJECT_SCHEMA,OBJECT_NAME,SQL_TEXT from UNIFIED_AUDIT_TRAIL order by EVENT_TIMESTAMP desc;

EVT_TS               AUDIT_TYPE           USERHOST             DBUSERNAME      ACTION_NAME     OBJECT_SCHEMA   OBJECT_NAME     SQL_TEXT
-------------------- -------------------- -------------------- --------------- --------------- --------------- --------------- --------------------------------------------------------------------------------
2022/11/26 16:17:58  Standard             node1.oracle12c.jp   SCOTT           LOGON
★ノード1でSCOTTがログイン失敗した監査ログが出力された

4. ノード2からSCOTTでDBログイン(間違ったパスワード)
[oracle@node2 ~]$ sqlplus scott/machigai

SQL*Plus: Release 12.1.0.2.0 Production on 土 11月 26 16:20:39 2022

Copyright (c) 1982, 2014, Oracle.  All rights reserved.

ERROR:
ORA-01017: ユーザー名/パスワードが無効です。ログオンは拒否されました。

#監査ログ確認
SQL> select TO_CHAR(EVENT_TIMESTAMP, 'YYYY/MM/DD HH24:MI:SS') as EVT_TS,AUDIT_TYPE,USERHOST,DBUSERNAME,ACTION_NAME,OBJECT_SCHEMA,OBJECT_NAME,SQL_TEXT from UNIFIED_AUDIT_TRAIL order by EVENT_TIMESTAMP desc;

EVT_TS               AUDIT_TYPE           USERHOST             DBUSERNAME      ACTION_NAME     OBJECT_SCHEMA   OBJECT_NAME     SQL_TEXT
-------------------- -------------------- -------------------- --------------- --------------- --------------- --------------- --------------------------------------------------------------------------------
2022/11/26 16:20:40  Standard             node2.oracle12c.jp   SCOTT           LOGON
★ノード2でSCOTTがログイン失敗した監査ログが出力された

5. SCOTTでテーブル作成
SQL> create table test_table(id number, text varchar(10), create_time date);

表が作成されました。

#監査ログ確認
SQL> select TO_CHAR(EVENT_TIMESTAMP, 'YYYY/MM/DD HH24:MI:SS') as EVT_TS,AUDIT_TYPE,USERHOST,DBUSERNAME,ACTION_NAME,OBJECT_SCHEMA,OBJECT_NAME,SQL_TEXT from UNIFIED_AUDIT_TRAIL order by EVENT_TIMESTAMP desc;

EVT_TS               AUDIT_TYPE           USERHOST             DBUSERNAME      ACTION_NAME     OBJECT_SCHEMA   OBJECT_NAME     SQL_TEXT
-------------------- -------------------- -------------------- --------------- --------------- --------------- --------------- --------------------------------------------------------------------------------
2022/11/26 16:23:14  Standard             node1.oracle12c.jp   SCOTT           CREATE TABLE    SCOTT           TEST_TABLE      create table test_table(id number, text varchar(10), create_time date)
★SCOTTがCREATE TABLE権限を使用した監査ログが出力された

6. SCOTTでテーブルにデータ挿入
SQL> insert into emp(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) values(9999, 'TEST', 'DBA', 7369, sysdate, 2000, 500, 10);

1行が作成されました。

#監査ログ確認
SQL> select TO_CHAR(EVENT_TIMESTAMP, 'YYYY/MM/DD HH24:MI:SS') as EVT_TS,AUDIT_TYPE,USERHOST,DBUSERNAME,ACTION_NAME,OBJECT_SCHEMA,OBJECT_NAME,SQL_TEXT from UNIFIED_AUDIT_TRAIL order by EVENT_TIMESTAMP desc;

EVT_TS               AUDIT_TYPE           USERHOST             DBUSERNAME      ACTION_NAME     OBJECT_SCHEMA   OBJECT_NAME     SQL_TEXT
-------------------- -------------------- -------------------- --------------- --------------- --------------- --------------- --------------------------------------------------------------------------------
2022/11/26 16:24:36  Standard             node1.oracle12c.jp   SCOTT           INSERT          SCOTT           EMP             insert into emp(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) values(99
★SCOTTがINSERT権限を使用した監査ログが出力された

 

ファイングレイン監査(FGA監査)
ユーザのファイングレイン監査対象の操作が監査されるか検証します
検証手順は従来型監査の検証と同じです

【検証手順】
1. ファイングレイン監査ポリシー作成
2. 監査ポリシーに該当するパターン1
3. 監査ポリシーに該当するパターン2
4. 監査ポリシーに該当しないパターン1
5. 監査ポリシーに該当しないパターン2

【想定】
ファイングレイン監査対象の操作が監査されること

【検証結果】
ファイングレイン監査対象の操作が監査された

【作業ログ】

1. ファイングレイン監査ポリシー作成
SQL> exec dbms_fga.add_policy(object_schema => 'SCOTT', object_name => 'EMP', policy_name => 'fga_audit_emp_sal', audit_condition => 'DEPTNO=10', audit_column => 'SAL', enable => TRUE, statement_types => 'SELECT');

PL/SQLプロシージャが正常に完了しました。
★DEPTNOが10のレコードのSAL列をSELECTしたら監査するように設定

2. 監査ポリシーに該当するパターン1
SQL> select empno, ename, sal, deptno from emp;

     EMPNO ENAME             SAL     DEPTNO
---------- ---------- ---------- ----------
      7369 SMITH            9999         10
      7499 ALLEN            1600         30
      7521 WARD             1250         30
      7566 JONES            2975         20
      7654 MARTIN           1250         30
      7698 BLAKE            2850         30
      7782 CLARK            2450         10
      7839 KING             5000         10
      7844 TURNER           1500         30
      7900 JAMES             950         30
      7902 FORD             3000         20
      7934 MILLER           1300         10

#監査ログ確認
SQL> select TO_CHAR(EVENT_TIMESTAMP, 'YYYY/MM/DD HH24:MI:SS') as EVT_TS,AUDIT_TYPE,USERHOST,DBUSERNAME,ACTION_NAME,OBJECT_SCHEMA,OBJECT_NAME,SQL_TEXT from UNIFIED_AUDIT_TRAIL order by EVENT_TIMESTAMP desc;

EVT_TS               AUDIT_TYPE           USERHOST             DBUSERNAME      ACTION_NAME     OBJECT_SCHEMA   OBJECT_NAME     SQL_TEXT
-------------------- -------------------- -------------------- --------------- --------------- --------------- --------------- --------------------------------------------------------------------------------
2022/11/26 16:32:37  FineGrainedAudit     node1.oracle12c.jp   SCOTT           SELECT          SCOTT           EMP             select empno, ename, sal, deptno from emp
★EMPNO7369・7782・7839・7934のレコードのDEPTNOが10で、SAL列をSELECTしているため、監査ログが出力された

3. 監査ポリシーに該当するパターン2
SQL> select empno, ename, sal, deptno from emp where deptno = 10;

     EMPNO ENAME             SAL     DEPTNO
---------- ---------- ---------- ----------
      7369 SMITH            9999         10
      7782 CLARK            2450         10
      7839 KING             5000         10
      7934 MILLER           1300         10

#監査ログ確認
SQL> select TO_CHAR(EVENT_TIMESTAMP, 'YYYY/MM/DD HH24:MI:SS') as EVT_TS,AUDIT_TYPE,USERHOST,DBUSERNAME,ACTION_NAME,OBJECT_SCHEMA,OBJECT_NAME,SQL_TEXT from UNIFIED_AUDIT_TRAIL order by EVENT_TIMESTAMP desc;

EVT_TS               AUDIT_TYPE           USERHOST             DBUSERNAME      ACTION_NAME     OBJECT_SCHEMA   OBJECT_NAME     SQL_TEXT
-------------------- -------------------- -------------------- --------------- --------------- --------------- --------------- --------------------------------------------------------------------------------
2022/11/26 16:33:42  FineGrainedAudit     node1.oracle12c.jp   SCOTT           SELECT          SCOTT           EMP             select empno, ename, sal, deptno from emp where deptno = 10
★DEPTNOを10で絞って、SAL列をSELECTしているため、監査ログが出力された

4. 監査ポリシーに該当しないパターン1
SQL> select empno, ename, sal, deptno from emp where deptno = 20;

     EMPNO ENAME             SAL     DEPTNO
---------- ---------- ---------- ----------
      7566 JONES            2975         20
      7902 FORD             3000         20

#監査ログ確認
★SAL列をSELECTしているがDEPTNOを20で絞っているため、監査ログが出力されない

5. 監査ポリシーに該当しないパターン2
SQL> select empno, ename, job, deptno from emp where deptno = 10;

     EMPNO ENAME      JOB            DEPTNO
---------- ---------- ---------- ----------
      7369 SMITH      CLERK              10
      7782 CLARK      MANAGER            10
      7839 KING       PRESIDENT          10
      7934 MILLER     CLERK              10

#監査ログ確認
★SAL列をSELECTしていないため、監査ログが出力されない

 

■参考資料
https://www.oracle.com/webfolder/technetwork/jp/ondemand/ddd2013/F-2.pdf
CREATE AUDIT POLICY (統合監査)
AUDIT (統合監査)
SYS_CONTEXT

■おわりに
ざっくり整理するとこんな感じでしょうか

従来型監査は監査によって監査ログの出力先が違っていましたが、統合監査は1つのビューで全監査ログを確認できるため、便利だと思います。
インスタンス起動/停止以外のSYSDBA権限の操作が監査されないのは焦りましたが…

Oracleの監査の検証(従来型監査編)

この記事は、JPOUG Advent Calendar 2022 16日目の記事です。
15日目はNAKASHOU06さんの記事「~クラウド1年生に贈るクラウド利用Tips~ - Qiita」でした。

今回はOracleの監査について検証しました。
監査はシステムのセキュリティ(機密性)を確保する手段の1つで、DBに対する操作を監視(記録)する機能です。
主に「誰が」、「いつ」、「何をしたか」を記録し、DBに対して不正なアクセスや操作がないか監視するのが目的です。

12c以降の場合、監査は2種類(従来型監査・統合監査)使用できますが、今回は従来型監査を検証しました。
今さら従来型監査の検証…という声も聞こえそうですが、統合監査との動きを比較するため、先ず従来型監査の動きを復習しました。
※個人の検証結果に基づく見解のため、正確性に欠ける可能性があります
※20c以降の場合、従来型監査は非推奨となっているため、設計する際はご注意ください (参考) AUDIT (従来型監査)

従来型監査は、必須監査、DBA監査、標準監査(標準データベース監査)、ファイングレイン監査(FGA監査)の監査証跡(監査ログ)をOSファイルやDBに出力します。

必須監査
特権ユーザ(SYSDBA/SYSOPER/SYSASM権限など)の操作を監査します
※必ず実施されるため、無効化できません

【監査対象】
インスタンス起動/停止
・特権ユーザによるインスタンス接続
【出力先】
UNIX
OSファイル(AUDIT_FILE_DEST初期化パラメータで設定したパス/<インスタンス名>_ora_<プロセスID>_<監査日時>.aud)
※DBインスタンス停止中は$ORACLE_HOME/rdbms/audit/<インスタンス名>_ora_<プロセスID>_<監査日時>.audに出力されます
Windows
イベントビューワ(未検証)

DBA監査
特権ユーザ(SYSDBA/SYSOPER権限など)の操作を監査します
必須監査はインスタンス起動/停止や接続を監査しますが、DBA監査は特権ユーザのDB内の操作を監査します
AUDIT_SYS_OPERATIONS初期化パラメータがTRUEの場合、有効になります

【監査対象】
・特権ユーザによるDB内の操作
【出力先】
UNIX
OSファイル(AUDIT_FILE_DEST初期化パラメータで設定したパス/<インスタンス名>_ora_<プロセスID>_<監査日時>.aud)
Windows
イベントビューワ(未検証)

標準監査(標準データベース監査)
一般ユーザのDBの操作を監査します
表に影響を与えるDDL文(CREATE TABLE/DROP TABLE/TRUNCATE TABLEなど)のSQL文や権限(システム権限、オブジェクト権限)の使用を監査します
SQL文の監査は成功(WHENEVER SUCCESSFUL)、失敗(WHENEVER NOT SUCCESSFUL)で監査対象の操作を絞れます

【監査対象】
・一般ユーザによるDBインスタンスへの接続
・一般ユーザによる特定のDDL文(DB構造の変更)の操作 (例)テーブル作成
・一般ユーザによるシステム権限を使用した操作 (例)CREATE ANY TABLE権限の使用
・一般ユーザによるオブジェクト権限を使用した操作 (例)SCOTT.EMP表へのSELECT
【出力先】
AUDIT_TRAIL初期化パラメータの設定値によって出力先が異なります
AUDIT_TRAIL=os、xmlxml,extendedのいずれか
OSファイル(AUDIT_FILE_DEST初期化パラメータで設定したパス/<インスタンス名>_ora_<プロセスID>_<監査日時>.aud)
※AUDIT_TRAIL=xml,extendedの場合、SQL文、バインド変数も出力します
WindowsでAUDIT_TRAIL=osの場合、イベントビューワ(未検証)

AUDIT_TRAIL=db、db,extendedのいずれか
DBA_AUDIT_TRAILビュー(SYS.AUD$表)
※AUDIT_TRAIL=db,extendedの場合、SQL文、バインド変数も出力します

値ベース監査
特権/一般ユーザのDB内の操作のうち、値の変化を監査します
例えばUPDATE文の場合、標準監査でSQL文は監査できますが、変更前後の値は監査できません
必須監査みたいに事前に組み込まれた監査機能ではないため、事前に変更前後の値を書き込むテーブルとUPDATE後に変更前後の値をテーブルに書き込むトリガーの作成が必要です
UPDATE文を実行するたびトリガーも実行される(DBへの負荷も増える)ため、注意が必要です

【監査対象】
UPDATE文などによる変更前後の値
【出力先】
ユーザが作成した値ベース監査用のテーブル

ファイングレイン監査(FGA監査)
特権/一般ユーザのデータベース内の操作(DML)を監査します
特定のテーブルのレコードの検索条件や監査対象の列、SQL文などの監査条件を指定して、標準監査よりさらに細かく監査します
※Enterprise Editionのみ

【監査対象】
監査ポリシーに合致したデータへの操作(DML)
【出力先】
DBA_FGA_AUDIT_TRAILビュー(SYS.FGA_LOG$表)
※AUDIT_TRAIL=db,extendedの場合、標準監査とファイングレイン監査の監査ログをDBA_COMMON_AUDIT_TRAILビューで参照可能

■検証環境
OS:Oracle Linux 6.5
GI:Oracle Grid Infrastructure 12c Release 1 (12.1.0.2.0) Enterprise Edition
DB:Oracle Database 12c Release 1 (12.1.0.2.0) Enterprise Edition
※2ノードRAC(管理者管理型DB)

■前提
・非CDB
・従来型監査のみ

■設定内容
事前に下記のとおり設定しています

統合監査設定
SQL> select inst_id, parameter, value from gv$option where parameter = 'Unified Auditing';

INST_ID PARAMETER            VALUE
------- -------------------- --------------------
      1 Unified Auditing     FALSE
      2 Unified Auditing     FALSE

【参考】統合監査(混合モード)無効化
1. 有効な監査ポリシー確認
SQL> select * from audit_unified_enabled_policies;

USER_NAME       POLICY_NAME          ENABLED_OPT              SUCCESS   FAILURE
--------------- -------------------- ------------------------ --------- ---------
ALL USERS       ORA_SECURECONFIG     BY                       YES       YES
ALL USERS       ORA_LOGON_FAILURES   BY                       YES       YES

2. 監査ポリシー取り消し
SQL> noaudit policy ORA_SECURECONFIG;

監査取消しが成功しました。

SQL> noaudit policy ORA_LOGON_FAILURES;

監査取消しが成功しました。

3. 有効な監査ポリシー確認
SQL> select * from audit_unified_enabled_policies;

レコードが選択されませんでした。

ASMの監査設定
SQL> --監査ログ(OSファイル)の出力先(デフォルト)
SQL> select inst_id, name, value from gv$parameter where name = 'audit_file_dest';

INST_ID NAME                           VALUE
------- ------------------------------ ----------------------------------------
      1 audit_file_dest                /u01/app/12.1.0/grid/rdbms/audit
      2 audit_file_dest                /u01/app/12.1.0/grid/rdbms/audit

DBの監査設定
SQL> --監査ログ(OSファイル)の出力先(デフォルト)
SQL> select inst_id, name, value from gv$parameter where name = 'audit_file_dest';

INST_ID NAME                           VALUE
------- ------------------------------ ----------------------------------------
      1 audit_file_dest                /u01/app/oracle/admin/orcl/adump
      2 audit_file_dest                /u01/app/oracle/admin/orcl/adump

SQL> --DBA監査設定(デフォルト)
SQL> select inst_id, name, value from gv$parameter where name = 'audit_sys_operations';

INST_ID NAME                           VALUE
------- ------------------------------ ----------------------------------------
      1 audit_sys_operations           TRUE
      2 audit_sys_operations           TRUE

 

■検証パターン
①必須監査
②DBA監査
③標準監査(標準データベース監査)
④値ベース監査
ファイングレイン監査(FGA監査)

■検証
①必須監査
特権ユーザの必須監査対象の操作が監査されるか検証します
検証環境はRACなのでASMとDBの起動/停止、接続を検証します
本検証では
・ASM操作はSYS(SYSASM権限)
・DB操作はSYS(SYSDBA権限)
を使用します

【検証手順】
1. ASMインスタンス停止
2. ASMインスタンス起動
3. ASMインスタンス接続
4. DBインスタンス停止
5. DBインスタンス起動
6. DBインスタンス接続

【想定】
必須監査対象の操作が監査されること

【検証結果】
必須監査対象の操作が監査された

【作業ログ】

1. ASMインスタンス停止
[root@node1 ~]# /u01/app/12.1.0/grid/bin/crsctl stop crs

#監査ログ確認
[root@node1 ~]# cat /u01/app/12.1.0/grid/rdbms/audit/+ASM1_ora_14288_20221124204627247146143795.aud
Thu Nov 24 20:46:30 2022 +09:00
LENGTH : '154'
ACTION :[18] 'SHUTDOWN IMMEDIATE'
DATABASE USER:[1] '/'
PRIVILEGE :[6] 'SYSASM'
CLIENT USER:[4] 'grid'
CLIENT TERMINAL:[0] ''
STATUS:[1] '0'
DBID:[0] ''
★SYSASM権限でASMインスタンス停止した監査ログが出力された

2. ASMインスタンス起動
[root@node1 ~]# /u01/app/12.1.0/grid/bin/crsctl start crs -wait

#監査ログ確認
[root@node1 ~]# cat /u01/app/12.1.0/grid/rdbms/audit/+ASM1_ora_19856_20221124204915718819143795.aud
Thu Nov 24 20:49:15 2022 +09:00
LENGTH : '142'
ACTION :[7] 'STARTUP'
DATABASE USER:[1] '/'
PRIVILEGE :[6] 'SYSASM'
CLIENT USER:[4] 'grid'
CLIENT TERMINAL:[0] ''
STATUS:[1] '0'
DBID:[0] ''
★SYSASM権限でASMインスタンス起動した監査ログが出力された

3. ASMインスタンス接続
[grid@node1 ~]$ sqlplus / as sysasm

#監査ログ確認
[root@node1 ~]# cat /u01/app/12.1.0/grid/rdbms/audit/+ASM1_ora_22562_20221124205356529751143795.aud
Thu Nov 24 20:53:56 2022 +09:00
LENGTH : '147'
ACTION :[7] 'CONNECT'
DATABASE USER:[1] '/'
PRIVILEGE :[6] 'SYSASM'
CLIENT USER:[4] 'grid'
CLIENT TERMINAL:[5] 'pts/0'
STATUS:[1] '0'
DBID:[0] ''
★SYSASM権限でASMインスタンス接続した監査ログが出力された

4. DBインスタンス停止
[oracle@node1 ~]$ srvctl stop database -db orcl

#監査ログ確認
[root@node1 ~]# cat /u01/app/oracle/admin/orcl/adump/orcl1_ora_22996_20221124205518173254143795.aud
Thu Nov 24 20:55:34 2022 +09:00
LENGTH : '156'
ACTION :[18] 'SHUTDOWN IMMEDIATE'
DATABASE USER:[1] '/'
PRIVILEGE :[6] 'SYSDBA'
CLIENT USER:[6] 'oracle'
CLIENT TERMINAL:[0] ''
STATUS:[1] '0'
DBID:[0] ''
★SYSDBA権限でDBインスタンス停止した監査ログが出力された

5. DBインスタンス起動
[oracle@node1 ~]$ srvctl start database -db orcl

#監査ログ確認
[root@node1 ~]# cat /u01/app/oracle/admin/orcl/adump/orcl1_ora_24752_20221124205952460023143795.aud
Thu Nov 24 20:59:52 2022 +09:00
LENGTH : '144'
ACTION :[7] 'STARTUP'
DATABASE USER:[1] '/'
PRIVILEGE :[6] 'SYSDBA'
CLIENT USER:[6] 'oracle'
CLIENT TERMINAL:[0] ''
STATUS:[1] '0'
DBID:[0] ''
★SYSDBA権限でDBインスタンス起動した監査ログが出力された

【補足】
DBインスタンス起動/停止はクラスタを跨ぐ操作かどうかで監査ログの出力先が変わります
クラスタを跨ぐ場合:全DBサーバに監査ログを出力(※1)
クラスタを跨がない(ノード単位)場合:操作対象のDBサーバに監査ログを出力(※2)
(※1) 例. srvctl start database -db orcl
(※2) 例. srvctl start instance -db orcl -node node1

6. DBインスタンス接続
[oracle@node1 ~]$ sqlplus / as sysdba

#監査ログ確認
[root@node1 ~]# cat /u01/app/oracle/admin/orcl/adump/orcl1_ora_31455_20221124211423662296143795.aud
Thu Nov 24 21:14:23 2022 +09:00
LENGTH : '160'
ACTION :[7] 'CONNECT'
DATABASE USER:[1] '/'
PRIVILEGE :[6] 'SYSDBA'
CLIENT USER:[6] 'oracle'
CLIENT TERMINAL:[5] 'pts/0'
STATUS:[1] '0'
DBID:[10] '1646447705'
★SYSDBA権限でDBインスタンス接続した監査ログが出力された

 

②DBA監査
特権ユーザのDBA監査対象の操作が監査されるか検証します
本検証ではSYS(SYSDBA権限)を使用します

【検証手順】
1. データディクショナリ問合せ
2. データ更新

【想定】
DBA監査対象の操作が監査されること

【検証結果】
DBA監査対象の操作が監査された

【作業ログ】

1. データディクショナリ問合せ
SQL> select count(*) from dba_users;

  COUNT(*)
----------
        42

#監査ログ確認
[root@node1 ~]# cat /u01/app/oracle/admin/orcl/adump/orcl1_ora_31987_20221124211615877436143795.aud
Thu Nov 24 21:16:17 2022 +09:00
LENGTH : '184'
ACTION :[30] 'select count(*) from dba_users'
DATABASE USER:[1] '/'
PRIVILEGE :[6] 'SYSDBA'
CLIENT USER:[6] 'oracle'
CLIENT TERMINAL:[5] 'pts/0'
STATUS:[1] '0'
DBID:[10] '1646447705'
★SYSDBA権限でデータディクショナリアクセスした監査ログが出力された

2. データ更新
SQL> update scott.emp set SAL = 1000 where EMPNO = 7369;

1行が更新されました。

#監査ログ確認
[root@node1 ~]# cat /u01/app/oracle/admin/orcl/adump/orcl1_ora_32177_20221124211651203243143795.aud
Thu Nov 24 21:18:06 2022 +09:00
LENGTH : '204'
ACTION :[50] 'update scott.emp set SAL = 1000 where EMPNO = 7369'
DATABASE USER:[1] '/'
PRIVILEGE :[6] 'SYSDBA'
CLIENT USER:[6] 'oracle'
CLIENT TERMINAL:[5] 'pts/0'
STATUS:[1] '0'
DBID:[10] '1646447705'
★SYSDBA権限でデータ更新した監査ログが出力された

 

③標準監査(標準データベース監査)
一般ユーザの標準監査対象の操作が監査されるか検証します
本検証ではSCOTT(サンプルユーザ)を使用します

【検証手順】
1. AUDIT_TRAIL初期化パラメータ設定
2. 監査オプション設定
3. ノード1からSCOTTでDBログイン(間違ったパスワード)
4. ノード2からSCOTTでDBログイン(間違ったパスワード)
5. SCOTTでテーブル作成
6. SCOTTでデータ挿入

【想定】
標準監査対象の操作が監査されること

【検証結果】
標準監査対象の操作が監査された

【作業ログ】

1. AUDIT_TRAIL初期化パラメータ設定
SQL> alter system set audit_trail = db, extended scope=spfile sid='*';

システムが変更されました。

SQL> select inst_id, name, value from gv$parameter where name = 'audit_trail';

INST_ID NAME                           VALUE
------- ------------------------------ ----------------------------------------
      1 audit_trail                    DB, EXTENDED
      2 audit_trail                    DB, EXTENDED
※反映させるため、DB再起動しています

2. 監査オプション設定
SQL> --ログイン失敗検証用
SQL> audit SESSION by SCOTT by access whenever not successful;

監査が成功しました。

SQL> --テーブル作成検証用
SQL> audit TABLE by SCOTT by access;

監査が成功しました。

SQL> --データ操作検証用
SQL> audit INSERT on scott.emp by access;

監査が成功しました。

SQL> select user_name, audit_option, success, failure from dba_stmt_audit_opts order by audit_option;

USER_NAME            AUDIT_OPTION         SUCCESS         FAILURE
-------------------- -------------------- --------------- ---------------
SCOTT                CREATE SESSION       NOT SET         BY ACCESS
SCOTT                TABLE                BY ACCESS       BY ACCESS
★オブジェクト権限の監査オプションは表示されなかった…

3. ノード1からSCOTTでDBログイン(間違ったパスワード)
[oracle@node1 ~]$ sqlplus scott/machigai

SQL*Plus: Release 12.1.0.2.0 Production on 木 11月 24 22:17:58 2022

Copyright (c) 1982, 2014, Oracle.  All rights reserved.

ERROR:
ORA-01017: ユーザー名/パスワードが無効です。ログオンは拒否されました。

#監査ログ確認
SQL> select TO_CHAR(TIMESTAMP, 'YYYY/MM/DD HH24:MI:SS') as TS, USERNAME, USERHOST, OWNER, OBJ_NAME, ACTION_NAME, SQL_TEXT from dba_audit_trail order by TIMESTAMP desc;

TS                   USERNAME        USERHOST             OWNER           OBJ_NAME        ACTION_NAME          SQL_TEXT
-------------------- --------------- -------------------- --------------- --------------- -------------------- --------------------------------------------------------------------------------
2022/11/24 22:17:59  SCOTT           node1.oracle12c.jp                                   LOGON
★ノード1でSCOTTがログイン失敗した監査ログが出力された

4. ノード2からSCOTTでDBログイン(間違ったパスワード)
[oracle@node2 ~]$ sqlplus scott/machigai

SQL*Plus: Release 12.1.0.2.0 Production on 木 11月 24 22:19:29 2022

Copyright (c) 1982, 2014, Oracle.  All rights reserved.

ERROR:
ORA-01017: ユーザー名/パスワードが無効です。ログオンは拒否されました。

#監査ログ確認
SQL> select TO_CHAR(TIMESTAMP, 'YYYY/MM/DD HH24:MI:SS') as TS, USERNAME, USERHOST, OWNER, OBJ_NAME, ACTION_NAME, SQL_TEXT from dba_audit_trail order by TIMESTAMP desc;

TS                   USERNAME        USERHOST             OWNER           OBJ_NAME        ACTION_NAME          SQL_TEXT
-------------------- --------------- -------------------- --------------- --------------- -------------------- --------------------------------------------------------------------------------
2022/11/24 22:19:30  SCOTT           node2.oracle12c.jp                                   LOGON
★ノード2でSCOTTがログイン失敗した監査ログが出力された

5. SCOTTでテーブル作成
SQL> create table test_table(id number, text varchar(10), create_time date);

表が作成されました。

#監査ログ確認
SQL> select TO_CHAR(TIMESTAMP, 'YYYY/MM/DD HH24:MI:SS') as TS, USERNAME, USERHOST, OWNER, OBJ_NAME, ACTION_NAME, SQL_TEXT from dba_audit_trail order by TIMESTAMP desc;

TS                   USERNAME        USERHOST             OWNER           OBJ_NAME        ACTION_NAME          SQL_TEXT
-------------------- --------------- -------------------- --------------- --------------- -------------------- --------------------------------------------------------------------------------
2022/11/24 22:20:10  SCOTT           node1.oracle12c.jp   SCOTT           TEST_TABLE      CREATE TABLE         create table test_table(id number, text varchar(10), create_time date)
★SCOTTがCREATE TABLE権限を使用した監査ログが出力された

6. SCOTTでデータ挿入
SQL> insert into emp(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) values(9999, 'TEST', 'DBA', 7369, sysdate, 2000, 500, 10);

1行が作成されました。

#監査ログ確認
SQL> select TO_CHAR(TIMESTAMP, 'YYYY/MM/DD HH24:MI:SS') as TS, USERNAME, USERHOST, OWNER, OBJ_NAME, ACTION_NAME, SQL_TEXT from dba_audit_trail order by TIMESTAMP desc;

TS                   USERNAME        USERHOST             OWNER           OBJ_NAME        ACTION_NAME          SQL_TEXT
-------------------- --------------- -------------------- --------------- --------------- -------------------- --------------------------------------------------------------------------------
2022/11/24 22:24:44  SCOTT           node1.oracle12c.jp   SCOTT           EMP             INSERT               insert into emp(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) values(9999
                                                                                                               , 'TEST', 'DBA', 7369, sysdate, 2000, 500, 10)
★SCOTTがINSERT権限を使用した監査ログが出力された

 

④値ベース監査
ユーザがデータ更新した際、変更前後の値が監査されるか検証します
本検証ではSCOTT(サンプルユーザ)を使用します

【検証手順】
1. 値ベース監査用テーブル作成
2. 値ベース監査用トリガー作成
3. SCOTTでデータ更新

【想定】
変更前後の値が監査されること

【検証結果】
変更前後の値が監査された

【作業ログ】

1. 値ベース監査用テーブル作成
SQL> create table audit_emp_sal(username varchar2(30), create_date date, empno number, new_sal number, old_sal number);

表が作成されました。

2. 値ベース監査用トリガー作成
SQL> create or replace trigger trg_audit_emp_sal after update of sal on scott.emp referencing new as new old as old for each row
  2  begin
  3    if :old.sal != :new.sal then
  4      insert into audit_emp_sal values(sys_context('userenv', 'session_user'), sysdate, :new.empno, :new.sal, :old.sal);
  5    end if;
  6  end;
  7  /

トリガーが作成されました。
★SCOTTのEMP表のSAL列を更新(UPDATE)後、変更前後の値をAUDIT_EMP_SAL表に書き込むように設定

3. SCOTTでデータ更新
SQL> select * from emp where EMPNO = 7369;

     EMPNO ENAME      JOB               MGR HIREDATE        SAL       COMM     DEPTNO
---------- ---------- ---------- ---------- -------- ---------- ---------- ----------
      7369 SMITH      CLERK            7902 80-12-17       1000                    10

SQL> update emp set SAL = 9999 where EMPNO = 7369;

1行が更新されました。

SQL> commit;

コミットが完了しました。

SQL> select * from emp where EMPNO = 7369;

     EMPNO ENAME      JOB               MGR HIREDATE        SAL       COMM     DEPTNO
---------- ---------- ---------- ---------- -------- ---------- ---------- ----------
      7369 SMITH      CLERK            7902 80-12-17       9999                    10

#監査ログ確認
SQL> select USERNAME, TO_CHAR(CREATE_DATE, 'YYYY/MM/DD HH24:MI:SS') as CREATE_DATE, EMPNO, NEW_SAL, OLD_SAL from audit_emp_sal;

USERNAME        CREATE_DATE               EMPNO    NEW_SAL    OLD_SAL
--------------- -------------------- ---------- ---------- ----------
SCOTT           2022/11/24 22:30:38        7369       9999       1000
★SCOTTのUPDATE文による変更前(OLD_SAL)、変更後(NEW_SAL)の値が監査ログに出力された

 

ファイングレイン監査(FGA監査)
ユーザのファイングレイン監査対象の操作が監査されるか検証します
本検証ではSCOTT(サンプルユーザ)を使用します

【検証手順】
1. ファイングレイン監査ポリシー作成
2. 監査ポリシーに該当するパターン1
3. 監査ポリシーに該当するパターン2
4. 監査ポリシーに該当しないパターン1
5. 監査ポリシーに該当しないパターン2

【想定】
ファイングレイン監査対象の操作が監査されること

【検証結果】
ファイングレイン監査対象の操作が監査された

【作業ログ】

1. ファイングレイン監査ポリシー作成
SQL> exec dbms_fga.add_policy(object_schema => 'SCOTT', object_name => 'EMP', policy_name => 'fga_audit_emp_sal', audit_condition => 'DEPTNO=10', audit_column => 'SAL', enable => TRUE, statement_types => 'SELECT');

PL/SQLプロシージャが正常に完了しました。
★DEPTNOが10のレコードのSAL列をSELECTしたら監査するように設定

2. 監査ポリシーに該当するパターン1
SQL> select empno, ename, sal, deptno from emp;

     EMPNO ENAME             SAL     DEPTNO
---------- ---------- ---------- ----------
      7369 SMITH            9999         10
      7499 ALLEN            1600         30
      7521 WARD             1250         30
      7566 JONES            2975         20
      7654 MARTIN           1250         30
      7698 BLAKE            2850         30
      7782 CLARK            2450         10
      7839 KING             5000         10
      7844 TURNER           1500         30
      7900 JAMES             950         30
      7902 FORD             3000         20
      7934 MILLER           1300         10

#監査ログ確認
SQL> select TO_CHAR(TIMESTAMP, 'YYYY/MM/DD HH24:MI:SS') as TS, DB_USER, USERHOST, OBJECT_NAME, SQL_TEXT, STATEMENT_TYPE from dba_fga_audit_trail order by TS desc;

TS                   DB_USER         USERHOST             OBJECT_NAME     SQL_TEXT                                                                         STATEMENT_TYPE
-------------------- --------------- -------------------- --------------- -------------------------------------------------------------------------------- ---------------
2022/11/24 22:32:35  SCOTT           node1.oracle12c.jp   EMP             select empno, ename, sal, deptno from emp                                        SELECT
★EMPNO7369・7782・7839・7934のレコードのDEPTNOが10で、SAL列をSELECTしているため、監査ログが出力された

3. 監査ポリシーに該当するパターン2
SQL> select empno, ename, sal, deptno from emp where deptno = 10;

     EMPNO ENAME             SAL     DEPTNO
---------- ---------- ---------- ----------
      7369 SMITH            9999         10
      7782 CLARK            2450         10
      7839 KING             5000         10
      7934 MILLER           1300         10

#監査ログ確認
SQL> select TO_CHAR(TIMESTAMP, 'YYYY/MM/DD HH24:MI:SS') as TS, DB_USER, USERHOST, OBJECT_NAME, SQL_TEXT, STATEMENT_TYPE from dba_fga_audit_trail order by TS desc;

TS                   DB_USER         USERHOST             OBJECT_NAME     SQL_TEXT                                                                         STATEMENT_TYPE
-------------------- --------------- -------------------- --------------- -------------------------------------------------------------------------------- ---------------
2022/11/24 22:32:42  SCOTT           node1.oracle12c.jp   EMP             select empno, ename, sal, deptno from emp where deptno = 10                      SELECT
★DEPTNOを10で絞って、SAL列をSELECTしているため、監査ログが出力された

4. 監査ポリシーに該当しないパターン1
SQL> select empno, ename, sal, deptno from emp where deptno = 20;

     EMPNO ENAME             SAL     DEPTNO
---------- ---------- ---------- ----------
      7566 JONES            2975         20
      7902 FORD             3000         20

#監査ログ確認
★SAL列をSELECTしているがDEPTNOを20で絞っているため、監査ログが出力されない

5. 監査ポリシーに該当しないパターン2
SQL> select empno, ename, job, deptno from emp where deptno = 10;

     EMPNO ENAME      JOB            DEPTNO
---------- ---------- ---------- ----------
      7369 SMITH      CLERK              10
      7782 CLARK      MANAGER            10
      7839 KING       PRESIDENT          10
      7934 MILLER     CLERK              10

#監査ログ確認
★SAL列をSELECTしていないため、監査ログが出力されない

 

■参考資料
AUDIT (従来型監査)
https://www.oracle.com/jp/a/tech/docs/technical-resources/db-audit-100831.pdf

■おわりに
監査ログを自動的に削除する機能はないため、削除運用も設計が必要です。
監査は機密性を確保する手段でしたが、完全性や可用性は同時実行性やバックアップ・リカバリなどで確保します。

次回は従来型監査で監査した項目を統合監査で監査した場合、どのように監査されるのか検証しようと思います。

最後までお読みいただきありがとうございました。
17日目の記事もお楽しみに!

LogMiner検証

OracleユーティリティのLogMinerについて、机上の知識はあるものの、実務経験が無いため、実際の動きを検証しました。

LogMinerは、REDOログファイルやアーカイブログファイルに記録された更新情報(SQLなど)を分析できるツールです。
ただし、LogMinerを使えばすぐ分析できるわけではなく、事前にサプリメンタル・ロギング(REDOログファイルに分析用の追加情報を出力する機能)を有効にする必要があります。

なお、サプリメンタル・ロギングを有効にする場合、有効範囲とロギングレベルを指定する必要があります。

有効範囲
データベースレベルか表レベルで指定できます。
データベースレベルの場合、DB全体に対して有効になります。
表レベルの場合、特定の表に対して有効になります。

データベースレベルで指定する場合
alter database [add | drop] supplemental log data [(ALL | PRIMARY KEY | UNIQUE | FOREIGN KEY) columns];

表レベルで指定する場合
alter <スキーマ名>.<表名> [add | drop] supplemental log data (ALL | PRIMARY KEY | UNIQUE | FOREIGN KEY) columns;

※本検証はデータベースレベルで検証します

ロギングレベル
最小レベルか識別キーレベルで指定できます。
ロギングレベルに応じてREDOログファイルに追加される情報も増えます。

最小サプリメンタル・ロギング
LogMiner(LogMinerの仕組みを使用している製品)を使用するにあたり、必要最低限の追加情報(ログ情報)をREDOログファイルに出力します。
表レベルでは設定できません。

識別キー・ロギング
PRIMARY KEY
主キーを持つ表に対して、更新されるたびにすべての主キー列をREDOログファイルに出力します。
データベースレベル、表レベルともに設定できます。(データベースレベルで有効した場合、同時に最小レベルも有効になります)

UNIQUE
一意キーを持つ表に対して、一意キー列のいずれかが更新されるたびにその一意キーに属するすべての列をREDOログファイルに出力します。
データベースレベル、表レベルともに設定できます。(データベースレベルで有効した場合、同時に最小レベルも有効になります)

FOREIGN KEY
外部キーを持つ表に対して、外部キー列のいずれかが更新されるたびにその外部キーに属するすべての列をREDOログファイルに出力します。
データベースレベル、表レベルともに設定できます。(データベースレベルで有効した場合、同時に最小レベルも有効になります)

ALL
行が更新されるたびにすべての列をREDOログファイルに出力します。
データベースレベル、表レベルともに設定できます。(データベースレベルで有効した場合、同時に最小レベルも有効になります)

余談ですが、サプリメンタル・ロギングはOracle GoldenGate(LogMinerの仕組みを使用している製品)などDB間でデータ連携するときにも使用しています。

LogMinerを使用する目的は公式マニュアルではざっくり下記のように記載されています。
・アプリケーションレベルのエラー(不適切な行削除や行更新、オブジェクトの削除など)など、論理破損が発生した時期やリカバリSQLの特定
・依存関係のあるトランザクションリカバリSQLの特定
SQLの実行履歴から実行したSQLの傾向分析(どの表で更新や挿入が頻発しているかなど)
・事後監査(DBで実行されたDMLDDL、実行順序、実行者などの追跡など)

特にアプリケーションレベルのエラーはフラッシュバック操作すればいいんじゃない?という疑問もありますが、高速リカバリ領域を構成していない(構成できない)場合などはLogMinerが使えそうですね。

■検証環境
OS:Oracle Linux 6.5
GI:Oracle Grid Infrastructure 12c Release 1 (12.1.0.2.0) Enterprise Edition
DB:Oracle Database 12c Release 1 (12.1.0.2.0) Enterprise Edition
※2ノードRAC(管理者管理型DB)

■前提
なし

■検証パターン
①最小サプリメンタル・ロギング
②識別キー(PRIMARY KEY)サプリメンタル・ロギング
③識別キー(FOREIGN KEY)サプリメンタル・ロギング
④識別キー(ALL)サプリメンタル・ロギング
※UNIQUEは手元に手ごろな表がなかったので未検証

■検証
①最小サプリメンタル・ロギング
ロギングレベルを最小サプリメンタル・ロギングにした場合、REDOログファイルに出力される内容を検証します

【検証手順】
1. 最小サプリメンタルロギング設定
2. 分析対象のREDOログファイル特定
3. LogMinerディクショナリ・ファイル作成
4. 分析対象のREDOログファイル追加
5. LogMiner起動
6. テストデータ更新
7. 分析
8. LogMiner終了
9. 最小サプリメンタルロギング削除

【作業ログ】

1. 最小サプリメンタルロギング設定
SQL> select supplemental_log_data_min, supplemental_log_data_pk, supplemental_log_data_ui, supplemental_log_data_fk, supplemental_log_data_all from v$database;

SUPPLEMENTAL_LOG_DATA_MIN SUPPLEMENTAL_LOG_DATA_PK  SUPPLEMENTAL_LOG_DATA_UI  SUPPLEMENTAL_LOG_DATA_FK  SUPPLEMENTAL_LOG_DATA_ALL
------------------------- ------------------------- ------------------------- ------------------------- -------------------------
NO                        NO                        NO                        NO                        NO

SQL> alter database add supplemental log data;

データベースが変更されました。

SQL> select supplemental_log_data_min, supplemental_log_data_pk, supplemental_log_data_ui, supplemental_log_data_fk, supplemental_log_data_all from v$database;

SUPPLEMENTAL_LOG_DATA_MIN SUPPLEMENTAL_LOG_DATA_PK  SUPPLEMENTAL_LOG_DATA_UI  SUPPLEMENTAL_LOG_DATA_FK  SUPPLEMENTAL_LOG_DATA_ALL
------------------------- ------------------------- ------------------------- ------------------------- -------------------------
YES                       NO                        NO                        NO                        NO

2. 分析対象のREDOログファイル特定
SQL> select
  2    l.GROUP#, l.THREAD#, l.SEQUENCE#, l.STATUS, lf.MEMBER
  3  from
  4    v$log l, v$logfile lf
  5  where
  6    l.GROUP#  = lf.GROUP#
  7  order by
  8    l.GROUP#, l.THREAD#;

    GROUP#    THREAD#  SEQUENCE# STATUS          MEMBER
---------- ---------- ---------- --------------- ----------------------------------------
         1          1         43 INACTIVE        +DATA/ORCL/ONLINELOG/redo01.log
         2          1         44 CURRENT         +DATA/ORCL/ONLINELOG/redo02.log
         3          2         39 CURRENT         +DATA/ORCL/ONLINELOG/redo03.log
         4          2         38 INACTIVE        +DATA/ORCL/ONLINELOG/redo04.log

3. LogMinerディクショナリ・ファイル作成
LogMinerディクショナリは、REDOログファイルのREDOデータをユーザに表示する際、オブジェクトIDをオブジェクト名に変換するためのファイルです(マップみたいなイメージ)
LogMinerディクショナリには3種類ありますが、本検証ではオンライン・カタログを使用するため、LogMinerディクショナリ・ファイル作成は割愛します

-オンライン・カタログ
-REDOログファイル埋め込み
-フラットファイル(非推奨)

4. 分析対象のREDOログファイル追加
SQL> EXECUTE DBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME => '+DATA/ORCL/ONLINELOG/redo02.log', OPTIONS => DBMS_LOGMNR.NEW);

PL/SQLプロシージャが正常に完了しました。

RACなのでスレッド2の方も追加
SQL> EXECUTE DBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME => '+DATA/ORCL/ONLINELOG/redo03.log', OPTIONS => DBMS_LOGMNR.ADDFILE);

PL/SQLプロシージャが正常に完了しました。

★ログスイッチ等でREDOログファイルが切り替わっても対応できるよう、CURRENT以外のREDOログ・ファイルも追加
SQL> EXECUTE DBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME => '+DATA/ORCL/ONLINELOG/redo01.log', OPTIONS => DBMS_LOGMNR.ADDFILE);

PL/SQLプロシージャが正常に完了しました。

SQL> EXECUTE DBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME => '+DATA/ORCL/ONLINELOG/redo04.log', OPTIONS => DBMS_LOGMNR.ADDFILE);

PL/SQLプロシージャが正常に完了しました。

5. LogMiner起動
SQL> EXECUTE DBMS_LOGMNR.START_LOGMNR(Options => DBMS_LOGMNR.DICT_FROM_ONLINE_CATALOG + DBMS_LOGMNR.COMMITTED_DATA_ONLY);

PL/SQLプロシージャが正常に完了しました。

6. テストデータ更新(別セッション)
TeraTermを立ち上げ、別セッションでテストデータを更新します

SQL> select * from emp where EMPNO = 7369;

     EMPNO ENAME      JOB               MGR HIREDATE        SAL       COMM     DEPTNO
---------- ---------- ---------- ---------- -------- ---------- ---------- ----------
      7369 SMITH      CLERK            7902 80-12-17        800                    20

SQL> update emp set SAL = 9999 where EMPNO = 7369;

1行が更新されました。

SQL> commit;

コミットが完了しました。

SQL> select * from emp where EMPNO = 7369;

     EMPNO ENAME      JOB               MGR HIREDATE        SAL       COMM     DEPTNO
---------- ---------- ---------- ---------- -------- ---------- ---------- ----------
      7369 SMITH      CLERK            7902 80-12-17       9999                    20

【参考】
一回ハマったのですが、DBMS_LOGMNR.START_LOGMNR()したセッションを終了してしまうと後続のv$logmnr_contentsへ問い合わせた際、下記エラーが発生します
------------------------------------------------------------
行8でエラーが発生しました。:
ORA-01306: dbms_logmnr.start_logmnr()は、v$logmnr_contentsから選択する前に起動する必要があります。
------------------------------------------------------------
※上記が発生した場合、ADD_LOGFILE()からやり直す必要があります

7. 分析
SQL> SELECT
  2    SCN, TO_CHAR(TIMESTAMP, 'YYYY/MM/DD HH24:MI:SS') AS TM_STAMP, OPERATION, SQL_REDO, SQL_UNDO
  3  FROM
  4    V$LOGMNR_CONTENTS
  5  WHERE
  6        USERNAME = 'SCOTT'
  7  ORDER BY
  8    SCN DESC;

       SCN TM_STAMP             OPERATION  SQL_REDO                                                     SQL_UNDO
---------- -------------------- ---------- ------------------------------------------------------------ ------------------------------------------------------------
   6727554 2022/11/11 10:28:46  UPDATE     update "SCOTT"."EMP" set "SAL" = '9999' where "SAL" = '800'  update "SCOTT"."EMP" set "SAL" = '800' where "SAL" = '9999'
                                           and ROWID = 'AAAWwdAAGAAAADFAAA';                            and ROWID = 'AAAWwdAAGAAAADFAAA';
SQL_REDO列のWHERE句がEMPNOではなく、SALとROWIDに変わっていました

8. LogMiner終了
SQL> EXECUTE DBMS_LOGMNR.END_LOGMNR();

PL/SQLプロシージャが正常に完了しました。

9. 最小サプリメンタルロギング削除
SQL> select supplemental_log_data_min, supplemental_log_data_pk, supplemental_log_data_ui, supplemental_log_data_fk, supplemental_log_data_all from v$database;

SUPPLEMENTAL_LOG_DATA_MIN SUPPLEMENTAL_LOG_DATA_PK  SUPPLEMENTAL_LOG_DATA_UI  SUPPLEMENTAL_LOG_DATA_FK  SUPPLEMENTAL_LOG_DATA_ALL
------------------------- ------------------------- ------------------------- ------------------------- -------------------------
YES                       NO                        NO                        NO                        NO

SQL> alter database drop supplemental log data;

データベースが変更されました。

SQL> select supplemental_log_data_min, supplemental_log_data_pk, supplemental_log_data_ui, supplemental_log_data_fk, supplemental_log_data_all from v$database;

SUPPLEMENTAL_LOG_DATA_MIN SUPPLEMENTAL_LOG_DATA_PK  SUPPLEMENTAL_LOG_DATA_UI  SUPPLEMENTAL_LOG_DATA_FK  SUPPLEMENTAL_LOG_DATA_ALL
------------------------- ------------------------- ------------------------- ------------------------- -------------------------
NO                        NO                        NO                        NO                        NO

 

②識別キー(PRIMARY KEY)サプリメンタル・ロギング
ロギングレベルを識別キー(PRIMARY KEY)サプリメンタル・ロギングにした場合、REDOログファイルに出力される内容を検証します

【検証手順】
1. 識別キー(PRIMARY KEY)サプリメンタル・ロギング設定
2. 分析対象のREDOログファイル特定
3. LogMinerディクショナリ・ファイル作成
4. 分析対象のREDOログファイル追加
5. LogMiner起動
6. テストデータ更新
7. 分析
8. LogMiner終了
9. 識別キー(PRIMARY KEY)サプリメンタル・ロギング削除

【作業ログ】

1. 識別キー(PRIMARY KEY)サプリメンタル・ロギング設定
SQL> select supplemental_log_data_min, supplemental_log_data_pk, supplemental_log_data_ui, supplemental_log_data_fk, supplemental_log_data_all from v$database;

SUPPLEMENTAL_LOG_DATA_MIN SUPPLEMENTAL_LOG_DATA_PK  SUPPLEMENTAL_LOG_DATA_UI  SUPPLEMENTAL_LOG_DATA_FK  SUPPLEMENTAL_LOG_DATA_ALL
------------------------- ------------------------- ------------------------- ------------------------- -------------------------
NO                        NO                        NO                        NO                        NO

SQL> alter database add supplemental log data (primary key) columns;

データベースが変更されました。

SQL> select supplemental_log_data_min, supplemental_log_data_pk, supplemental_log_data_ui, supplemental_log_data_fk, supplemental_log_data_all from v$database;

SUPPLEMENTAL_LOG_DATA_MIN SUPPLEMENTAL_LOG_DATA_PK  SUPPLEMENTAL_LOG_DATA_UI  SUPPLEMENTAL_LOG_DATA_FK  SUPPLEMENTAL_LOG_DATA_ALL
------------------------- ------------------------- ------------------------- ------------------------- -------------------------
IMPLICIT                  YES                       NO                        NO                        NO
★識別キーサプリメンタル・ロギングを有効にした際、同時に最小サプリメンタル・ロギングも有効になっているため、SUPPLEMENTAL_LOG_DATA_MIN列もIMPLICITになっています

2. 分析対象のREDOログファイル特定
SQL> select
  2    l.GROUP#, l.THREAD#, l.SEQUENCE#, l.STATUS, lf.MEMBER
  3  from
  4    v$log l, v$logfile lf
  5  where
  6    l.GROUP#  = lf.GROUP#
  7  order by
  8    l.GROUP#, l.THREAD#;

    GROUP#    THREAD#  SEQUENCE# STATUS          MEMBER
---------- ---------- ---------- --------------- ----------------------------------------
         1          1         43 INACTIVE        +DATA/ORCL/ONLINELOG/redo01.log
         2          1         44 CURRENT         +DATA/ORCL/ONLINELOG/redo02.log
         3          2         39 CURRENT         +DATA/ORCL/ONLINELOG/redo03.log
         4          2         38 INACTIVE        +DATA/ORCL/ONLINELOG/redo04.log

3. LogMinerディクショナリ・ファイル作成
本検証ではオンライン・カタログを使用するため、LogMinerディクショナリ・ファイル作成は割愛します

4. 分析対象のREDOログファイル追加
SQL> EXECUTE DBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME => '+DATA/ORCL/ONLINELOG/redo02.log', OPTIONS => DBMS_LOGMNR.NEW);

PL/SQLプロシージャが正常に完了しました。

RACなのでスレッド2の方も追加
SQL> EXECUTE DBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME => '+DATA/ORCL/ONLINELOG/redo03.log', OPTIONS => DBMS_LOGMNR.ADDFILE);

PL/SQLプロシージャが正常に完了しました。

★ログスイッチ等でREDOログファイルが切り替わっても対応できるよう、CURRENT以外のREDOログ・ファイルも追加
SQL> EXECUTE DBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME => '+DATA/ORCL/ONLINELOG/redo01.log', OPTIONS => DBMS_LOGMNR.ADDFILE);

PL/SQLプロシージャが正常に完了しました。

SQL> EXECUTE DBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME => '+DATA/ORCL/ONLINELOG/redo04.log', OPTIONS => DBMS_LOGMNR.ADDFILE);

PL/SQLプロシージャが正常に完了しました。

5. LogMiner起動
SQL> EXECUTE DBMS_LOGMNR.START_LOGMNR(Options => DBMS_LOGMNR.DICT_FROM_ONLINE_CATALOG + DBMS_LOGMNR.COMMITTED_DATA_ONLY);

PL/SQLプロシージャが正常に完了しました。

6. テストデータ更新(別セッション)
TeraTermを立ち上げ、別セッションでテストデータを更新します
PRIMARY KEY制約(EMPNO列)があるレコードを更新してみます

SQL> select * from emp where EMPNO = 7369;

     EMPNO ENAME      JOB               MGR HIREDATE        SAL       COMM     DEPTNO
---------- ---------- ---------- ---------- -------- ---------- ---------- ----------
      7369 SMITH      CLERK            7902 80-12-17       9999                    20

SQL> update emp set SAL = 10000 where EMPNO = 7369;

1行が更新されました。

SQL> commit;

コミットが完了しました。

SQL> select * from emp where EMPNO = 7369;

     EMPNO ENAME      JOB               MGR HIREDATE        SAL       COMM     DEPTNO
---------- ---------- ---------- ---------- -------- ---------- ---------- ----------
      7369 SMITH      CLERK            7902 80-12-17      10000                    20

7. 分析
SQL> SELECT
  2    SCN, TO_CHAR(TIMESTAMP, 'YYYY/MM/DD HH24:MI:SS') AS TM_STAMP, OPERATION, SQL_REDO, SQL_UNDO
  3  FROM
  4    V$LOGMNR_CONTENTS
  5  WHERE
  6        USERNAME = 'SCOTT'
  7  ORDER BY
  8    SCN DESC;

       SCN TM_STAMP             OPERATION  SQL_REDO                                                     SQL_UNDO
---------- -------------------- ---------- ------------------------------------------------------------ ------------------------------------------------------------
   6729463 2022/11/11 10:39:21  UPDATE     update "SCOTT"."EMP" set "SAL" = '10000' where "EMPNO" = '73 update "SCOTT"."EMP" set "SAL" = '9999' where "EMPNO" = '736
                                           69' and "SAL" = '9999' and ROWID = 'AAAWwdAAGAAAADFAAA';     9' and "SAL" = '10000' and ROWID = 'AAAWwdAAGAAAADFAAA';
★最小サプリメンタル・ロギングではEMPNOはWHERE句に含まれていませんでしたが、SQL_REDO列のWHERE句がEMPNOとSALとROWIDに変わっていました

8. LogMiner終了
SQL> EXECUTE DBMS_LOGMNR.END_LOGMNR();

PL/SQLプロシージャが正常に完了しました。

9. 識別キー(PRIMARY KEY)サプリメンタル・ロギング削除
SQL> select supplemental_log_data_min, supplemental_log_data_pk, supplemental_log_data_ui, supplemental_log_data_fk, supplemental_log_data_all from v$database;

SUPPLEMENTAL_LOG_DATA_MIN SUPPLEMENTAL_LOG_DATA_PK  SUPPLEMENTAL_LOG_DATA_UI  SUPPLEMENTAL_LOG_DATA_FK  SUPPLEMENTAL_LOG_DATA_ALL
------------------------- ------------------------- ------------------------- ------------------------- -------------------------
IMPLICIT                  YES                       NO                        NO                        NO

SQL> alter database drop supplemental log data (primary key) columns;

データベースが変更されました。

SQL> select supplemental_log_data_min, supplemental_log_data_pk, supplemental_log_data_ui, supplemental_log_data_fk, supplemental_log_data_all from v$database;

SUPPLEMENTAL_LOG_DATA_MIN SUPPLEMENTAL_LOG_DATA_PK  SUPPLEMENTAL_LOG_DATA_UI  SUPPLEMENTAL_LOG_DATA_FK  SUPPLEMENTAL_LOG_DATA_ALL
------------------------- ------------------------- ------------------------- ------------------------- -------------------------
NO                        NO                        NO                        NO                        NO

 

③識別キー(FOREIGN KEY)サプリメンタル・ロギング
ロギングレベルを識別キー(FOREIGN KEY)サプリメンタル・ロギングにした場合、REDOログファイルに出力される内容を検証します

【検証手順】
1. 識別キー(FOREIGN KEY)サプリメンタル・ロギング設定
2. 分析対象のREDOログファイル特定
3. LogMinerディクショナリ・ファイル作成
4. 分析対象のREDOログファイル追加
5. LogMiner起動
6. テストデータ更新
7. 分析
8. LogMiner終了
9. 識別キー(FOREIGN KEY)サプリメンタル・ロギング削除

【作業ログ】

1. 識別キー(FOREIGN KEY)サプリメンタル・ロギング設定
SQL> select supplemental_log_data_min, supplemental_log_data_pk, supplemental_log_data_ui, supplemental_log_data_fk, supplemental_log_data_all from v$database;

SUPPLEMENTAL_LOG_DATA_MIN SUPPLEMENTAL_LOG_DATA_PK  SUPPLEMENTAL_LOG_DATA_UI  SUPPLEMENTAL_LOG_DATA_FK  SUPPLEMENTAL_LOG_DATA_ALL
------------------------- ------------------------- ------------------------- ------------------------- -------------------------
NO                        NO                        NO                        NO                        NO

SQL> alter database add supplemental log data (foreign key) columns;

データベースが変更されました。

SQL> select supplemental_log_data_min, supplemental_log_data_pk, supplemental_log_data_ui, supplemental_log_data_fk, supplemental_log_data_all from v$database;

SUPPLEMENTAL_LOG_DATA_MIN SUPPLEMENTAL_LOG_DATA_PK  SUPPLEMENTAL_LOG_DATA_UI  SUPPLEMENTAL_LOG_DATA_FK  SUPPLEMENTAL_LOG_DATA_ALL
------------------------- ------------------------- ------------------------- ------------------------- -------------------------
IMPLICIT                  NO                        NO                        YES                       NO

2. 分析対象のREDOログファイル特定
SQL> select
  2    l.GROUP#, l.THREAD#, l.SEQUENCE#, l.STATUS, lf.MEMBER
  3  from
  4    v$log l, v$logfile lf
  5  where
  6    l.GROUP#  = lf.GROUP#
  7  order by
  8    l.GROUP#, l.THREAD#;

    GROUP#    THREAD#  SEQUENCE# STATUS          MEMBER
---------- ---------- ---------- --------------- ----------------------------------------
         1          1         43 INACTIVE        +DATA/ORCL/ONLINELOG/redo01.log
         2          1         44 CURRENT         +DATA/ORCL/ONLINELOG/redo02.log
         3          2         39 CURRENT         +DATA/ORCL/ONLINELOG/redo03.log
         4          2         38 INACTIVE        +DATA/ORCL/ONLINELOG/redo04.log

3. LogMinerディクショナリ・ファイル作成
本検証ではオンライン・カタログを使用するため、LogMinerディクショナリ・ファイル作成は割愛します

4. 分析対象のREDOログファイル追加
SQL> EXECUTE DBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME => '+DATA/ORCL/ONLINELOG/redo02.log', OPTIONS => DBMS_LOGMNR.NEW);

PL/SQLプロシージャが正常に完了しました。

RACなのでスレッド2の方も追加
SQL> EXECUTE DBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME => '+DATA/ORCL/ONLINELOG/redo03.log', OPTIONS => DBMS_LOGMNR.ADDFILE);

PL/SQLプロシージャが正常に完了しました。

★ログスイッチ等でREDOログファイルが切り替わっても対応できるよう、CURRENT以外のREDOログ・ファイルも追加
SQL> EXECUTE DBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME => '+DATA/ORCL/ONLINELOG/redo01.log', OPTIONS => DBMS_LOGMNR.ADDFILE);

PL/SQLプロシージャが正常に完了しました。

SQL> EXECUTE DBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME => '+DATA/ORCL/ONLINELOG/redo04.log', OPTIONS => DBMS_LOGMNR.ADDFILE);

PL/SQLプロシージャが正常に完了しました。

5. LogMiner起動
SQL> EXECUTE DBMS_LOGMNR.START_LOGMNR(Options => DBMS_LOGMNR.DICT_FROM_ONLINE_CATALOG + DBMS_LOGMNR.COMMITTED_DATA_ONLY);

PL/SQLプロシージャが正常に完了しました。

6. テストデータ更新(別セッション)
TeraTermを立ち上げ、別セッションでテストデータを更新します
外部キー制約(DEPTNO列)がある列の値を更新してみます

SQL> select * from emp where EMPNO = 7369;

     EMPNO ENAME      JOB               MGR HIREDATE        SAL       COMM     DEPTNO
---------- ---------- ---------- ---------- -------- ---------- ---------- ----------
      7369 SMITH      CLERK            7902 80-12-17      10000                    20

SQL> update emp set DEPTNO = 10 where EMPNO = 7369;

1行が更新されました。

SQL> commit;

コミットが完了しました。

SQL> select * from emp where EMPNO = 7369;

     EMPNO ENAME      JOB               MGR HIREDATE        SAL       COMM     DEPTNO
---------- ---------- ---------- ---------- -------- ---------- ---------- ----------
      7369 SMITH      CLERK            7902 80-12-17      10000                    10

7. 分析
SQL> SELECT
  2    SCN, TO_CHAR(TIMESTAMP, 'YYYY/MM/DD HH24:MI:SS') AS TM_STAMP, OPERATION, SQL_REDO, SQL_UNDO
  3  FROM
  4    V$LOGMNR_CONTENTS
  5  WHERE
  6        USERNAME = 'SCOTT'
  7  ORDER BY
  8    SCN DESC;

       SCN TM_STAMP             OPERATION  SQL_REDO                                                     SQL_UNDO
---------- -------------------- ---------- ------------------------------------------------------------ ------------------------------------------------------------
   6733768 2022/11/11 11:00:45  UPDATE     update "SCOTT"."EMP" set "DEPTNO" = '10' where "DEPTNO" = '2 update "SCOTT"."EMP" set "DEPTNO" = '20' where "DEPTNO" = '1
                                           0' and ROWID = 'AAAWwdAAGAAAADFAAA';                         0' and ROWID = 'AAAWwdAAGAAAADFAAA';
SQL_REDO列のWHERE句がEMPNOではなく、DEPTNOとROWIDに変わっていました

8. LogMiner終了
SQL> EXECUTE DBMS_LOGMNR.END_LOGMNR();

PL/SQLプロシージャが正常に完了しました。

9. 識別キー(FOREIGN KEY)サプリメンタル・ロギング削除
SUPPLEMENTAL_LOG_DATA_MIN SUPPLEMENTAL_LOG_DATA_PK  SUPPLEMENTAL_LOG_DATA_UI  SUPPLEMENTAL_LOG_DATA_FK  SUPPLEMENTAL_LOG_DATA_ALL
------------------------- ------------------------- ------------------------- ------------------------- -------------------------
IMPLICIT                  NO                        NO                        YES                       NO

SQL> alter database drop supplemental log data (foreign key) columns;

データベースが変更されました。

SQL> select supplemental_log_data_min, supplemental_log_data_pk, supplemental_log_data_ui, supplemental_log_data_fk, supplemental_log_data_all from v$database;

SUPPLEMENTAL_LOG_DATA_MIN SUPPLEMENTAL_LOG_DATA_PK  SUPPLEMENTAL_LOG_DATA_UI  SUPPLEMENTAL_LOG_DATA_FK  SUPPLEMENTAL_LOG_DATA_ALL
------------------------- ------------------------- ------------------------- ------------------------- -------------------------
NO                        NO                        NO                        NO                        NO

 

④識別キー(ALL)サプリメンタル・ロギング
ロギングレベルを識別キー(ALL)サプリメンタル・ロギングにした場合、REDOログファイルに出力される内容を検証します

【検証手順】
1. 識別キー(ALL)サプリメンタル・ロギング設定
2. 分析対象のREDOログファイル特定
3. LogMinerディクショナリ・ファイル作成
4. 分析対象のREDOログファイル追加
5. LogMiner起動
6. テストデータ更新
7. 分析
8. LogMiner終了
9. 識別キー(ALL)サプリメンタル・ロギング削除

【作業ログ】

1. 識別キー(ALL)サプリメンタル・ロギング設定
SQL> select supplemental_log_data_min, supplemental_log_data_pk, supplemental_log_data_ui, supplemental_log_data_fk, supplemental_log_data_all from v$database;

SUPPLEMENTAL_LOG_DATA_MIN SUPPLEMENTAL_LOG_DATA_PK  SUPPLEMENTAL_LOG_DATA_UI  SUPPLEMENTAL_LOG_DATA_FK  SUPPLEMENTAL_LOG_DATA_ALL
------------------------- ------------------------- ------------------------- ------------------------- -------------------------
NO                        NO                        NO                        NO                        NO

SQL> alter database add supplemental log data (all) columns;

データベースが変更されました。

SQL> select supplemental_log_data_min, supplemental_log_data_pk, supplemental_log_data_ui, supplemental_log_data_fk, supplemental_log_data_all from v$database;

SUPPLEMENTAL_LOG_DATA_MIN SUPPLEMENTAL_LOG_DATA_PK  SUPPLEMENTAL_LOG_DATA_UI  SUPPLEMENTAL_LOG_DATA_FK  SUPPLEMENTAL_LOG_DATA_ALL
------------------------- ------------------------- ------------------------- ------------------------- -------------------------
IMPLICIT                  NO                        NO                        NO                        YES

2. 分析対象のREDOログファイル特定
SQL> select
  2    l.GROUP#, l.THREAD#, l.SEQUENCE#, l.STATUS, lf.MEMBER
  3  from
  4    v$log l, v$logfile lf
  5  where
  6    l.GROUP#  = lf.GROUP#
  7  order by
  8    l.GROUP#, l.THREAD#;

    GROUP#    THREAD#  SEQUENCE# STATUS          MEMBER
---------- ---------- ---------- --------------- ----------------------------------------
         1          1         43 INACTIVE        +DATA/ORCL/ONLINELOG/redo01.log
         2          1         44 CURRENT         +DATA/ORCL/ONLINELOG/redo02.log
         3          2         39 CURRENT         +DATA/ORCL/ONLINELOG/redo03.log
         4          2         38 INACTIVE        +DATA/ORCL/ONLINELOG/redo04.log

3. LogMinerディクショナリ・ファイル作成
本検証ではオンライン・カタログを使用するため、LogMinerディクショナリ・ファイル作成は割愛します

4. 分析対象のREDOログファイル追加
SQL> EXECUTE DBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME => '+DATA/ORCL/ONLINELOG/redo02.log', OPTIONS => DBMS_LOGMNR.NEW);

PL/SQLプロシージャが正常に完了しました。

RACなのでスレッド2の方も追加
SQL> EXECUTE DBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME => '+DATA/ORCL/ONLINELOG/redo03.log', OPTIONS => DBMS_LOGMNR.ADDFILE);

PL/SQLプロシージャが正常に完了しました。

★ログスイッチ等でREDOログファイルが切り替わっても対応できるよう、CURRENT以外のREDOログ・ファイルも追加
SQL> EXECUTE DBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME => '+DATA/ORCL/ONLINELOG/redo01.log', OPTIONS => DBMS_LOGMNR.ADDFILE);

PL/SQLプロシージャが正常に完了しました。

SQL> EXECUTE DBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME => '+DATA/ORCL/ONLINELOG/redo04.log', OPTIONS => DBMS_LOGMNR.ADDFILE);

PL/SQLプロシージャが正常に完了しました。

5. LogMiner起動
SQL> EXECUTE DBMS_LOGMNR.START_LOGMNR(Options => DBMS_LOGMNR.DICT_FROM_ONLINE_CATALOG + DBMS_LOGMNR.COMMITTED_DATA_ONLY);

PL/SQLプロシージャが正常に完了しました。

6. テストデータ更新(別セッション)
TeraTermを立ち上げ、別セッションでテストデータを更新します

SQL> select * from emp where EMPNO = 7369;

     EMPNO ENAME      JOB               MGR HIREDATE        SAL       COMM     DEPTNO
---------- ---------- ---------- ---------- -------- ---------- ---------- ----------
      7369 SMITH      CLERK            7902 80-12-17      10000                    10

SQL> update emp set SAL = 800 where EMPNO = 7369;

1行が更新されました。

SQL> commit;

コミットが完了しました。

SQL> select * from emp where EMPNO = 7369;

     EMPNO ENAME      JOB               MGR HIREDATE        SAL       COMM     DEPTNO
---------- ---------- ---------- ---------- -------- ---------- ---------- ----------
      7369 SMITH      CLERK            7902 80-12-17        800                    10

7. 分析
SQL> SELECT
  2    SCN, TO_CHAR(TIMESTAMP, 'YYYY/MM/DD HH24:MI:SS') AS TM_STAMP, OPERATION, SQL_REDO, SQL_UNDO
  3  FROM
  4    V$LOGMNR_CONTENTS
  5  WHERE
  6        USERNAME = 'SCOTT'
  7  ORDER BY
  8    SCN DESC;

       SCN TM_STAMP             OPERATION  SQL_REDO                                                     SQL_UNDO
---------- -------------------- ---------- ------------------------------------------------------------ ------------------------------------------------------------
   6736256 2022/11/11 11:11:53  UPDATE     update "SCOTT"."EMP" set "SAL" = '800' where "EMPNO" = '7369 update "SCOTT"."EMP" set "SAL" = '10000' where "EMPNO" = '73
                                           ' and "ENAME" = 'SMITH' and "JOB" = 'CLERK' and "MGR" = '790 69' and "ENAME" = 'SMITH' and "JOB" = 'CLERK' and "MGR" = '7
                                           2' and "HIREDATE" = TO_DATE('80-12-17', 'RR-MM-DD') and "SAL 902' and "HIREDATE" = TO_DATE('80-12-17', 'RR-MM-DD') and "S
                                           " = '10000' and "COMM" IS NULL and "DEPTNO" = '10' and ROWID AL" = '800' and "COMM" IS NULL and "DEPTNO" = '10' and ROWID
                                            = 'AAAWwdAAGAAAADFAAA';                                      = 'AAAWwdAAGAAAADFAAA';
SQL_REDO列のWHERE句がEMP表のすべての列とROWIDに変わっていました

8. LogMiner終了
SQL> EXECUTE DBMS_LOGMNR.END_LOGMNR();

PL/SQLプロシージャが正常に完了しました。

9. 識別キー(ALL)サプリメンタル・ロギング削除
SQL> select supplemental_log_data_min, supplemental_log_data_pk, supplemental_log_data_ui, supplemental_log_data_fk, supplemental_log_data_all from v$database;

SUPPLEMENTAL_LOG_DATA_MIN SUPPLEMENTAL_LOG_DATA_PK  SUPPLEMENTAL_LOG_DATA_UI  SUPPLEMENTAL_LOG_DATA_FK  SUPPLEMENTAL_LOG_DATA_ALL
------------------------- ------------------------- ------------------------- ------------------------- -------------------------
IMPLICIT                  NO                        NO                        NO                        YES

SQL> alter database drop supplemental log data (all) columns;

データベースが変更されました。

SQL> select supplemental_log_data_min, supplemental_log_data_pk, supplemental_log_data_ui, supplemental_log_data_fk, supplemental_log_data_all from v$database;

SUPPLEMENTAL_LOG_DATA_MIN SUPPLEMENTAL_LOG_DATA_PK  SUPPLEMENTAL_LOG_DATA_UI  SUPPLEMENTAL_LOG_DATA_FK  SUPPLEMENTAL_LOG_DATA_ALL
------------------------- ------------------------- ------------------------- ------------------------- -------------------------
NO                        NO                        NO                        NO                        NO

 

■参考資料
使用例1: LogMinerを使用した特定のユーザーによる変更の追跡
LogMinerディクショナリ・オプション
DBMS_LOGMNR
データベース・レベルのサプリメンタル・ロギング

Data Guardの保護モード

Data Guardの保護モードについて調査しました。

製品バージョンによって、保護モードの設定方法が変わるものもありますが、今回は12cR1を調査対象としています。

保護モードには3つあります。
・最大保護モード
・最大可用性モード
・最大パフォーマンスモード

保護モードはデータ保護、システム性能など優先する要件によって使い分けます。
・データ保護(プライマリDBとスタンバイDBのデータ整合性など)
・システム性能(プライマリDBに対するスタンバイDBからの応答速度など)

最大保護モード
プライマリDBとスタンバイDBでデータ整合性(プライマリDB障害時、スタンバイDBはデータロストなし)が保証されている状態です
プライマリDBでデータコミットし、REDOデータ転送後、プライマリDBはスタンバイDB(RFSプロセス)からコミット応答を待ちます
コミット応答受信後、プライマリDBもコミット確定します

障害等でスタンバイDBから応答がない場合、プライマリDBは停止します
一般的にはプライマリDBの停止を回避するため、マルチスタンバイ構成にし、1つのスタンバイDBが壊れてもプライマリDBが停止しないようにします
データ保護を優先するモードです

SQL> select protection_mode, protection_level from v$database;

PROTECTION_MODE                PROTECTION_LEVEL
------------------------------ ------------------------------
MAXIMUM PROTECTION             MAXIMUM PROTECTION

最大可用性モード
プライマリDBとスタンバイDBでデータ整合性(プライマリDB障害時、スタンバイDBはデータロストなし)が(通常時は)保証されている状態です
プライマリDBでデータコミットし、REDOデータ転送後、プライマリDBはスタンバイDB(RFSプロセス)からコミット応答を待ちます
コミット応答受信後、プライマリDBもコミット確定します
ただし、プライマリDBから受信したREDOデータをスタンバイDBで確認するタイミングによって、コミット応答のタイミングが変わります

確認タイミングがAFFIRMの場合、プライマリDBから受信したREDOデータをスタンバイREDOログへ書き込んだ後にプライマリDBへコミット応答します(データ書込み完了まで待って応答する)
確認タイミングがNOAFFIRMの場合、プライマリDBから受信したREDOデータをスタンバイREDOログへ書き込む前にプライマリDBへコミット応答します(データ書き込み完了を待たず応答する)
※11gR2では、確認タイミングに指定できるのはAFFIRMだけでした

障害等でスタンバイDBから応答がない場合、保護モードを最大パフォーマンスモードへ遷移して稼働を継続します
通常時はデータ保護、障害時はシステム性能を優先するモードです

SQL> select protection_mode, protection_level from v$database;

PROTECTION_MODE                PROTECTION_LEVEL
------------------------------ ------------------------------
MAXIMUM AVAILABILITY           MAXIMUM AVAILABILITY

最大パフォーマンスモード
プライマリDBとスタンバイDBでデータ整合性(プライマリDB障害時、スタンバイDBはデータロストあり)が保証されない状態です
プライマリDBでデータコミット、REDOデータ転送後、プライマリDBはスタンバイDB(RFSプロセス)からコミット応答を待たずにコミット確定します
デフォルトの保護モードです
システム性能を優先するモードです

SQL> select protection_mode, protection_level from v$database;

PROTECTION_MODE                PROTECTION_LEVEL
------------------------------ ------------------------------
MAXIMUM PERFORMANCE            MAXIMUM PERFORMANCE

保護モードの動きを整理しました。

同期モード(SYNC AFFIRM)

準同期モード(SYNC NOAFFIRM)

非同期モード(ASYNC NOAFFIRM)


REDOデータ転送プロセス
NSS (Network Server Sync)
プライマリDBのREDOデータを同期形式でスタンバイDBのRFSプロセスへ転送するプロセス
プライマリDBはデータコミットし、REDOデータ転送後、スタンバイDBのRFSプロセスからの応答を待つ

NSA (Network Server Async)
プライマリDBのREDOデータを非同期形式でスタンバイDBのRFSプロセスへ転送するプロセス
プライマリDBはデータコミットし、REDOデータ転送後、スタンバイDBのRFSプロセスからの応答を待たない

REDOデータ転送モード
SYNC
プライマリDBはデータコミットし、REDOデータ転送後、スタンバイDBのRFSプロセスからの応答を待つ

ASYNC
プライマリDBはデータコミットし、REDOデータ転送後、スタンバイDBのRFSプロセスからの応答を待たない

●受信したREDOデータの確認タイミング(コミット応答タイミング)
AFFRIM
プライマリDBから受信したREDOデータをスタンバイREDOログへ書き込んだ後に確認する(スタンバイREDOログに書き込み完了したらコミット応答する)

NOAFFIRM
プライマリDBから受信したREDOデータをスタンバイREDOログへ書き込む前に確認する(スタンバイREDOログに書き込む前にコミット応答する)

【参考】
プライマリDBで保護モードを変更する際、プライマリDBがOPENモードだと下記エラーが発生しました
MOUNTモードで再起動後、alter文を再実行したらコマンド正常終了しました

------------------------------------------------------------
SQL> alter database set standby database to maximize PROTECTION;
alter database set standby database to maximize PROTECTION
*
行1でエラーが発生しました。:
ORA-01126: データベースはこのインスタンスでマウントし、どのインスタンスでもオープンしないでください
------------------------------------------------------------

■参考文献
Oracle Data Guardの保護モード
LOG_ARCHIVE_DEST_nパラメータの属性
Active Data Guard diffs from 11gR2 to 19c - Speaker Deck