SQLチューニング詳細 作成日:2013年11月10日 作成者:石川 易 難 本課題の目的 データベースシステムの仕組みと動きを理解し、正しいチューニ ング方法を理解する事を目的とする。 本課題では、Oracleを中心に説明しているが、理論や考え方は どのRDMBSでも通用する。 本課題の範囲 本来は、SQLチューニング詳細が目的でしたが、RDBMSの動き を理解せずサイトに書かれているSQLテクニックをただコピーし ているだけの人が多い為、今回はSQLを処理する仕組み(内部 の動き)について説明し、今後説明するSQLチューニングへの 土台作りをしようと思います。 問題 以下の中でチューニング効果が高い順は? (選択) 1.テーブル構造のチューニング 2.SQLのチューニング 3.メモリのチューニング 4.ディスクI/Oのチューニング 5.OSのチューニング 答え チューニング効果と費用効果は、一般的に以下のように言われる。 効果・リスク・コスト 大 テーブル構造のチューニング SQLのチューニング メモリのチューニング ディスクI/Oのチューニング OSのチューニング 効果・リスク・コスト 小 テーブル構造のチューニング① 最も効果があるのは、テーブル構造の変更である。 • テーブルのシンプル化 • データの分割 • データの事前集計 テーブル構造のチューニング② ■テーブルのシンプル化 複数のテーブルを結合する複雑なSQLや副問い合わせを駆使してデータ を検索するSQLは、複雑さの分だけデータベースも解析とデータ取得に時 間が掛かる可能性がある。 ■データの分割 データ件数が増加したり、列(カラム)数が極端に多くなる場合は、テーブ ルを分割する事で効率良くデータを取得できる可能性がある。 ■データの事前集計 データ件数の増加により集計処理が時間が掛かる場合は、バッチ処理な どで事前に集計し、集計結果を検索する事でパフォーマンスが向上する。 テーブル構造のチューニング③ テーブル構造のチューニングは、本番システム稼動後や データ蓄積後に行うとプログラムの改修範囲が多く、テスト工数 増加によるコスト増加や時間が掛かる。 また、完成するまで全体的な効果が実感できないという点も大 きなリスクとなり得る。 ⇒効果が最も大きいが、リスク・コストも大きい。 SQLのチューニング 次に効果があるのは、SQLのチューニングで比較的に短い期 間でパフォーマンスが改善できる。 SQLは同じ結果を求めるSQLであっても書き方により何倍も 違った効果が得られる為、開発現場では、間違った使われ方が 多くチューニングとして最も多く行われる。 ⇒比較的リスクが小さく、大きな効果が得られる。 メモリ・ディスクI/O、OSのチューニング これらのチューニングはアプリケーションの改修を一切行わず アプリケーション全体のパフォーマンスの向上が見込めるが、 実際に実施するまで効果が表れない。 また、これらのチューニングだけで目標を満たす事は稀(何倍 の効果が得られる事はない)ので、前述のチューニングが前提 となるケースが多い。 ⇒リスクは小さいが、効果も小さい。 SQLのチューニングのポイント RDBMSのアーキテクチャと仕組みを説明したいが、それこそ書 籍レベルのボリュームとなる為、ここではポイントだけを記述 します。 • • • • SQLの内部処理 オプティマイザと実行計画 アクセス方法 テーブル結合 SQLの内部処理① SQLを実行するとRDBMSは、以下の流れで処理する。 アプリケーション RDBMS プログラム ①解析 SELECT * FROM TBL_XXX ②実行 ③フェッチ SQLの内部処理② ① 解析 構文チェックや対象テーブルなどの権限チェックを実施。 解析後はライブラリ・キャッシュに格納される。同一のSQLが キャッシュ存在すれば、これらのチェックはキャンセルされる。 つまり、SQLをキャッシュされるように心掛ければ、この処理 は行われない。 ・バインド変数を使ったSQLを使用。 ・SQL記述の統一化。似たようなSQLを多く作らない。 SQLの内部処理② ② 実行 Oracleは、オプティマイザを使って最も高速に処理できる方法 を導き出し、実行計画を作成する。 その後、実行計画をSQLを実行し、データ取得処理を行う。 前述の①解析でキャッシュされていれば、キャッシュされてい る実行計画が使用される。 旨くキャッシュさせる事で、データディクショナリへの再帰SQL 実行がなくなる為、CPU使用率の軽減、メモリ使用量 削減が 実現できる。 SQLの内部処理③ ③ フェッチ SQL結果セットからデータを取得する。 Oracleはデータを取り出す際に最初にデータベース・バッファ・ キャッシュ内を検索し、キャッシュに存在しなければハードディ スクからデータを取り出す。 データベース・バッファ・キャッシュのヒット率を高めるようにす る事で、低速なハードディスクアクセスを減らす事ができる。 SQLの内部処理④ (内部のイメージ図) RDBMS ①解析 SGA 共有プール ②実行 ③フェッチ ライブラリ・キャッシュ データディクショナリ キャッシュ データベース・バッファ・キャッシュ ハードディスク オプティマイザとは? SQL実行時にどのようにSQLを実行するかを考える頭脳。 オプティマイザには「RBO」「CBO」の2種類ある。 種類 RBO (ルールベースオプ ティマイザ) CBO (コストベースオプ ティマイザ) 特徴 メリット デメリット 予め決められたルー ルを元に索引、テー ブル結合処理を実施 して実行計画を立て る データの増減に左右 されずに安定した実 行計画を実施できる データ件数やデータ の偏りにより最適な 実行計画が実施でき ない Oracleの新機能を利 用できない テーブル行数やブ ロック数、値の分布 などのデータ情報 (統計情報)を元に実 行計画を立てる データ件数やデータ の偏りにより最適な 実行計画が実施でき る 統計情報が古いと最 適な実行計画が立て られない ある日、突然パ フォーマンスが悪くな る事もある 質問 CBOを使っていて「ある日、突然パフォーマンスが悪くなる」時っ て、どういうケースで発生するの? 答え 統計情報は、1日1回スケジューラーによって自動収集される。 この統計情報の収集の実施後、極端にデータの分布が変わる ようなSQL実施を行うと、前日まで最適だったSQLが最適でなく なるため、パフォーマンスが悪くなることがある。 ⇒TRUNCATE後やIMPORTによる大量データ投入など 解決方法としては、手動統計収集や常に大量データを集計す るような夜間バッチの場合、ヒント句を使ったRBOを利用するこ とで統計情報に左右されないSQLにする。 実行計画とは? オプティマイザによって導きだされたSQLを実行する為の指示書。 SQL> select seq, text1 from tbl_heavy; 1000000行が選択されました。 実行計画 ---------------------------------------------------------Plan hash value: 3031089632 ------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1063K| 65M| 20950 (1)| 00:04:12 | | 1 | TABLE ACCESS FULL| TBL_HEAVY | 1063K| 65M| 20950 (1)| 00:04:12 | ------------------------------------------------------------------------------- SQL> select seq, text1 from tbl_heavy where seq = 10000; 大部分を取得 するので FULL SCAN create table tbl_heavy ( seq number, text1 varchar(100), text2 varchar(100), text3 varchar(100), text4 varchar(100), text5 varchar(100), text6 varchar(100), text7 varchar(100), text8 varchar(100), text9 varchar(100), text10 varchar(100), primary key( seq ) ); TBL_HEAVY (100万件) 実行計画 ---------------------------------------------------------Plan hash value: 901822958 ------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 65 | 1 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| TBL_HEAVY | 1 | 65 | 1 (0)| 00:00:01 | |* 2 | INDEX UNIQUE SCAN | SYS_C004051 | 1 | | 1 (0)| 00:00:01 | ------------------------------------------------------------------------------------------- 索引が使える から INDEX SCAN アクセス方法① アクセス方法とはオプティマイザが指示するデータの取得方法。 • フルテーブルスキャン • ROWIDアクセス • 索引(インデックス)スキャン – B*Tree索引 – ビットマップ索引 – ファンクション索引 • 全索引スキャン(高速全索引スキャン) アクセス方法② 【フルテーブルスキャン】 テーブルにある全ての行をブロック単位でHWM(最高水位標)まで読み続 ける。 ブロックとはOracleが読み込む最小単位で1ブロック内に複数行レコードが 格納されている。フルスキャンではマルチブロックアクセスが行われるため、 大量のデータアクセスでは最も速い。 但し、大量データを読み込む必要がある時には有効だが、例えデータが1 件しかなくてもHWMの位置までデータブロックを読み続けるので注意が必 要! HWM(High Water Mark)とは (HWMのイメージ) 1行目 テーブル 2行目 データが入っている部分 ・・・ ・・・ ・・・ ・・・ ・・・ ・・・ 以前はデータが入っていたが、 既にデータが削除された部分 ・・・ ・・・ HWM 必ずここまで 読み続ける アクセス方法③ 【ROWIDアクセス】 ROWIDには、データ・ブロック番号とオフセット番号が格納され、アクセス方 法の中で最も速い。 全く同じデータであっても、Export/ImportやデータのDELTE/INSERTで ROWIDが変わってしまうため、検索条件として指定するのはNG。 使用する場合は、必ず行ロックが保証されている条件下でのみ使用するこ と。 ROWIDアクセス (ROWIDのイメージ) ・・・データブロック(通常は1ブロック内に複数レコード存在) AAADatAAEAAAADsAAA 1行目 AAADatAAEAAAADsAAB ROWID・・・ ブロック番号とオフセット番号 =データのアドレス値 2行目 AAADatAAEAAAADsAAC 3行目 AAADatAAEAAAADsAAD ... アクセス方法④ 【索引スキャン】 索引には、ROWIDとキー値のセットが格納されている。 Oracleは該当するキーのROWIDを取得後、ROWIDスキャンでデータを取 得している。 索引スキャンでは該当するキーを取得するまで、シングルブロックアクセス で索引テーブルをスキャンしていくため、ある程度のデータ数を取得する時 は、索引スキャンよりフルスキャンの方が効率が良い場合もある。 アクセス方法⑤ 【全索引スキャン】 索引の値を取得するだけで、実際のデータを取得する事ができる。(索引へ のアクセスだけで完結できる) ただし、問い合わせ時に以下の条件を満たすときのみ実施。 ・必要な列が索引に存在していること。(SELECT句やWHERE句で指定し た列が索引内に存在) ・索引内部の行の10%以上が問い合わせから戻されること。 テーブル結合① データを取得する際、FROM句に2つ以上のテーブルが指定さ れていたとしても、必ず任意の2つのテーブル同士の結合を繰 り返し行っている。 効率よくアクセスし、データを取得する方法として、以下の結合 方法がある。 • ネステッド・ループ結合 • ソート/マージ結合 • ハッシュ結合 テーブル結合② (結合イメージ) select a.id, b.id, c.id from A a, B b, C c where a.id = b.id and a.id = c.id テーブルA テーブルB テーブルC 抽出結果 第一段階 テーブルA テーブルX テーブルB 抽出結果 第二段階 テーブルX テーブルC テーブルY テーブル結合③ 【ネステッド・ループ結合】 結合処理の駆動元テーブル(外部テーブル)から結合先テーブル(内部テー ブル)を順次参照する結合。 ★ポイント ・外部テーブルの選択レコード数が少ないとパフォーマンスが向上。 ・内部テーブルの結合列に索引があるとパフォーマンスが向上。 ・後述する他の結合より、一時表領域の利用が少ない。 テーブル結合④ (ネステッド・ループ結合イメージ) dep_codeが’20’(=OPERATIONS)のemp_codeとdep_nameを抽出。 外部テーブル 内部テーブル tbl_employ emp_code tbl_department dep_code 0001 10 0002 20 0003 30 0004 20 0005 10 dep_code ① ② emp_code dep_code dep_name 10 SALES 20 OPERATIONS 30 ACCOUNTING 40 RESEARCH dep_name 0002 20 OPERATIONS 0004 20 OPERATIONS テーブル結合⑤ 実行計画はこんな感じ SQL> select emp.emp_code, dep.dep_code, dep.name from tbl_employ emp, tbl_department dep where emp.dep_code = dep.dep_code ; 実行計画 ---------------------------------------------------------Plan hash value: 1900569049 ------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 4 | 84 | 2 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| TBL_DEPARTMENT | 1 | 13 | 1 (0)| 00:00:01 | | 2 | NESTED LOOPS | | 4 | 84 | 2 (0)| 00:00:01 | | 3 | INDEX FULL SCAN | IDX_EMPLOY_02 | 4 | 32 | 1 (0)| 00:00:01 | |* 4 | INDEX RANGE SCAN | IDX_DEPARTMENT_01 | 1 | | 0 (0)| 00:00:01 | ------------------------------------------------------------------------------------------------Predicate Information (identified by operation id): --------------------------------------------------4 - access("EMP"."DEP_CODE"="DEP"."DEP_CODE") 統計 ---------------------------------------------------------0 recursive calls 0 db block gets 7 consistent gets 0 physical reads 0 redo size 636 bytes sent via SQL*Net to client 384 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 4 rows processed テーブル結合⑥ 【ソート/マージ結合】 双方のテーブルを結合条件列でソートし、ソートした行をマージしていく結合。 ★ポイント ・条件列に索引があり、事前にその索引で絞り込まれているとソートが不要 となるためにパフォーマンスが向上。 ・ソート/マージ結合のために、ソート処理が発生する場合は効率が悪くな る。 ・ソート済みの表、索引列だけの操作などソート処理が不要な場合で有用。 テーブル結合⑥ (ソート/マージ結合イメージ) tbl_employ emp_code tbl_department dep_code 0001 10 0005 10 0002 20 0004 20 0003 30 dep_code 10 SALES 20 OPERATIONS 30 ACCOUNTING 40 RESEARCH dep_codeでソート済み マージ dep_codeでソート済み dep_code emp_code dep_name dep_name 0001 10 SALES 0005 10 SALES 0002 20 OPERATIONS 0004 20 OPERATIONS 0003 30 ACCOUNTING テーブル結合⑧ 実行計画はこんな感じ SQL> select /*+ USE_MERGE(emp dep) */ emp.emp_code, emp.dep_code, dep.name from tbl_employ emp, tbl_department dep where emp.dep_code = dep.dep_code; 実行計画 ---------------------------------------------------------Plan hash value: 1652441396 -------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 4 | 84 | 4 (25)| 00:00:01 | | 1 | MERGE JOIN | | 4 | 84 | 4 (25)| 00:00:01 | | 2 | TABLE ACCESS BY INDEX ROWID| TBL_DEPARTMENT | 4 | 52 | 2 (0)| 00:00:01 | | 3 | INDEX FULL SCAN | IDX_DEPARTMENT_01 | 4 | | 1 (0)| 00:00:01 | |* 4 | SORT JOIN | | 4 | 32 | 2 (50)| 00:00:01 | | 5 | INDEX FULL SCAN | IDX_EMPLOY_02 | 4 | 32 | 1 (0)| 00:00:01 | -------------------------------------------------------------------------------------------------Predicate Information (identified by operation id): --------------------------------------------------4 - access("EMP"."DEP_CODE"="DEP"."DEP_CODE") filter("EMP"."DEP_CODE"="DEP"."DEP_CODE") 統計 ---------------------------------------------------------0 recursive calls 0 db block gets 5 consistent gets 0 physical reads 0 redo size 636 bytes sent via SQL*Net to client 384 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 1 sorts (memory) 0 sorts (disk) 4 rows processed テーブル結合⑨ 【ハッシュ結合】 メモリ内部にあるテーブルを結合する特殊な結合。 ※CBOを利用しないとオプティマイザに選択されない。 ★ポイント ・結合条件に等価条件(=)が指定され、大量のレコード、表の大部分を結 合する場合に有効。 ・ハッシュテーブルを作る為に有効な索引がないとフルスキャンが発生する。 テーブル結合⑩ (ハッシュ結合イメージ) tbl_employ emp_code tbl_department hash化 dep_code ハッシュ化 0001 0005 0002 0004 0003 10 10 20 20 30 dep_code dep_name ハッシュ関数を実施 10 10 20 20 10 SALES 20 OPERATIONS 30 ACCOUNTING 40 RESEARCH 30 ハッシュ値が一致する 列を抽出 dep_code emp_code dep_name 0001 10 SALES 0005 10 SALES 0002 20 OPERATIONS 0004 20 OPERATIONS 0003 30 ACCOUNTING テーブル結合⑪ 実行計画はこんな感じ SQL> select /*+ USE_HASH(emp, dep) */ emp.emp_code, dep.dep_code, dep.name from tbl_employ emp, tbl_department dep where emp.dep_code = dep.dep_code ; 実行計画 ---------------------------------------------------------Plan hash value: 2532871061 ------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 4 | 84 | 5 (20)| 00:00:01 | |* 1 | HASH JOIN | | 4 | 84 | 5 (20)| 00:00:01 | | 2 | INDEX FULL SCAN | IDX_EMPLOY_02 | 4 | 32 | 1 (0)| 00:00:01 | | 3 | TABLE ACCESS FULL| TBL_DEPARTMENT | 4 | 52 | 3 (0)| 00:00:01 | ------------------------------------------------------------------------------------Predicate Information (identified by operation id): --------------------------------------------------1 - access("EMP"."DEP_CODE"="DEP"."DEP_CODE") 統計 ---------------------------------------------------------1 recursive calls 0 db block gets 9 consistent gets 0 physical reads 0 redo size 636 bytes sent via SQL*Net to client 384 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 4 rows processed まとめ テーブルのアクセス方法、テーブルの結合方法以外にRDBMS独自の技術・ 仕様がありますが、結局のところ、以下の2点をどう解決するかに集約され ます。 ・データブロックへアクセス量を減らす ・キャッシュを有効利用し、ハードディスクへの物理アクセスを減らす SQLによるチューニングのテクニックは、上記2点の理解が前提となるため、 これらの仕組みを正しく理解するようにして下さい。
© Copyright 2024 ExpyDoc