第81回 夜な夜な なにわオラクル塾 - oracle...king miller clark scott adams ford turner...
TRANSCRIPT
-
実践!! パフォーマンス・チューニング ~ 索引チューニング編 ~
日本オラクル株式会社 2012年04月25日
第81回 夜な夜な! なにわオラクル塾
-
Copyright© 2012, Oracle. All rights reserved. 2
以下の事項は、弊社の一般的な製品の方向性に関する概要を説明するものです。また、情報提供を唯一の目的とするものであり、いかなる契約にも組み込むことはできません。以下の事項は、マテリアルやコード、機能を提供することをコミットメント(確約)するものではないため、購買決定を行う際の判断材料になさらないで下さい。オラクル製品に関して記載されている機能の開発、リリースおよび時期については、弊社の裁量により決定されます。
OracleとJavaは、Oracle Corporation 及びその子会社、関連会社の米国及びその他の国における登録商標です。文中の社名、商品名等は各社の商標または登録商標である場合があります。
-
Copyright© 2012, Oracle. All rights reserved. 3
本セミナーの目的とゴール
全体
• 索引の役割を理解し、適切な運用方法を身に付けること • 索引の種類と役割を理解する
• Oracleデータベースの索引の種類
• 索引の構造の理解
• 索引が利用される条件を理解する
• SQLの応答時間改善へのアプローチ
• オプティマイザの理解
• 索引を使用してのSQL応答性能改善
• 索引のメンテナンス
-
Copyright© 2012, Oracle. All rights reserved.
内容
• Bツリー索引の構造 • 全表走査と索引走査のちがい
• オプティマイザによる索引走査/全表走査の判断
• ヒストグラムによる索引利用の効率化
• 索引チューニングのポイント
• さまざまな索引の種類
4
-
Copyright© 2012, Oracle. All rights reserved.
索引の種類
• 索引とは
• 百科事典についている索引のように、特定の項目を早く見つけるためのオブジェクト
• 検索条件で使用する表の列に対して作成
5
索引の種類 役割
Bツリー索引 デフォルトの設定で、最も一般的
Bツリー・クラスタ索引 クラスタ用に定義
ハッシュ・クラスタ索引 ハッシュ・クラスタ用に定義
グローバル索引とローカル索引 パーティション表とパーティション索引に関連
逆キー索引 RACアプリケーションに最も役立つ
ビットマップ索引 サイズが小さいので、小さい値の集合を持つ列に効果的
ファンクション索引 事前計算された関数や式の値を含む
ドメイン索引 アプリケーションまたはカートリッジに固有の索引
複合索引 複数列に定義されたBツリー索引
索引構成表 表の主キーに定義された索引にデータを格納
-
Copyright© 2012, Oracle. All rights reserved.
データ検索方法
6
データ検索
索引走査
(インデックススキャン)
全表走査
(フルスキャン)
• 全表走査(フルスキャン)
• 全てのデータを検索、比較して該当データを取得
• 索引走査(インデックススキャン)
• 索引にアクセスし、索引ブロックから行アドレス(ROWID)を取得
• ROWIDを使用して直接該当データの入ったブロックにアクセス
-
Copyright© 2012, Oracle. All rights reserved.
全表走査(フルスキャン)
7
EMP表
SMITH
ALLEN
WARD
KING
MILLER
CLARK
SCOTT
ADAMS
FORD
TURNER
BLAKE
TIGER
データがバラバラに入っている
BLAKEのデータが欲しい データブロック
表のブロックデータを全て読み込み
BLAKEのデータを返す
表にあるすべての行を読み取り、 選択基準を満たしていない行を
フィルタリング
-
Copyright© 2012, Oracle. All rights reserved.
索引走査(インデックススキャン)
8
索 引
KEY ROWID
ALLEN rowid
BLAKE rowid
CLARK rowid
EMP表
SMITH
ALLEN
WARD
KING
MILLER
CLARK
SCOTT
ADAMS
FORD
TURNER
BLAKE
TIGER
キー順に
並んでいる
データの
ある位置
データがバラバラに入っている
BLAKEのデータが欲しい
少量のデータを読む場合、
読まなければならない
ブロックが少なくなる
①索引を読み込み
BLAKEを発見
①
②索引のROWIDを利用し、
表の中からBLAKEの
データを読み込む
②
BLAKEのデータを返す
-
Copyright© 2012, Oracle. All rights reserved.
全表走査と索引走査
9
• 全表走査(フルスキャン) – 全てのデータを検索、比較する必要がある
– マルチブロック READをサポート
• 索引走査(インデックススキャン) – 索引は列値の昇順に格納される
– 索引ブロックから行アドレス(ROWID)を得て直接行を検索する
– シングルブロック READ
– 更新時に負荷がかかる
基本的に選択行数が少ない方が索引走査が有利になる。
-
Copyright© 2012, Oracle. All rights reserved.
全表走査と索引走査
10
選択行数によっては全表走査の方が速いので、厳密に チューニングするのであれば、全表操作時と索引走査時 のタイムを測って、パフォーマンスの良い方を選択する
例)社員番号(EMPNO) :1001~2000番の社員リストが欲しい EMP(社員)表
• データ件数:社員番号1~2000番の2000件
(社員番号1001~2000番の社員は全体の50%)
• サイズ:1600KB (ブロックサイズ:8K、200ブロック(1ブロックあたり10件格納))
select * from EMP
where EMPNO > 1000
and ENPNO < 2001;
索引走査 (EMPNOに索引ありの場合)
• I/O: 100ブロック+索引ブロック = 100+α
全表走査 (DB_FILE_MULTIBLOCK_READ_COUNT=8の場合)
• I/O: 200ブロック/8 = 25 1/2の絞込み
○
×
?
-
Copyright© 2012, Oracle. All rights reserved.
マルチブロックREAD
11
1回のI/Oで複数のブロックを読み込む機能
→ DISKへのI/O回数を減らす事ができる 表
ブロック
8ブロック単位
でデータを読み込む
初期化パラメータ db_file_multiblock_read_count = 8
全表走査 → マルチブロックREAD
索引走査 → シングルブロックREAD
現在のシステムは一般的に、DISK I/O ネックになる事が多いので効果大
選択率が高くなる程、
全表走査が有利になる
-
Copyright© 2012, Oracle. All rights reserved.
マルチブロックREADの効果例
12
マルチブロックREADの設定を変えてSQL文を実行経過時間を比較
<補足> db_file_multiblock_read_countの最大値は?
db_file_multiblock_read_count =< 最大I/Oサイズ / db_block_size
※最大I/Oサイズは、オペレーティング・システムの制限を受けます。
SQL> alter system set db_file_multiblock_read_count=1;
SQL> SELECT * FROM employees;
経過: 00:00:04.35
SQL> alter system set
db_file_multiblock_read_count=10;
SQL> SELECT * FROM employees;
経過: 00:00:01.37
-
Copyright© 2012, Oracle. All rights reserved.
Bツリー索引の構成(1)
13
本の索引は、例えば、アルファベット順にソートされた索引だったら、 ‘A’の項目の方が‘X’の項目よりも見つけやすい(先頭に近い値が有利)
この問題を解決して、どの値に対しても同じ工数でアクセスできるよう バランス化したのがBツリー索引
ルートブロック
ブランチブロック
リーフブロック
-
Copyright© 2012, Oracle. All rights reserved.
Bツリー索引の構成(2)
14
• ルートブロックからリーフブロックまでの高さが一定
• リーフブロックには列のキー列値とROWIDを含む
• NULL値は含まれない
• WHERE句で頻繁に使われる列に作成すると有効
ABE - rowid
BABA - rowid
ENDO - rowid
<社員名列の索引イメージ>
ブランチ ブロック
リーフ
ブロック
ルート
ブロック
高さ Mよりも前
Gよりも前
M以降
G以上
( M )
・・・ ・・・
・・・
( S )
・・・
( G )
-
Copyright© 2012, Oracle. All rights reserved.
Bツリー 索引による検索イメージ
15
• ルートブロックから順にリーフブロックをたどって該当データを検索
• リーフ・ブロック(最下層)には列のキー列値とROWID(物理アドレス) が含まれる
Suzukiさんのデータが欲しい
SELECT * FROM 社員表 WHERE 社員名=‘Suzuki’;
Abe rowid
Baba rowid
・・・・・
Fujita rowid Hirota rowid
・・・・・
Mよりも前
Gよりも前
M以降
G以降
( M )
Morita rowid Nakata rowid
・・・・・
( S ) ( G )
Sよりも前
Suzuki rowid Tanaka rowid
・・・・・
S以降
どのデータにも3ブロックの索引 アクセス+対象データのブロック (計4ブロック)でアクセス可能
社員番号 名前 勤務地 性別
1 Tanaka 関東 男
2 Suzuki 関東 女
3 Yoshida 東北 男
4 Abe 関西 女
5 Inoue 関東 男
ROWID1
ROWID2
ROWID3
ROWID4
ROWID5
-
Copyright© 2012, Oracle. All rights reserved.
索引はどういう基準でつけるべき?
• 主キーは必ず作成する • 主キー制約を設定すると索引が自動的に作られる
• 索引をつける対象の列の選定 1. システムで発行するSQL文を洗い出して、絞込条件になる列をピックアップ
2. 洗い出した列に対して以下の観点で索引をつけるかどうかを決める
• レコード件数が1/50より絞り込める場合は索引をつける
• レコード数が1/50~1/10の絞り込みの場合は、速度の向上具合と、更新に伴う性能劣化を考慮して決める
• レコード数が1/10より絞り込めない場合は索引をつけない
• 実際のしきい値はレコード長など各種条件によって異なるので、あくまでも目安とすること
3. SQLパフォーマンスチューニングに関する知識があれば、結合対象列、ソート対象列についても索引をつけることを検討する
16
-
Copyright© 2012, Oracle. All rights reserved.
索引を使う欠点
17
表データと索引データを更新しなければならない
→ 更新処理においては、負荷がかかる
NAME SAL …
ALLEN 100
BLAKE 500
CLARK 300
EMP表
NAME列の索引 SAL列の索引
key rowid
100 rowid
300 rowid
500 rowid
…
key rowid
ALLEN rowid
BLAKE rowid
CLARK rowid
… 更新
更新 更新
• あまりたくさんつけない • 検索は早くなるが更新が遅くなる
• 絞込みがあまり出来ない場合は索引をつけない方が検索が早い
-
Copyright© 2012, Oracle. All rights reserved. 18
オプティマイザによる索引走査/全表走査の判断
-
Copyright© 2012, Oracle. All rights reserved.
SQL文の処理ステップ
1. 発行されたSQL文はパーサーによってパース(解析)
• SQL文の構文チェック、意味チェック(表、列が存在するか等)
• 「同一SQL文」が共有プールにキャッシュされているかチェック
• キャッシュに存在すれば、すぐに実行する(soft parse)
• キャッシュに存在しなければ、オプティマイザによる処理を行う(hard parse)
2. オプティマイザにより最適な実行計画を検討
• 索引を利用するか、全表走査するか
• 複数表を結合する場合にどの順番で、どの結合方法を使うかetc
3. ジェネレータがオプティマイザが生成した実行計画を受け取り、実行
19
オプティマイザ
SQL文
パーサ(Parser) SQL実行
出力
行ソース
ジェネレータ
Soft Parse
Hard Parse
-
Copyright© 2012, Oracle. All rights reserved.
SQL文の実行計画
• 実行計画の調べ方 • SQL*PLUSのAUTOTRACEコマンド
• Explain plan for
• SQLトレース
• V$SQL及びV$SQL_PLAN
• Oracle Enterprise Manager
• SQL Developer
20
1. SYSユーザでPLUSTRACEロールを作成し、SQLを実行するユーザに付与する。
SQL> @%ORACLE_HOME%¥sqlplus¥admin¥plustrce.sql
SQL> GRANT plustrace TO scott;
2. SQLを実行するユーザで実行計画を保存するための表(PLAN_TABLE)を作成する。
SQL> connect scott/tiger
SQL> @%ORACLE_HOME%¥rdbms¥admin¥utlxplan.sql
3. AUTOTRACE 機能を ON にし、SQL文を実行する。
SQL> SET AUTOTRACE ON
SQL> SELECT ...
-
Copyright© 2012, Oracle. All rights reserved.
実行計画の例
21
SELECT last_name,department_name FROM employees JOIN departments USING (department_id);
------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|・・・ ------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 106 | 2862 | 6 (17)|
| 1 | MERGE JOIN | | 106 | 2862 | 6 (17)|
| 2 | TABLE ACCESS BY INDEX ROWID| DEPARTMENTS | 27 | 432 | 2 (0)|
| 3 | INDEX FULL SCAN | DEPT_ID_PK | 27 | | 1 (0)|
|* 4 | SORT JOIN | | 107 | 1177 | 4 (25)|
| 5 | TABLE ACCESS FULL | EMPLOYEES | 107 | 1177 | 3 (0)|
SELECT last_name,department_name FROM employees JOIN departments USING (department_id) WHERE department_id=10;
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|・・・ ------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 27 | 2 (0)|
| 1 | NESTED LOOPS | | 1 | 27 | 2 (0)|
| 2 | TABLE ACCESS BY INDEX ROWID| DEPARTMENTS | 1 | 16 | 1 (0)|
|* 3 | INDEX UNIQUE SCAN | DEPT_ID_PK | 1 | | 0 (0)|
| 4 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 1 | 11 | 1 (0)|
|* 5 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX | 1 | | 0 (0)|
結合方法 EMPLOYEES表への アクセス方法
DEPARTMENTS表への アクセス方法
-
Copyright© 2012, Oracle. All rights reserved.
オプティマイザとは
• オプティマイザ:問合せの結果を生成する最も効率的な方法 (物理的なアクセス手順)を決定し、実行計画を作成する機能
• 索引を利用するか
• 全表スキャンを利用するか
• 複数の表を結合するときに、結合順序/結合方法はどうするかなど
• ルールベースオプティマイザ(RBO)
• あらかじめ定義されたルール、ランキングに基づいて実行計画を生成
• SQL文の書き方のみでアクセスパスが決まり、データの量/特性に依存しない
• Oracle 10g 以降ではサポートされない
• コストベースオプティマイザ(CBO)
• オプティマイザ統計に基づきコストを算出し、最もコストの低い実行計画を生成
• データの量/特性によってアクセスパスが決まる
22
-
Copyright© 2012, Oracle. All rights reserved. 23
ヒストグラムによる索引利用の効率化
-
Copyright© 2012, Oracle. All rights reserved.
最適でないアクセスパスの選択例1
• 10000件のうち5000件がヒットしているにもかかわらず索引スキャン
24
SQL> SELECT * FROM emp WHERE deptno=1;
実行計画 ----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4953 | 2437K| 2378 (1)| 00:00:29 |
| 1 | TABLE ACCESS BY INDEX ROWID| EMP | 4953 | 2437K| 2378 (1)| 00:00:29 |
|* 2 | INDEX RANGE SCAN | DEPT_IDX | 4953 | | 11 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
SQL> SELECT COUNT(*) FROM emp WHERE deptno=1;
COUNT(*)
----------
5000 ⇒10000件のうち5000件がHit
-
Copyright© 2012, Oracle. All rights reserved.
最適でないアクセスパスの選択例2
• 10000件のうち1件しかヒットしてないにもかかわらず全表スキャン
25
SQL> SELECT * FROM emp WHERE deptno>500;
実行計画 --------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 8 | 4120 | 204 (0)| 00:00:03 |
|* 1 | TABLE ACCESS FULL| EMP | 8 | 4120 | 204 (0)| 00:00:03 |
--------------------------------------------------------------------------
SQL> SELECT COUNT(*) FROM emp WHERE deptno>500;
COUNT(*)
----------
1 ⇒10000件のうち1件がHit
-
Copyright© 2012, Oracle. All rights reserved.
EMP表の状態
• データの分布に偏りがある
26
合計:10000件
* deptno= 1~200にデータが分布 特異値としてdeptno=1000を1件
* DEPTNOに対してDEPT_IDX という索引を作成
-
Copyright© 2012, Oracle. All rights reserved.
なぜ最適でないアクセスパスが選択されるのか?
• オプティマイザは、列値は最小値と最大値間に均一に分散していると考える
• 実際の値の分布に偏りがある場合、最適でないアクセスパスを選択する場合がある
27
DEPTNO
10人
人数
DEPTNO
5000人
人数
1 2 3 4 ・・・
・・・
998 999 1000 1 2 3 4 ・・・
・・・
199 200 1000
1000の部門にそれぞれ10人ずつ均等に配属されている
部門番号1の部門に
社員が5000人(全体の50%) 配属されている
全体の1% 全体の50%
索引走査 全表走査
オプティマイザの判断 実際
-
Copyright© 2012, Oracle. All rights reserved.
データの分布に偏りがある場合の解決法
• ヒストグラムを作成することで改善する可能性 • データの分布状況を統計情報として取得できる
• データの実際の分布状況に応じて最適なアクセスパスを選択できる
• 検索対象データ数が少ない場合は索引スキャン
• 検索対象データ数が多い場合は全表スキャン
28
-
Copyright© 2012, Oracle. All rights reserved.
ヒストグラムの種類
• ヒストグラムの種類 • 頻度分布ヒストグラム(Frequency)
• それぞれの値が何行あるか正確に記録できる
• 値の種類数が254以下である場合に作成できる
• 高さ調整ヒストグラム(Hight Balanced)
• 頻度分布ほど正確ではないがデータの偏りを検出できる
29
SQL> exec DBMS_STATS.GATHER_TABLE_STATS (
ownname=>スキーマ名,
tabname=>表名,
estimate_percent =>100(サンプリング率),
method_opt=>'FOR COLUMNS SIZE n deptno',
cascade =>TRUE(索引の情報も取得するか));
FOR ALL INDEXED COLUMNS:すべての索引列について取得
FOR ALL COLUMNS:すべての列について取得
FOR COLUMN 列名:特定の列について取得
SIZE AUTO:ヒストグラム・バケット数を自動設定
SiZE n:ヒストグラム・バケット数を指定
FOR ALL COLUMNS SIZE AUTO
:どの列にヒストグラムが必要かOracleが判別
-
Copyright© 2012, Oracle. All rights reserved.
頻度分布ヒストグラム
• method_opt引数で列の種類数よりも大きな値を指定する
30
-
Copyright© 2012, Oracle. All rights reserved.
頻度分布ヒストグラム
• それぞれの値が何行あるか正確に記録される
31
SQL> SELECT endpoint_value, endpoint_number
2 > FROM user_tab_histograms
3 > WHERE table_name='EMP' and
4 > column_name='DEPTNO’
ENDPOINT_VALUE ENDPOINT_NUMBER
-------------- ---------------
1 5000
2 5025
3 5050
・・・ ・・・ 199 9975 200 9999
1000 10000
1
10
100
1000
10000
1 2 3 4 198 199 200 1000 DEPTNO
件数
・・・
-
Copyright© 2012, Oracle. All rights reserved.
高さ調整ヒストグラム
• method_opt引数で列の種類数よりも小さな値を指定する
32
-
Copyright© 2012, Oracle. All rights reserved.
高さ調整ヒストグラム
• 頻度分布ヒストグラムほど正確ではないが、ヒストグラムなしよりも精度が高い
• 指定したバケット数で分割し、ソートした上で同じ件数ずつバケットに格納
• 各バケットの最後の値(endpoint_value)を記録し、データの偏りを検出するために使う
• 複数バケットのendpoint_valueが等しければ、他の値よりも多く存在する(ポピュラー値)
• 領域使用率向上のためポピュラー値がある場合には、最後のバケット情報のみ記録
33
DEPTNO
件数
SQL> SELECT endpoint_value, endpoint_number
2 > FROM user_tab_histograms
3 > WHERE table_name='EMP'
4 > column_name='DEPTNO’
ENDPOINT_VALUE ENDPOINT_NUMBER
-------------- ---------------
1 12
9 13
25 14
41 15
・・・ ・・・ 169 23
185 24
1000 25 0
0.2
0.4
0.6
0.8
1
0 1 2 3 4 13 14 15 23 24 25
・・・ ・・・
-
Copyright© 2012, Oracle. All rights reserved.
高さ調整ヒストグラムのイメージ
34
DBMS_STATS.GATHER_TABLE_STATS (
ownname =>スキーマ名, tabname => 表名,
estimate_percent => 100,
block_sample => FALSE,
method_opt =>'FOR ALL INDEXED COLUMNS SIZE 4',
cascade =>TRUE);
12件のデータ
( 1が3分の2 ) 1 1 1 1 1 1 1 1 5 12 20 100
全体をつ4のバケットに分割
1 1 1 1 1 1 1 1 5 12 20 100
データの最小値 と 各々のバケットの最大値 を統計情報に加える
-
Copyright© 2012, Oracle. All rights reserved.
高さ調整ヒストグラムのイメージ
35
1 1 1 5 100
ヒストグラムで偏りを調べると 1以下のバケットは2つなので 全体の50%→ 全表走査
SELECT * FROM 表 WHERE 列 = 1;
ヒストグラムを使わない → 索引操作
各々のバケットの最大値
-
Copyright© 2012, Oracle. All rights reserved.
ヒストグラム利用方法
<補足>
• バケット数
• デフォルトのバケット数(75)がほとんどの場合に有効
• よりよい結果を得るために、別の値を試す事も必要
• ヒストグラムを使っても意味がないケース
• where 句で使用されない列
• データが均一に分布している表
• 列が一意で等価検索しかされない
36
• ヒストグラムが存在しない場合
• CBOは値の最小値~最大値の間にデータが均一に分散されていると考える
• ヒストグラムが存在する場合
• CBOはデータの分布状況を見て最適なアクセスパスを選択できる
-
Copyright© 2012, Oracle. All rights reserved.
ヒストグラム統計収集の自動化
• 9i以前のデータベースでは、データ分布が均一でないためにパフォーマンスが劣化している部分についは、手動でヒストグラム情報を取得する必要
• 10gからの自動オプティマイザ統計収集では、ヒストグラム関連の統計情報がデフォルトで取得される
37
-
Copyright© 2012, Oracle. All rights reserved. 38
索引チューニングのポイント
-
Copyright© 2012, Oracle. All rights reserved.
索引がうまく使われない4つのパターン
39
索引を作成したのに検索が遅い時どう考えれば良いか? <4つのチェックポイント>
1. 索引を使用する事で速くなる処理か?
2. 索引を利用できるSQL文か?
3. オプティマイザが索引利用を選択しているか?
4. 索引のメンテナンスをしているか?
-
Copyright© 2012, Oracle. All rights reserved.
1.索引を使用する事で速くなる処理か? 全表走査と索引走査
40
• 表のサイズや選択率(選択行数)によっては全表走査の方が速い可能性
選択率が高い場合には
全表走査が有利
単一行へのアクセスは 索引走査が有利
• 全表走査(フルスキャン)
• 全てのデータを検索、比較する必要がある
• マルチブロック READにより、少ないI/O 回数でデータをみ込むことができる
• 索引走査(インデックススキャン)
• 索引から行アドレス(ROWID)を得て、単一の ブロックを読み込むことができる
全表走査 索引走査
表のサイズ 小 表が大きいほど読み込むデータ量が増加するので、表が大きい場合には向かない
大 大量のデータがあっても読み込むデータは 一部なので、大きい表にも有効
検索対象の割合 多 検索対象の割合が多い場合には、全てのブロックを読み込んだほうが効率的
少 大きな表から一部のデータを読み込む場合に効率的
読み込み方 マルチブロックREAD シングルブロックREAD
-
Copyright© 2012, Oracle. All rights reserved. 41
<参考>索引作成の基準
• 検索対象となる列に対して以下の観点で索引をつけるかどうかを決める
(実際のしきい値はレコード長など各種条件によって異なるので、あくまでも目安として)
レコード件数が1/50より絞り込める場合は索引をつける
レコード数が1/50~1/10の絞り込みの場合は、速度の向上具合と、更新に伴う性能劣化を考慮して決める
レコード数が1/10より絞り込めない場合は索引をつけない
厳密にチューニングするのであれば、全表走査時と索引走査時の タイムを測って、パフォーマンスの良い方を選択する
どれくらいの選択率なら 索引を付けた方がいいの?
-
Copyright© 2012, Oracle. All rights reserved.
1.索引を使用する事で速くなる処理か? 索引の限界
42
• 索引検索でもパフォーマンスが上がらないケース
• 表サイズが大きく、検索対象データも多い場合、索引と表に大量のアクセスが 発生するため、パフォーマンスがあがらない
4月の売上 データを集計したい
4月1日のデータを 検索したい
大量の索引読み込みが行われる検索では、 索引を使ってもDISK I/Oがボトルネックになることも
内部的に 表を分割
1月
2月
3月
4月
5月
6月
7月
8月
4月の売上 データを集計したい マルチブロック
READ
パーティショニング機能で 読み込みを効率化
パーティショニング機能とは
• 単一の表を内部的に複数の 領域に分割して格納する機能
• 表名等は変わらないため、 SQLの変更は不要
-
Copyright© 2012, Oracle. All rights reserved.
2.索引を利用できるSQL文か? 索引が使われないSQL文
43
• SQL文によっては、索引があっても使用されない可能性 • 索引列に対してNULL条件やNOT条件が使用されている場合
• 条件式に計算を含む場合
• LIKE条件を使った中間一致・後方一致検索をする場合
-
Copyright© 2012, Oracle. All rights reserved.
2.索引を利用できるSQL文か? NULL条件やNOT条件が使用されている場合
44
SELECT EFROM EMP WHERE COMM IS NULL ;
SELECT ENAME FROM EMP WHERE DEPTNO = 30 ;
• IS NULLやIS NOT NULL条件を使用した検索例
COMM列に索引がある場合
SELECT ENAME FROM EMP WHERE COMM IS NOT NULL;
• NOT条件を使用した検索例
DEPTNO列に索引がある場合
SELECT ENAME FROM EMP WHERE COMM = 0.1;
SELECT ENAME FROM EMP WHERE DEPTNO != 30 ;
-
Copyright© 2012, Oracle. All rights reserved.
2.索引を利用できるSQL文か? 条件式に計算や関数を含む場合
45
SELECT ENAME FROM EMP WHERE SAL*1.1 > 10000;
SELECT ENAME FROM EMP
WHERE HIREDATE = TO_DATE('010403','DDMMYY');
• 列に計算式を含む検索例
SAL列に索引がある場合
• 列に関数を使用する検索例
HIRE_DATE列に索引がある場合
SELECT ENAME FROM EMP WHERE SAL > 10000/1.1;
SELECT ENAME FROM EMP
WHERE TO_CHAR( HIREDATE,'DDMMYY')='010403’;
-
Copyright© 2012, Oracle. All rights reserved.
2.索引を利用できるSQL文か? LIKE条件を使った検索をする場合
46
SELECT ENAME FROM EMP WHERE ENAME LIKE ‘Su%';
SELECT ENAME FROM EMP WHERE ENAME LIKE '%zu%';
SELECT ENAME FROM EMP WHERE ENAME LIKE '%ki';
• 索引は昇順、降順にデータが並べられているため、前方一致には 索引が使われるが、中間一致・後方一致では索引が使われない
• LIKE条件を使った検索例
SAL列に索引がある場合
-
Copyright© 2012, Oracle. All rights reserved.
3.適切な実行計画が選択されているか? オプティマイザと実行計画
47
• オプティマイザが適切な実行計画を選択していない可能性
• 実行計画を確認する
• 適切な実行方法を指定(ヒント)
1. SYSユーザでPLUSTRACEロールを作成し、SQLを実行するユーザに付与する。
SQL> @%ORACLE_HOME%¥sqlplus¥admin¥plustrce.sql
SQL> GRANT plustrace TO scott;
2. SQLを実行するユーザで実行計画を保存するための表(PLAN_TABLE)を作成する。
SQL> connect scott/tiger
SQL> @%ORACLE_HOME%¥rdbms¥admin¥utlxplan.sql
3. AUTOTRACE 機能を ON にし、SQL文を実行する。
SQL> SET AUTOTRACE ON
SQL> SELECT ...
参考 実行計画の調べ方 (SQL*PlusのAUTOTRACE機能)
SQL文
SQLの実行
オプティマイザが実行計画を生成
ない
オプティマイザが 索引を使うか使わないかの
判断をする
メモリ上に 実行計画があるか
プログラム
ある
-
Copyright© 2012, Oracle. All rights reserved.
3.適切な実行計画が選択されているか? 効率的な実行計画が立てられない場合
48
SELECT name FROM emp WHERE deptno=20 AND job=‘営業’;
EMPには10,000行
DEPTNO列とJOB列には索引がある
DEPTNO列のうちDEPTNO=20の占める割合は30%
JOB列の値のうち営業が占める割合は30%
コストベースオプティマイザの分析
予想されるI/O ⇒1万行×30%×30% = 900行(全体の9 %と判断)
⇒ コストを比較した結果JOB列の 索引走査 を選択
• データの偏りなどにより、コストベース・オプティマイザが最適な実行計画を立てられない場合
実際 : 営業のDEPTNOが20で、営業のほとんど人の DEPTNOが20だった場合 1万行 × 30% = 3000行 (全体の30%)
⇒全表走査のほうが効率的
⇒ ヒントによる チューニング
-
Copyright© 2012, Oracle. All rights reserved.
3.適切な実行計画が選択されているか? ヒントによるチューニング(全表走査)
49
• ヒント:コストベースオプティマイザに、より良い実行方法を指定する手法
• 適切な索引の使用や、結合順序の指定、処理の優先度(スループット重視 or OLTP向き)をSQL文の中で指定
• /*+ と */ の間でヒントを指定し、SQLに直接埋め込む
SELECT name FROM emp WHERE deptno=20 AND job=‘営業’;
実際 : 営業のDEPTNOが20で、営業のほとんど人の DEPTNOが20だった場合 1万行 × 30% = 3000行 (全体の30%) ⇒全表走査のほうが効率的
SELECT /*+ FULL(emp)*/ NAME FROM emp
WHERE deptnp=20 AND job=‘営業’;
全表走査を実行 させる「ヒント」
ヒントを使って全表走査を指定
-
Copyright© 2012, Oracle. All rights reserved.
3.適切な実行計画が選択されているか? ヒントによるチューニング(索引スキャン)
50
<例> sales表の「cust_id_indx」索引を使用させる
SELECT /*+ INDEX(sales cust_id_indx) */
sales_date,sales_amount
FROM sales
WHERE customer_id=100;
• /*+ INDEX(表 索引名) */
• 特定の索引を使用するようオプティマイザに指示する
• 一つの表に複数の索引がある場合、最適な索引を指示することができる
索引を指定する 「ヒント」
-
Copyright© 2012, Oracle. All rights reserved.
3.適切な実行計画が選択されているか? ヒントによるチューニング(高速全索引スキャン)
51
• /*+ INDEX_FFS ( 表 索引… ) */
• 高速全索引スキャンを実行するようオプティマイザに指示する
• 索引の中に求めるデータがすべてあるため、索引をマルチブロックREADで 読み込むことで、必要なデータを取得することができる (表にアクセスする必要がない)
<例> 高速全索引スキャン
CREATE TABLE emp
(empno NUMBER(10) PRIMARY KEY,
name VARCHAR2(30) NOT NULL(※), salary NUMBER(10),
deptno NUMBER(5));
CREATE INDEX name_idx
ON emp(name);
(※)高速全索引スキャンは、索引キー内の列の少なくとも1つの
列にNOT NULL制約がついている必要がある
表アクセス不要
SELECT /*+ INDEX_FFS(emp name_idx) */ name
FROM emp;
nameの一覧の問い合わせ
索引を読めば、NAMEの データが取得できる
(表へのアクセスは不要)
-
Copyright© 2012, Oracle. All rights reserved.
3.適切な実行計画が選択されているか? ヒントを書くときの注意点
• ヒントの指定方法が間違っていた場合、ヒントは無視される • 索引名や表名が間違っていた場合
• 表名に表別名が指定されていた場合
エラー出力はされないため、ヒント指定後は必ず実行計画を確認
• ヒント内で索引が複数指定されている場合は、最もコストが低い索引が 使用される
• ヒントの使用により、実行計画が固定されてしまう • 最適な実行計画は、データの件数、値によって変わってくる可能性があるが ヒントを指定することで実行計画が固定されてしまう
定期的なヒントの見直しが必要
52
<例> SELECT /*+ INDEX(e deptno_index) */
name
FROM emp e WHERE deptno=20;
ヒント内で表名「emp」を 指定すると、ヒントが
無視される
-
Copyright© 2012, Oracle. All rights reserved.
<参考>SQLプロファイルによる実行計画の最適化
53
実行計画がどのように変わるのかを確認
統計の追加により、索引スキャンを選択するように変わり、見積もり時間も大幅
に短くなっている
SQLチューニングアドバイザによるチューニング
-
Copyright© 2012, Oracle. All rights reserved.
4.索引のメンテナンスをしているか? 索引メンテナンスの必要性
• 表のメンテナンスに伴う索引のメンテナンス
• 表データを移動した場合(MOVEコマンド等)
• 索引内のアドレスと行データのアドレスが異なり、索引が使用できない可能性
• 索引の断片化に伴うメンテナンス
• データを追加、削除、変更した場合
• 索引ブロックが断片化し、パフォーマンスが劣化する可能性
• 不要な索引のメンテナンス
• 使われていない索引があることにより、DML文のパフォーマンスに悪影響を 与える可能性
54
索引の定期的なメンテナンスが必要
-
Copyright© 2012, Oracle. All rights reserved.
4.索引のメンテナンスをしているか? 索引断片化の例(データを追加する場合)
55
1~ 51~
1~ 27~
51~ 76~
1 3 5 ・・・ 23 25
27 29 31 ・・・ ・・・ 49
51 53 55 ・・・ ・・・ 75
77 79 81 ・・・ 98 99
1~ 51~
1~ 24~ 27~
51~ 76~
1 3 5 ・・・ 23
24 25
51 53 55 ・・・ ・・・ 75
77 79 81 ・・・ ・・・ 99
27 29 31 ・・・ ・・・ 49
ブロックが 分割される
<例>昇順にデータを追加した場合
• INSERT 98 INSERT 99
<例>ランダムにデータを追加した場合
• INSERT 24
• 昇順にデータを追加するよりもランダムに追加する方が断片化しやすい
最後の ブロックに 追加される
※スライドの図はイメージあり、厳密な索引構造ではありません
-
Copyright© 2012, Oracle. All rights reserved.
4.索引のメンテナンスをしているか? 索引断片化の例(データを変更・削除する場合)
56
51~
27~ 76~
1 3 5 ・・・ 23 25
27 29 31 ・・・ ・・・ 49
51 53 55 ・・・ ・・・ 75
77 79 81 ・・・ ・・・ 99
51~
27~ 76~
<例>データを削除した場合
• DELETE 23,31,53,81
<例>データを変更した場合
• UPDATE 23 ⇒ 24
削除
• データを更新・削除すると、削除済みエントリが残る
• データを削除した場合:論理削除のみが行われる(領域は減らない)
• データを更新した場合:論理削除とデータの追加が行われる(領域が増える)
1 3 5 ・・・ 23
24 25
51 53 55 ・・・ ・・・ 75
77 79 81 ・・・ ・・・ 99
27 29 31 ・・・ ・・・ 49
削除 古いエントリを論理削除し、 新しいエントリを追加
論理削除が
行われる
※スライドの図はイメージあり、厳密な索引構造ではありません
-
Copyright© 2012, Oracle. All rights reserved.
4.索引のメンテナンスをしているか? 索引構造情報の分析方法
57
ANALYZE INDEX 索引名 VALIDATE STRUCTURE ;
• 索引構造の分析
• 索引に含まれるブロック数や高さ
• 索引に含まれている削除済みエントリ数
INDEX_STATS データディクショナリに分析結果を格納
SQL> SELECT blocks,pct_used,lf_rows,del_lf_rows FROM index_stats;
BLOCKS PCT_USED LF_ROWS DEL_LF_ROWS HEIGHT ------ -------- ------- ----------- ------
56 54 4373 949 2
主な項目 BLOCKS 使用ブロック数
PCT_USED 領域の使用率(%)
LF_ROWS 全リーフ行数(削除済み含)
DEL_LF_ROWS 削除されたリーフの行数
HEIGHT Bツリーの高さ
-
Copyright© 2012, Oracle. All rights reserved.
4.索引のメンテナンスをしているか? 索引再構築の目安
• メンテナンスの目安 • 領域の使用比率(PCT_USED)を定期的に監視し、使用率が低下したら 再構築を検討
• 一般的に、下記の数値を超えたタイミングで再構築を検討
• 階層の高さが4階層以上 ( HEIGHT => 4 )
• 削除された行エントリーの占める割合が20~30%を越える場合 ( DEL_LF_ROW/LF_ROWS > 0.2 )
• 表に対する大量データ操作の後、再構築
• 大量のデータを追加・削除した場合 例:夜間に大量のデータをローディングした
• 表のメンテナンスをした場合 例:ALTER TABLE MOVEコマンドなどで表を移動した場合
• ある程度傾向をみて定期バッチで再構築 (データの更新量により、1回/1週間~1回/1ヶ月)
58
-
Copyright© 2012, Oracle. All rights reserved.
4.索引のメンテナンスをしているか? 索引の再構築
59
• 索引の再構築方法
• 索引の再作成(削除して再作成)
•索引のREBUILD
ALTER INDEX 索引名 REBUILD; 索引のREBUILD
• 既存の索引をもとに索引を作成し、新しい索引の作成後、古い索引を削除
• 索引のREBUILDのメリット
• 既存の索引(ソート済みのデータ)をもとに、新しく索引を作成するため、一から索引を作成するよりも高速
• 索引の再構築中も、古い索引を使用してアクセス(SELECT)可能
• オンライン再構築(索引の再構築中にDML操作可能)が可能 (注意:オンライン再構築はEnterprise Editionの機能です)
• 索引のREBILDのデメリット
• 新しい索引を索引してから古い索引を削除するので、一時的に2倍の 領域が必要
-
Copyright© 2012, Oracle. All rights reserved.
4.索引のメンテナンスをしているか? 索引の作成のテクニック
• 索引作成のパラレル化 • 高速に索引を作成(Enterprise Editionの機能)
• 問合せサーバー・プロセスごとに別々の記憶域パラメータが使用
• INITIAL値を5MB、並行度を12で索引を作成する場合は、作成時に60MB以上の記憶域を使用
• 索引作成時のNOLOGGINGの使用 • CREATE INDEX文でNOLOGGINGを指定すると、索引の作成時の
REDOログ生成を最小限におさえる
• REDOログ・ファイルの領域を節約
• 索引の作成に要する時間が削減
• 大規模な索引のパラレル作成のパフォーマンスが向上
60
-
Copyright© 2012, Oracle. All rights reserved.
4.索引のメンテナンスをしているか? 未使用索引の確認と削除
61
ALTER INDEX 索引名 MONITORING USAGE;
ALTER INDEX 索引名 NOMONITORING USAGE;
• 使用されていない索引を特定し、削除する
• 使われていない索引が無駄な領域をとるのを防ぐため
• 更新操作のボトルネックになるため
• 未使用索引の特定方法
この間、索引が使われたかどうかを監視し、 使用状況をV$OBJECT_USAGEビューに格納
未使用索引の監視
SELECT index_name,used FROM V$OBJECT_USAGE;
INDEX_NAME USED
--------------- ---
DEPT_ID_IDX YES
EMP_ID_IDX NO
「USED」列が「NO」の索引は、この期間使用されなかった
-
Copyright© 2012, Oracle. All rights reserved.
4.索引のメンテナンスをしているか? Enterprise Managerを活用した削除候補の確認
62
SQLアクセスアドバイザで索引削除のアドバイスが可能
ワークロードの有効範囲に「完全なワークロード」を選択
アドバイザ結果例
-
Copyright© 2012, Oracle. All rights reserved.
4.索引のメンテナンスをしているか? 索引削除前の影響の確認
• 索引を削除する前に削除によって発生する影響をテストする場合使用禁止索引・不可視索引を使用
• 使用禁止索引
• オプティマイザで無視され、DMLではメンテナンスされない
• 初期化パラメータSKIP_UNUSABLE_INDEXESがTRUE(デフォルト)の場合
• 表に対するDML文は続行されますが、使用禁止索引のメンテナンスは実行されない
• 一意制約を規定する使用禁止索引がある場合は、DML文はエラーで終了
• SKIP_UNUSABLE_INDEXESがFALSEの場合
• DML文はエラーで終了
• SELECT文は使用禁止索引を使用する場合、文はエラーで終了
• 不可視索引
• 不可視索引はDML文中でも維持される
• OPTIMIZER_USE_INVISIBLE_INDEXES初期化パラメータを明示的にTRUEに設定しないと使用されない
• 索引を削除する前に、削除した状態をテスト可能
• アプリケーション全体に影響を与えずに、アプリケーションの特定の操作またはモジュールに対して一時的な索引構造を使用可能
63
バルク・ロードのパフォーマンスを向上させる場合、
オプティマイザによるその索引の使用を停止する場合 にも活用
-
Copyright© 2012, Oracle. All rights reserved.
索引を作成したのに検索が遅い! 索引チューニング 4つのチェックポイントの整理
1. 索引を使用する事で速くなる処理か? • 表のサイズや選択率(選択行数)によっては全表走査の方が速い可能性
• 大量の索引読み込みが行われる検索ではパーティショニングが有効
2. SQLの構文によっては、索引があっても使用されない可能性 • 索引列に対してNULL条件やNOT条件が使用されている場合
• 条件式に計算を含む場合
• LIKE条件を使った中間一致・後方一致検索をする場合
3. オプティマイザが適切な実行計画を選択していない可能性 • 実行計画を確認し、適切な実行方法を指定(ヒント)
4. 表のメンテナンスに伴う索引のメンテナンス • 表データを移動した場合(MOVEコマンド等)
• 索引の断片化に伴うメンテナンス
• 不要な索引のメンテナンス
64
-
Copyright© 2012, Oracle. All rights reserved. 65
様々なタイプの索引
-
Copyright© 2012, Oracle. All rights reserved.
ビットマップ索引とは?
66
• 列値と各レコードがその値に該当するか否かをビットで表した索引
• 検索処理ではビットの有無で条件に該当するか否かが判定される
ROWID 関東 関西 東北
ROWID1 1 0 0
ROWID2 1 0 0
ROWID3 0 0 1
ROWID4 0 1 0
ROWID5 1 0 0
勤務地列のビットマップ索引
番号 名前 勤務地 性別
1 Tanaka 関東 男
2 Suzuki 関東 女
3 Yoshida 東北 男
4 Abe 関西 女
5 Inoue 関東 男
ROWID1
ROWID2
ROWID3
ROWID4
ROWID5
ROWID 男 女
ROWID1 1 0
ROWID2 0 1
ROWID3 1 0
ROWID4 0 1
ROWID5 1 0
性別列のビットマップ索引
社員表
Abe rowid
Baba rowid
・・・・・
Hirota rowid
Inoue rowid ・・・・・
AからL
AからG
MからZ
HからL
A~ 左 M~ 右
Morita rowid Nakano rowid
・・・・・
M~左 S~ 右
A~ 左 H~ 右
MからR
Suzuki rowid Tanaka rowid
・・・・・
SからZ
名前列の Bツリー索引
-
Copyright© 2012, Oracle. All rights reserved.
ビットマップ索引の特徴と作成例
67
• カーディナリティの低い(値の種類が少ない)列に有効 • 地域(東北・関東・中部・関西 など)
• 性別(男・女) 年代(10代・20代・30代 など)
• 複数の列をWHERE条件に指定している場合、各列にビットマップ索引を作成しておくことで、ビット演算による高速な検索を行うことが可能 • 関東に住んでいる20代男性
• 製品Aを買った男性 など
• ビットマップ索引の作成例 • CREATE INDEX文で「BITMAP」を指定
ROWID 関東 関西 東北
ROWID1 1 0 0
ROWID2 1 0 0
ROWID3 0 0 1
ROWID4 0 1 0
ROWID5 1 0 0
ROWID 男 女
ROWID1 1 0
ROWID2 0 1
ROWID3 1 0
ROWID4 0 1
ROWID5 1 0
関西に住んでいる 女性は?
CREATE BITMAP INDEX 索引名 ON 表名(列名);
-
Copyright© 2012, Oracle. All rights reserved.
Bツリー索引とビットマップ索引の比較
68
Bツリー索引 ビットマップ索引
カーディナリティの高い (種類の多い)列に適している
カーディナリティの低い (種類の少ない)列に適している
ORを使用した検索には それほど適していない
OR条件などのビット演算による 高速な検索を行うことが可能
更新のオーバーヘッドが大きい 更新のオーバーヘッドが非常に大きい
OLTP向き DWH向き
索引のサイズ比較的大きい 索引のサイズ比較的小さい
ROWID Tanaka Suzuki Yoshida ・・・
ROWID1 1 0 0 0
ROWID2 0 1 0 0
ROWID3 0 0 1 0
ROWID4 0 0 0 1
・・・ 0 0 0 0
名前列のビットマップ索引
男 rowid 男 rowid
・・・・・
男 rowid 男 rowid
・・・・・
女 rowid
女 rowid
・・・・・
女 rowid
女 rowid
・・・・・
( 男 ) (女)
(女 )
性別列のBツリー索引 × ×
-
Copyright© 2012, Oracle. All rights reserved.
逆キー索引とは?
69
• 索引列のデータをビット単位で反転させ、反転させたデータをソートして 索引に格納する索引
• 索引のキー値を逆にすることにより、挿入値を索引のリーフ・キー全体 に分散させることができる
51~
27~ 76~
1 3 5 ・・・ 23 25
27 29 31 ・・・ ・・・ 49
51 53 55 ・・・ ・・・ 75
77 79 81 ・・・ 98 99
INSERT 96
INSERT 97
INSERT 98
INSERT 99
<通常の索引>
• 連続した値がINSERTされる環境では 特定のブロックにアクセスが集中
51~
27~ 76~
09 19
39 49
59 69
INSERT 90→09
INSERT 91→19
INSERT 93→39
INSERT 94→49
INSERT 95→59
INSERT 96→69
<逆キー索引>
• 値を逆にすることによりアクセスを 分散させることが可能
※スライドの図はイメージあり、厳密な索引構造ではありません
-
Copyright© 2012, Oracle. All rights reserved.
逆キー索引の特徴と作成例
70
• 逆キー索引のメリット
• 索引のキー値を逆にすることにより、挿入値を索引のリーフ・キー全体に 分散させることができるため、特定の索引ブロックにアクセスが集中する ことを防ぐことが可能
• 逆キー索引のデメリット
• 範囲検索( 、betweenなど)に索引を使うことができない
• ビットマップ索引の作成例
• CREATE INDEX文で「REVERSE」を指定
CREATE INDEX 索引名 ON 表名(列名) REVERSE;
-
Copyright© 2012, Oracle. All rights reserved.
複合索引(コンポジット索引)とは?
71
• 複数の列を指定した索引
親コード 分類 種別 製品名
1 A あ XXX
3 B う XXX
・・・ ・・・ ・・・ ・・・
2 A い XXX
3 C あ XXX
・・・ ・・・ ・・・ ・・・
3 C か XXX
2 A さ XXX
・・・ ・・・ ・・・ ・・・
1 B い XXX
2 B え XXX
・・・ ・・・ ・・・ ・・・
• 親コードが1で、分類がAで、種別が「う」の 製品を検索
• 親コードが3で、分類がCの製品を検索
CREATE TABLE 製品表
( 親コード number(30), 分類 varchar2(30), 種別 varchar2(30), 製品名 number(10));
以下のような製品表の索引を検討
以下のような条件で検索を検討
-
Copyright© 2012, Oracle. All rights reserved.
単一列索引と複合索引の作成例
72
親コード 分類 種別 製品名
1 A あ XXX
3 B う XXX
・・・ ・・・ ・・・ ・・・
2 A い XXX
3 C あ XXX
・・・ ・・・ ・・・ ・・・
3 C か XXX
2 A さ XXX
・・・ ・・・ ・・・ ・・・
1 B い XXX
2 B え XXX
・・・ ・・・ ・・・ ・・・
単一列索引 それぞれの列に索引を作成
CREATE INDEX 製品_idx
ON 製品表(親コード,分類,種別);
CREATE INDEX 製品_親_idx ON 製品表(親コード);
CREATE INDEX 製品_分類_idx ON 製品表(分類);
CREATE INDEX 製品_種別_idx ON 製品表(種別);
複合索引 複数列にまとめて一つの索引を作成
-
Copyright© 2012, Oracle. All rights reserved.
複合索引の構造
73
• 複合索引の構造
• 指定した索引列のデータが、指定した列の順にソートされて、リーフ・ブロックに格納される
• 単一列索引を3つ読み、マージするよりも効率的
親コード 分類 種別 1 A あ ROWID
1 A い ROWID
1 A う ROWID
1 B あ ROWID
1 B え ROWID
1 C あ ROWID
2 A あ ROWID
2 A う ROWID
2 B あ ROWID
2 C あ ROWID
2 C い ROWID
3 A い ROWID
・・・ ・・・ ・・・ ・・・
ルート ブロック
ブランチ ブロック
リーフ ブロック
一つ目のキー列を元に Bツリーのツリー構造が構成
親コード 分類 種別 製品名
1 A あ XXX
3 B う XXX
・・・ ・・・ ・・・ ・・・
2 A い XXX
3 C あ XXX
・・・ ・・・ ・・・ ・・・
3 C か XXX
2 A さ XXX
・・・ ・・・ ・・・ ・・・
1 B い XXX
2 B え XXX
・・・ ・・・ ・・・ ・・・
各キー列値ごとに階層的に ソートされて格納されている
-
Copyright© 2012, Oracle. All rights reserved.
複合索引の検索イメージ
74
SELECT * FROM 製品表 WHERE 親コード=1 AND 分類 =‘A’ AND 種別 =‘う’;
親コード 分類 種別
1 A あ ROWID
1 A い ROWID
1 A う ROWID
1 B あ ROWID
1 B え ROWID
1 C あ ROWID
2 A あ ROWID
2 A う ROWID
2 B あ ROWID
2 C あ ROWID
2 C い ROWID
3 A い ROWID
・・・ ・・・ ・・・ ・・・
SELECT * FROM 製品表 WHERE 親コード=1 AND 分類=‘A’; 親コード 分類 種別
1 A あ ROWID
1 A い ROWID
1 A う ROWID
1 B あ ROWID
1 B え ROWID
1 C あ ROWID
2 A あ ROWID
2 A う ROWID
2 B あ ROWID
2 C あ ROWID
2 C い ROWID
3 A い ROWID
・・・ ・・・ ・・・ ・・・
• 複合索引を使った検索例
• 索引を使って、複数の検索条件にあった行を絞り込みが可能
• 索引に含まれる全ての列を指定しなくても、複合索引をつかうことが可能
-
Copyright© 2012, Oracle. All rights reserved.
複合索引を利用できる条件
75
• 複合索引の対応条件とパフォーマンス
SELECT * FROM 製品表 WHERE 親コード=? AND 分類 =? AND 種別 =?;
○:WHERE句に条件あり ×:WHERE句に条件なし
・・・◎
・・・○
・・・△
・・・×
複合索引の使用がパフォーマンス最適
複合索引を使用可能 単一列索引より複合索引のほうが早いケースが多い (列数や各列のサイズなどによって変わる)
9i以降複合索引を使用可能 複合索引より単一列索引のほうが早いケースが多い (パフォーマンスが最適かどうかはデータ構造などによる)
複合索引を使用不可
親コード 分類 種別
○ ○ ○
○ ○ ×
○ × ○
○ × ×
× ○ ○
× ○ ×
× × ○
× × ×
WHRER句の条件にどの列を使うかに よって、索引を使用できるかどうか、 および、パフォーマンスが変わる
-
Copyright© 2012, Oracle. All rights reserved.
キー圧縮型索引
• キー圧縮を使用して、Bツリー索引または索引構成表の主キー列値の一部を圧縮 • キー列の接頭辞が同じ値で繰り返し格納されることを回避し索引に使用される領域が大幅に減少
• 領域が大幅に節約され、各索引ブロックに格納できるキー数が増え、パフォーマンスが向上
• 利用場面 • 一意の複数列索引がある場合
• ROWIDを追加してキーを一意にしている非一意索引がある場合
• キー圧縮の使用 • COMPRESS句を使用
• 接頭辞の長さをキー列の数で指定
CREATE INDEX orders_mod_stat_ix ON orders ( order_mode, order_status)
TABLESPACE users COMPRESS 1;
• ディスク I/O とメモリ消費を削減効果例) • ディスクスペースの節約:
• 索引領域の75%を節約できた実績
• DB全体で30%を削減
• パフォーマンス:
• 高いCPU利用率を I/O 削減により埋め合わせでき、10-20%のスループット向上を実現
76
-
Copyright© 2012, Oracle. All rights reserved.
キー圧縮索引の構造
77
Department1 Job1
Department1 Job1
Department1 Job1
Department1 Job2
Department1 Job2
Department1 Job2
Department2 Job1
Department2 Job1
Department2 Job1
D1J1
D1J1
D1J1
D1J2
D1J2
D1J2
D2J1
D2J1
D2J1
Prefix Suffix Prefix
Suffix
-
Copyright© 2012, Oracle. All rights reserved.
ファンクション索引
• 関数(ファンクション)や式(計算等)を含んだ索引
• 式の処理結果に対して索引を作成したもの
• WHERE句にファンクションや式を含む文を効率化
• Oracle8iから使用可能
• コストベース・オプティマイザ(CBO)のみで利用可能
78
例:データベースの中の文字データが大文字か小文字か分からない場合
SELECT * FROM employees
WHERE UPPER(first_name) = 'RICHARD' ;
CREATE INDEX uppercase_idx
ON employees (UPPER(first_name));
-
Copyright© 2012, Oracle. All rights reserved.
索引構成表とは?
79
• データ全体をBツリー索引に格納している索引
• 索引のキー列だけでなく、その他の列(非キー列)もリーフブロックに格納 している索引
• 索引にアクセスするだけでデータが取得できるため、より高速な検索が可能
① 索引のリーフ・ ブロックから ROWIDを読む
②ROWIDをもとに 表を検索
100
①索引キー列の 検索のみで データまで到達
エントリ・ヘッダ キー列 非キー列
Suzuki 東京 20000 100 ROWID
エントリ・ヘッダ キー列
<通常の索引を使用した検索> <索引構成表を使用した検索>
2段階の処理が必要で余分なI/Oが発生 検索時のI/Oを減少させるので高速
-
Copyright© 2012, Oracle. All rights reserved.
索引構成表作成例
80
CREATE TABLE countries
( emp_id NUMBER(2) PRIMARY KEY,
name VARCHAR2(40),
・・・・
address VARCHAR2(25),
・・・・ )
ORGANIZATION INDEX TABLESPACE indx
PCTTHRESHOLD 20
OVERFLOW TABLESPACE data;
100 Suzuki 東京 20000
行の残りの部分
PCTTHRESHOLD内の行
東京都渋谷区・・・
オーバーフロー領域
• 索引構成表の作成例
• CREATE TABLEで「ORGANIZATION INDEX」を指定
• しきい値(PCTTHRESHOLD)を設定し、一部のデータを別の領域に格納 することもできる
-
Copyright© 2012, Oracle. All rights reserved.
様々なタイプの索引 表の特性、検索条件に合わせて最適な索引を選択
• ビットマップ索引 • 列値と各レコードがその値に該当するか否かをビットで表した索引
• カーディナリティの低い列で、ビット演算による高速な検索を行うことが可能
• 複合索引 • 索引列のデータをビット単位で反転させ、反転させたデータをソートして
索引に格納する索引
• 挿入値を索引のリーフ・キー全体に分散させ、特定の索引ブロックにアクセス が集中することを防ぐことが可能
• 逆キー索引 • 複数の列を指定した索引
• 索引を使って、複数の検索条件にあう行を絞り込みが可能
• 索引構成表 • 索引のキー列だけでなく、その他の列(非キー列)もリーフブロックに格納して
いる索引
• 索引にアクセスするだけでデータが取得できるため、より高速な検索が可能
81
-
Copyright© 2012, Oracle. All rights reserved.
まとめ
• Bツリー索引の構造
• オプティマイザによる索引走査/全表走査の判断 • ヒストグラムによる索引利用の効率化
• 索引チューニングのポイント • 索引を使用する事で速くなる処理か?
• 索引を利用できるSQL文か?
• オプティマイザが索引利用を選択しているか?
• 索引のメンテナンスをしているか?
• さまざまな索引の種類
82
-
Copyright© 2012, Oracle. All rights reserved. 83
Appendix
-
Copyright© 2012, Oracle. All rights reserved.
パーティション表とパーティション索引
• パーティション表にも索引を作成できる • 索引で少数の行を特定することももちろん可能
• だからオンライン・トランザクション処理にも使用できる
• 索引もパーティション化できる • パーティション・キーの概念をもう一度考える
• パーティション索引の種類
• ローカル索引 ローカル同一キー索引
ローカル非同一キー索引
• グローバル索引 グローバル同一キー索引
84
-
Copyright© 2012, Oracle. All rights reserved.
ローカル索引とグローバル索引
85
各パーティションと同じキーで索引 パーティション化
各パーティションとは 別のキーで索引を パーティション化
ローカル索引 グローバル索引 パーティション表
P1
P2
P3
P4
P5
P1
P2
P3
P4
P5
P6
Partition1
Partition2
Partition3
Partition4
Partition5
Partition6
-
Copyright© 2012, Oracle. All rights reserved.
ローカル同一キー索引
• 索引のキー列が表パーティションのキーと同一である索引
86
索引 (orderkey)
Order表
orderkey 1~999
orderkey 1000~1999
orderkey 2000~2999
orderkey 3000~3999
orderkey 800の
データを見たい
パーティションプルーニング 索引にアクセス
orderkey 1000~1999
orderkey 2000~2999
orderkey 3000~3999
orderkey 1~999
-
Copyright© 2012, Oracle. All rights reserved.
ローカル非同一キー索引(1)
• 索引のキー列が、表パーティションのキー列と同一でない索引
87
Order表 orderkey 1~999
orderkey 1000~1999
orderkey 3000~3999
orderkey 2000~2999
索引 (cust_id)
cust_id 1 cust_id 21
・・・
cust_id 7 cust_id 12
・・・
cust_id 21 cust_id 97
・・・
cust_id 2 cust_id 81
・・・
索引にアクセス
Orderkey2000~2999の間の
顧客ID21のデータを見たい パーティションプルーニング
パーティションプルーニングが 使用される例
-
Copyright© 2012, Oracle. All rights reserved.
ローカル非同一キー索引(2)
• 索引のキー列が、表パーティションのキー列と同一でない索引 • 検索条件によっては、パーティションプルーニングが使用されない
• グローバル索引の方がパフォーマンスが高い場合もある
88
索引 (cust_id)
Order表
cust_id 7 cust_id 12
・・・
cust_id 21 cust_id 97
・・・
cust_id 2 cust_id 81
・・・
①索引にアクセス ②索引全体を検索
cust_id 1
cust_id 21
・・・
cust_id 21 cust_id 97
・・・
cust_id 1
cust_id 21
・・・
orderkey 1~999
orderkey 1000~1999
orderkey 3000~3999
orderkey 2000~2999
顧客ID21のデータを見たい
パーティションプルーニングが 使用されない例
-
Copyright© 2012, Oracle. All rights reserved.
ローカル索引の特徴
• パーティション表のメンテナンスに合わせて索引も自動的にメンテナンスされるので、管理負荷が軽減する
• ある表パーティションが使用できなくなっても、対応する検索パーティション以外は影響を受けない
• ある表パーティションのメンテナンス操作にあわせて、 メンテナンスが必要になる索引パーティションが限定されるため、メンテナンス時間が短縮できる
89
-
Copyright© 2012, Oracle. All rights reserved.
グローバル索引
• グローバル同一キー索引 • 表パーティションとは別のキーでパーティション化する索引
• 例:索引をCUST_IDで、表をORDER_DATEでパーティション化
90
※ グローバル非同一キー索引を作成することはできません。(エラーが返されます。)
索引 (cust_id)
Order表 (Order_date)
cust_id
1000 ~ 1999
cust_id
2000以上
cust_id
1 ~ 999
顧客ID2100の
データを見たい
パーティションプルーニング
Q1 Q2 Q3 Q4
-
Copyright© 2012, Oracle. All rights reserved.
グローバル索引
91
レンジパーティション化されたグローバル索引
ハッシュパーティション化されたグローバル索引
CREATE INDEX month_ix ON ORDER(cust_id)
GLOBAL PARTITION BY RANGE(cust_id)
(PARTITION pm1_ix VALUES LESS THAN (1000),
PARTITION pm2_ix VALUES LESS THAN (2000),
PARTITION pm3_ix VALUES LESS THAN (MAXVALUE));
CREATE INDEX month_ix ON ORDER(cust_id)
GLOBAL PARTITION BY HASH(cust_id)
(PARTITION p1 TABLESPACE USERS1,
PARTITION p2 TABLESPACE USERS2,
PARTITION p3 TABLESPACE USERS3,
PARTITION p4 TABLESPACE USERS4);
レンジパーティションの場合、最後のパーティションにMAXVALUEキーワードを指定する
VALUES LESS THANキーワードによって、パーティションに格納されるデータを限定
レンジパーティション化
ハッシュパーティション化
表領域を指定
-
Copyright© 2012, Oracle. All rights reserved.
グローバル索引の特徴
• パーティション化された表に関係なく、自由に索引を パーティション化することが可能
• パーティションメンテナンスの影響を受ける • TRUNCATE PARTITION等を行うと索引が壊れ、該当パーティションは無効(UNUSABLE)になってしまう ⇒ 関連アプリケーション(プロシージャなど)にも影響
• 一部の表パーティションにデータを投入しても索引全体を再作成するため時間がかかる
92
UPDATE GROBAL INDEXES句をつけた場合は
自動更新される索引の再構築も不要
ALTER TABLE T1 DROP PARTITION P1 UPDATE GLOBAL INDEXES;
-
Copyright© 2012, Oracle. All rights reserved. 93