データベースについて調べてみた

データベースについて調べたことのメモ。Oracle要素多めになる予定。

表パーティションに対する操作で索引がどうなるか調べてみた ~ その2 ~

前回からの続きです。今回はパーティション表に索引を付与した状態で、表のパーティションに対してTRUNCATEした場合に、索引の状態がどうなるかを調べてみました。

 

前回の再掲ですが、今回の実機調査(19.22.0)で使用したパーティション表、ローカル索引、グローバル非パーティション索引、グローバルパーティション索引のDDLは以下となります。

-- パーティション表のDDL
CREATE TABLE tab07(
 id NUMBER(10) NOT NULL,
 ym CHAR(6) NOT NULL,
 cls NUMBER(5),
 rnk NUMBER(5),
 memo CHAR(50)
)
PARTITION BY RANGE (ym) (
 PARTITION pt_2021 VALUES LESS THAN ('202201'),
 PARTITION pt_2022 VALUES LESS THAN ('202301'),
 PARTITION pt_2023 VALUES LESS THAN ('202401'),
 PARTITION pt_2024 VALUES LESS THAN ('202501'),
 PARTITION pt_2025 VALUES LESS THAN ('202601')
);

-- ローカル索引のDDL
CREATE UNIQUE INDEX idx01tab07 ON tab07(id, ym) LOCAL;

-- グローバル非パーティション索引のDDL
CREATE INDEX idx02tab07 ON tab07(rnk) GLOBAL;

-- グローバルパーティション索引のDDL
CREATE INDEX idx03tab07 ON tab07(cls)
GLOBAL PARTITION BY RANGE (cls) (
 PARTITION pt_00000 VALUES LESS THAN (05000),
 PARTITION pt_05000 VALUES LESS THAN (10000),
 PARTITION pt_10000 VALUES LESS THAN (15000),
 PARTITION pt_15000 VALUES LESS THAN (20000),
 PARTITION pt_20000 VALUES LESS THAN (MAXVALUE)
);

 

 

4. 表パーティションのTRUNCATE

指定したパーティションのみのTRUNCATEは、以下のSQLで実行できます。前回調査したダイレクトパスロードのときは、グローバル索引が存在したことでエラーとなりましたが、パーティションのTRUNCATEについては実行することができました。

SQL> ALTER TABLE tab07 TRUNCATE PARTITION pt_2023;

表が切り捨てられました。

 

TRUNCATE後の索引の状態を確認してみると、ローカル索引(IDX01TAB07)はUSABLE(使用可能)のままでしたが、2つのグローバル索引(IDX02TAB07, IDX03TB07)のstatus列がUNUSABLE(使用不可)になっていました。

SQL> SELECT index_name, status, orphaned_entries FROM user_indexes ORDER BY 1;

INDEX_NAME      STATUS   ORP
--------------- -------- ---
IDX01TAB07      N/A      NO
IDX02TAB07      UNUSABLE NO
IDX03TAB07      N/A      NO


SQL> SELECT index_name, partition_name, status, orphaned_entries FROM user_ind_partitions ORDER BY 1,2;

INDEX_NAME      PARTITION_NAME  STATUS   ORP
--------------- --------------- -------- ---
IDX01TAB07      PT_2021         USABLE   NO
IDX01TAB07      PT_2022         USABLE   NO
IDX01TAB07      PT_2023         USABLE   NO
IDX01TAB07      PT_2024         USABLE   NO
IDX01TAB07      PT_2025         USABLE   NO
IDX03TAB07      PT_00000        UNUSABLE NO
IDX03TAB07      PT_05000        UNUSABLE NO
IDX03TAB07      PT_10000        UNUSABLE NO
IDX03TAB07      PT_15000        UNUSABLE NO
IDX03TAB07      PT_20000        UNUSABLE NO

 

結論として、表のパーティションがTRUNCATEされると、グローバル索引は全て使用不可になるため、再作成が必要となります。

ただ、これでは面倒なので、パーティションをTRUNCATEする際に、UPDATE INDEXES句を付与することで、グローバル索引のメンテナンスが実行され、パーティションTRUNCATE後の再作成作業が不要となります。

SQL> ALTER TABLE tab07 TRUNCATE PARTITION pt_2023 UPDATE INDEXES;

表が切り捨てられました。


SQL> SELECT index_name, status, orphaned_entries FROM user_indexes ORDER BY 1;

INDEX_NAME      STATUS   ORP
--------------- -------- ---
IDX01TAB07      N/A      NO
IDX02TAB07      VALID    YES
IDX03TAB07      N/A      NO


SQL> SELECT index_name, partition_name, status, orphaned_entries FROM user_ind_partitions ORDER BY 1,2;

INDEX_NAME      PARTITION_NAME  STATUS   ORP
--------------- --------------- -------- ---
IDX01TAB07      PT_2021         USABLE   NO
IDX01TAB07      PT_2022         USABLE   NO
IDX01TAB07      PT_2023         USABLE   NO
IDX01TAB07      PT_2024         USABLE   NO
IDX01TAB07      PT_2025         USABLE   NO
IDX03TAB07      PT_00000        USABLE   YES
IDX03TAB07      PT_05000        USABLE   YES
IDX03TAB07      PT_10000        USABLE   YES
IDX03TAB07      PT_15000        USABLE   YES
IDX03TAB07      PT_20000        USABLE   YES

 

グローバル非パーティション索引(IDX02TAB07)のstatus列がVALID、グローバルパーティション索引(IDX03TAB07)のstatus列がUSABLEとなっていることから、先程とは違い両グローバル索引がパーティションTRUNCATE後も使用可能であることがわかります。

ただしグローバル索引のorphaned_entries列がYESになっている点に注意が必要です。これは、Oracle12cからUPDATE INDEXES句指定時のグローバル索引のメンテナンス時間を短縮するために非同期化されたことで、グローバル索引の中にTRUNCATEされたレコードの情報が残存(索引スキャン時にヒットはしない様になっているイメージ)していることを表しています。マニュアル上の表現ではグローバル索引中に孤立データが、乱暴な表現ではグローバル索引中にゴミが残った状態の様なものなので、残置しておくと索引のスキャン効率が低下するため、クリーンアップする必要があります。
デフォルトでは、毎日2:00に自動実行される自動スケジューラジョブSYS.PMO_DEFERRED_GIDX_MAINT_JOBによって、このグローバル索引中のゴミがクリーンアップされ、orphaned_entries列がNOに戻ります。自動実行ではなく、手動でクリーンアップしたい場合は、以下の手段があります。

  1. SYS.PMO_DEFERRED_GIDX_MAINT_JOBスケジューラジョブの手動実行
  2. DBMS_PART.CLEANUP_GIDXプロシージャの実行
  3. ALTER INDEX文によるクリーンアップ処理の実行

3のALTER INDEX文によるクリーンアップについて、以降で触れたいと思います。

 

 

5. ALTER INDEX文によるグローバル索引のメンテナンス

ALTER INDEX文を用いた索引のメンテナンス方法として、大きく4種類があります。ALTER INDEXの構文としては以下のようになります。
※グローバルパーティション索引の場合は、パーティションの指定が必要となりますが、ここでは省略します。

ALTER INDEX <索引名> <指定句>;

 

指定句の部分に何を指定するかによって、索引のメンテナンス範囲が変わるので、その違いを以下に纏めてみました。「索引の高さ」はBツリー索引の高さの是正を、「リーフの縮小」は索引のリーフブロックのコンパクションを、「孤立データ」はグローバル索引における孤立データのクリーンアップの可否を示しています。

No. 指定句 索引の高さ リーフの縮小 孤立データ
1 REBUILD
2 COALESCE × ×
3 COALESCE CLEANUP ×
4 COALESCE CLEANUP ONLY × ×

 

パーティションTRUNCATEにおけるグローバル索引の孤立データ(所謂ゴミ)のクリーンアップを目的とした場合、指定句にCOALESCEだけを指定したALTER INDEX文の実行では目的を達成できません。実際に実行した場合のuser_indexesディクショナリビューのorphaned_entries列の値を確認すると、YESのままになっていました。

SQL> ALTER INDEX idx02tab07 COALESCE;

索引が変更されました。

SQL> SELECT index_name, status, orphaned_entries FROM user_indexes ORDER BY 1;

INDEX_NAME                     STATUS   ORP
------------------------------ -------- ---
IDX02TAB07                     VALID    YES

 

指定句にCOALESCE CLEANUP ONLYを指定した場合は、orphaned_entries列の値がNOに変わったことから、孤立データのクリーンアップができていることが確認できました。

SQL> ALTER INDEX idx02tab07 COALESCE CLEANUP ONLY;

索引が変更されました。

SQL> SELECT index_name, status, orphaned_entries FROM user_indexes ORDER BY 1;

INDEX_NAME                     STATUS   ORP
------------------------------ -------- ---
IDX02TAB07                     VALID    NO

 

6. さいごに

前回の内容含め、グローバル索引は性能面でメリットがありますが、パーティション単位でのダイレクトロードができなかったり、パーティションTRUNCATEによる索引メンテナンスが必要だったりと、メンテナンス性に難があることをご理解頂けたかと思います。グローバル索引のメリット・デメリットを理解した上で、索引設計をして頂ければと思います。

 

 

次の記事:マテリアライズドビューを使ったDB移行方法を調べてみた ~ その1 ~

 

前回の記事: 表パーティションに対する操作で索引がどうなるか調べてみた ~ その1 ~