はじめに
CacheやIRISのバージョンを上げるとき、SQL オプティマイザが大幅に変わり、今まで順調に動作していたクエリが、突然遅くなる事がありました。
クエリの数が1,000を超えていたため、1つずつ確認する時間が取れず、クエリプランをテキストに出力して、機械的にチェックを行い難を逃れました。
今回は、その時に使用したクエリプランのテキスト出力をご紹介致します。
EXPLAINの利用(IRIS)
EXPLAIN コマンドを実行することでクエリ実行プランを生成できます。
ターミナルで動作を確認する
では、ターミナルでコマンドの確認をしてみましょう。
※クエリ自体は7行目になります。
:sql
SAMPLE>:sql
SQL Command Line Shell
----------------------------------------------------
The command prefix is currently set to: <<nothing>>.
Enter <command>, 'q' to quit, '?' for help.
[SQL]SAMPLE>>EXPLAIN select name from developer_data.Sample where code='B659'
3. EXPLAIN select name from developer_data.Sample where code='B659'
Plan
"<plans>
<plan>
<sql>
select name from developer_data.Sample where code='B659' /*#OPTIONS {""DynamicSQL"":1} */
</sql>
<cost value=""451.41""/>
Read index map developer_data.Sample.idx, using the given %SQLUPPER(code), and looping on ID.
For each row:
Read master map developer_data.Sample.IDKEY, using the given idkey value.
Output the row.
</plan>
</plans>"
1 Rows(s) Affected
statement prepare time(s)/globals/cmds/disk: 0.0004s/4/141/0ms
execute time(s)/globals/cmds/disk: 0.0275s/1,498/46,443/0ms
cached query class: %sqlcq.SAMPLE.cls7
---------------------------------------------------------------------------
<cost>タグの下にクエリプランが記載されています。
念のため、管理ポータルのクエリプランと比較してみましょう。
クエリプランと相対コストが一致していることが分かります。
では、いよいよテキストへの出力になります。
適当なクラスを作成し、下記関数を作成してみました。
テキストファイルへの出力
簡単ですが、SQLを実行するコマンドとファイルへの出力サンプルになります。
ClassMethod explain(filePath As %String, query As %String)
{
s stm = ##class(%SQL.Statement).%New()
d stm.%Prepare("EXPLAIN "_query)
s tre = stm.%Execute()
s:( tre.%Next() ) txt = tre.%GetData(1)
Open filePath:"NWS"
Use filePath Write txt,!
Close filePath
}
さて、関数実行時に作成されたファイルを確認してみると・・・
いいですね。
ちゃんとプランが出力されています。
後は、不要なタグ等を除去する処理を追加すれば、クエリプランの出力完成です!
ShowPlanの利用(IRIS/Cache)
CacheでもIRISでも両方使える汎用性を求めるのであれば「ShowPlan」を使います。
ClassMethod showPlan(filePath As %String, query As %String)
{
s oldstat=$SYSTEM.SQL.SetSQLStatsJob(3)
s sql=1
s sql(1)=query
d $system.SQL.ShowPlan(.sql,1,1)
d $system.SQL.SetSQLStatsJob(oldstat)
Open filePath:"NWS"
f pos=1:1:%plan Use filePath w %plan(pos),!
Close filePath
}
「ShowPlan」の第2引数に「1」を渡す事で、クエリプランが「%plan」に格納されます。
→「0」だと、そのまま出力されるので、ターミナルで確認する場合は0の方が良いです。
後はテキストに出力、若しくはグローバルに格納すれば良いですね。
手に入ったクエリプランをどう料理するかは、使用者にお任せします。
一先ず、出力結果を確認したいと思います。
<plan>
<sql>
select name from developer_data.Sample where code='B659'
</sql>
<cost value="451.41"/>
<stats>
<ModuleName>MAIN</ModuleName>
<TimeSpent>0.0003373333333333333333</TimeSpent>
<GlobalRefs>3</GlobalRefs>
<CommandsExecuted>215</CommandsExecuted>
<DiskWait>0</DiskWait>
<RowCount>1</RowCount>
<ModuleCount>1</ModuleCount>
<Counter>3</Counter>
</stats>
<stats>
<ModuleName>FIRST</ModuleName>
<TimeSpent>0.000054</TimeSpent>
<GlobalRefs>2</GlobalRefs>
<CommandsExecuted>45</CommandsExecuted>
<DiskWait>0</DiskWait>
<RowCount>0</RowCount>
<ModuleCount>0</ModuleCount>
<Counter>3</Counter>
</stats>
<stats>
<ModuleName>B</ModuleName>
<TimeSpent>0.00004966666666666666667</TimeSpent>
<GlobalRefs>3</GlobalRefs>
<CommandsExecuted>42</CommandsExecuted>
<DiskWait>0</DiskWait>
<RowCount>0</RowCount>
<ModuleCount>1</ModuleCount>
<Counter>3</Counter>
</stats>
Read index map developer_data.Sample.idx, using the given %SQLUPPER(code), and looping on ID.
For each row:
Read master map developer_data.Sample.IDKEY, using the given idkey value.
Output the row.
</plan>
「EXPLAIN」と比較して、圧倒的に情報量が増えていますね。
色々分析をするのであれば、こちらの方が良いかもしれません。
「EXPLAIN」は簡易版といったところでしょうか。
おわりに
以上で、クエリプランの出力方法のご紹介は終わります。
クエリプランを出力する機会が、そうそう訪れる事は無いと思いますが、何かの折に出力する場面がきたら、思い出して頂けると幸いです。