ねら~ITエンジニア雑記

やきうのお兄ちゃんが綴るOracle Databaseメインのブログ

APPENDヒント付きの INSERT 〜 SELECT 〜 文のロック範囲が、INSERT文側のPARTITION句有無で異なる。

通常、APPENDヒント付きの INSERT 〜 SELECT文 は 排他ロック を表全体で獲得します。

例えば以下の SQL文 を別セッションから併行で実行すると、
後から実行した SQL は TM enqueue で待機します。

-- ★セッション1
INSERT /*+ APPEND */ INTO TBL_B SELECT * FROM TBL_A PARTITION(P00);
9999 rows created.

-- ★セッション2
INSERT /*+ APPEND */ INTO TBL_B SELECT * FROM TBL_A PARTITION(P01);
※待たされる。

-- ★セッション3
SET LINESIZE 300;
COLUMN SID FORMAT 99999;
COLUMN EVENT FORMAT A40;
COLUMN BINS FORMAT 999;
COLUMN BSID FORMAT 99999;
SELECT SID, SERIAL#, SQL_ID, EVENT, BLOCKING_INSTANCE AS BINS, BLOCKING_SESSION AS BSID
FROM V$SESSION WHERE STATUS = 'ACTIVE' AND USERNAME IS NOT NULL;

   SID    SERIAL# SQL_ID        EVENT                                    BINS   BSID
------ ---------- ------------- ---------------------------------------- ---- ------
   137       2297 8c6anh1adh59k enq: TM - contention                        1    202 ★TMロック待ち

SET LINESIZE 300;
COLUMN SESS FORMAT A20;
SELECT DECODE(request,0,'Holder: ','Waiter: ') || 
          sid sess, id1, id2, lmode, request, type
   FROM V$LOCK
 WHERE (id1, id2, type) IN (SELECT id1, id2, type FROM V$LOCK WHERE request > 0)
   ORDER BY id1, request;

SESS                        ID1        ID2      LMODE    REQUEST TY
-------------------- ---------- ---------- ---------- ---------- --
Holder: 202              104275          0          6          0 TM ★TMロック取得済み
Waiter: 137              104275          0          0          6 TM ★TMロック待ち

上記のようなケースで、INSERT文側に PARTITION句 を付与すると、ロック範囲が PARTITION単位 に 限定されるため、
APPENDヒント付きの INSERT 〜 SELECT文 を PARTITION単位で同時併行で実行できます。

-- ★セッション1
INSERT /*+ APPEND */ INTO TBL_B PARTITION(P00) SELECT * FROM TBL_A PARTITION(P00);
                ^^^^^^^^^^^^^^コレ
9999 rows created.

-- ★セッション2
INSERT /*+ APPEND */ INTO TBL_B PARTITION(P01) SELECT * FROM TBL_A PARTITION(P01);
                ^^^^^^^^^^^^^^コレ
10000 rows created.

-- ★セッション3
SET LINESIZE 300;
SELECT sid, id1, id2, lmode, request, type
  FROM v$lock
 WHERE sid in (202, 137)
 ORDER BY type, id1, sid, request;

   SID        ID1        ID2      LMODE    REQUEST TY
------ ---------- ---------- ---------- ---------- --
   137        100          0          4          0 AE
   202        100          0          4          0 AE
   137     104275          0          3          0 TM ★104275 のTMロックを LMODE=3 で取得 ※
   202     104275          0          3          0 TM ★104275 のTMロックを LMODE=3 で取得 ※
   202     104276          0          6          0 TM ★104756 のTMロック(LMODE=6) を取得
   137     104277          0          6          0 TM ★104277 のTMロック(LMODE=6) を取得
   202      79833          1          3          0 TO
   202     196630       6342          6          0 TX
   137     458766       5920          6          0 TX

9 rows selected.

LMODE=3同士は競合しないため、ロックをお互いに取得できます。そして、ここに出て来る104275, 104276, 104277 は DBA_OBJECTS の OBJECT_ID に対応しているようです。

SELECT OBJECT_ID
     , OBJECT_NAME
     , SUBOBJECT_NAME
  FROM DBA_OBJECTS
 WHERE OBJECT_ID IN (104275, 104276, 104277);

 OBJECT_ID OBJECT_NAME                    SUBOBJECT_NAME
---------- ------------------------------ ------------------------------
    104275 TBL_B
    104276 TBL_B                          P00
    104277 TBL_B                          P01

表全体はLMODE=3で競合しないようにロック取得して、
パーティション単位で排他ロックを取得しているのが解ったZe!!!(`・ω・)Ъ