どこまでSQL SELECT文を高速化できるのか?

司会者:本セッションは5月18日開催の「Oracle Code 2017 Tokyo」にて大変人気のあったセッションです。ですが、本日はテック・ナイト用の夏祭りバージョンとして、特別版をしばちょうさんよりお届けしていただきます。

それではしばちょうさん、どうぞよろしくお願いいたします。

柴田長氏(以下、柴田):みなさん、こんにちは。日本オラクルのしばちょう……あ、柴田長です。どうぞよろしくお願いします。

特別版と言われたのですが、そんな余裕がなくてですね。そこで5月に行われた「Oracle Code 2017 Tokyo」で講演したものをもう一度お話したいと思っております。当時より15分も長く時間がありますので、くわしくお話できると思っています。大阪の方、名古屋の方、どうぞよろしくお願いします。

では、さっそくいきましょうか。「Live Challenge!! SQLパフォーマンスの高速化の限界を目指せ!」ということで、その場でデモをしていきます。私は正直、デモが苦手です。この5月の「Oracle Code 2017 Tokyo」で初めて100パーセント成功しました。そんな人間なので、温かい目で見守ってくださいね。

ということで、改めまして「しばちょう」こと、柴田長と申します。

(スライドを指して)5年以上53回というのは、ご存じの方いらっしゃると思いますけれども、「Oracle Technology Network」のほうで……最近は非常にサボリ気味ですけれども、ほぼ毎月連載のほう書かせていただいております。ぜひアクセスしていただいて参考にしてもらえますと幸いでございます。

本日のライブデモの設定シナリオが、こちらになります。

昨今、データ量が増えてきて、アナリティクスやAIといった技術が非常に注目されています。分析ツールなど、実際にデータウェアハウスにアクセスするようになりましたよね。そういったとき、SQLを手で書くことはないですよね。みなさん、ビジネス・インテリジェンスのツールなどで自動的にSQLが生成されるでしょう。そういったSQL、見たことあります? ものすごい行数ですよね。

実際にSQLのそういった性能が出ない場合、みなさんは書き直せるか。書き直したくないですよね。私自身、あまりSQLを書けない人間なので難しいです。

そんな私のような人間のために、今回はSQL文をまったく書き直しません。最初は性能が出ないSQLをそのまま、最後まで一切書き換えずに、Oracle Database 12cの機能を最大限活用してどこまでSQL SELECT文が高速化していくかという限界に、ライブでチャレンジしたいと思っています。

0~29の30種類の値を生成

最初は問題のSQLなので、実行時間が非常にかかりますので、先に流しておきましょう。

バージョンをまず確認しますね。みなさん、見えます? これはマルチテナント環境ですね。「v$version」と打ちますと、12.2.0.1の環境だとご理解いただけるかと思います。

実際にスキーマは、マルチテナント、POCOというPDB(プラガブル・データベース)を用意しています。そちらにログインをした上で、「@query_CPU」というSQL。これを見ただけで74行ぐらいのSQLが実行されているわけですね。みなさん、これを書き換えることなく速くしていきますからね。

ということでスライドに戻りましょう。こちらです。

SHという話をしましたけれども、今回実行しているスキーマはOracle Databaseで提供させていただいているサンプル・スキーマですね。サンプル・スキーマの中のSales Historyのスキーマを利用しています。

みなさんから見て左手にあるのが、ER図です。こちらはマニュアルにも記載がされていますね。こちらのサンプル・スキーマをベースにしています。

これは、そのままだと非常にデータ量が小さいので、真ん中にあるSALES表でデータを増幅しています。増幅の方法は、ちょっとデータ量を増やしたくて、CHAR型のダミー列を2つ追加しています。105文字ですね。

ここは少しおもしろいんですけれども……RPAD関数わかります? CHAR型だから本当はいらないんですけど、あえて入れています。

「mod(CUST_ID,30)」。これについて、なにをしているかわかる方はいらっしゃいます? どうでしょう? MOD関数は余りを計算する関数ですね。そのため、CUST_ID÷30するわけです。そうすると、この「mod(CUST_ID,30)」は何パターンの値ができるのか、0~29の30種類の値が生成されますね。それをrpadで105文字を埋めますよ、と言っているだけです。

同じようなデータがあればあるほど、圧縮効率が高い

なにをしたかったのかというと……。少し脱線しますけれど、こういったパフォーマンスのデモをするときは、ある程度の結果が出るようなデータを用意しないといけません。

今回のシナリオの中では、圧縮機能を使います。Oracleの圧縮機能です。圧縮はやはり同じようなデータがあればあるほど、圧縮効率が高いです。そのため、すべて同じデータにしたら相当圧縮されちゃうんですね。かといって、全部ユニークにすると、まったく圧縮されない。

みなさんに圧縮の効果をお見せしたかったので、バランスのよい30種類の値にしています。答えを言ってしまうとそんな感じです。デモをするときはけっこう苦労するんですよ。微調整が入っています、という話ですね。

このデータをINSERT文をくり返し実行して、約32GBまでこのSALES表を大きくしています。

実際にどうやって大きくしたのかは、先ほどお話ししたしばちょう先生の連載の第53回、最新版のほうに、実際にサンプル・スキーマを作ってこの関数でSALES表を作り直している。そういった例も載せています。ぜひ今日、私のデモを見て「自分もやりたい」と思う方は、その連載53回目を見ていただいて、実際にスキーマを作ってみてくださいね。

今回ご用意しているSQLは、2種類あります。CPUバウンドなクエリとI/Oバウンドなクエリです。CPUバウンドは、CPUをよく使用するSQLですね。SELECT文の意味ですね。I/Oバウンドは、I/Oをよく使うクエリですね。

そういう意味で、CPUバウンドとI/Oバウンドというのを2つ用意しています。それぞれのクエリがどんな機能によって高速化していくのかというのを実際に見ていただきたかったので、2種類用意しています。

実はよく……小さくて見えないんですけど、もともとはBIツールから出力されているのはI/OバウンドなSQLです。こちらのSELECT文がBIツールで自動生成されたものです。これを少し加工して、CPUバウンドにしています。

CPUをよく使わせるためのクエリというと?

みなさん、夏祭りということでまた脱線しますが、CPUをよく使わせるためのクエリというとどんなものを思い浮かべます? どうやったらCPUをよく使わせるようなクエリになると思います?

「関数を使う」などですよね。ですが、「データ量を増やす」というものもあるでしょう。ただ、データ量を増やすとなってしまうと、I/O量も増えちゃいますよね。僕はI/O量を変えずに、CPUだけたくさん使わせるようにしたかったんですね。

そのためになにをしたか。よく直積(ちょくせき)というものがありますよね。SELECTで2つの表をジョインするんですけど、WHERE句はなにも指定しない。

例えば、5行と5行を直積すると5×5で25行になりますよね。レコード数が単純に掛け算になりますね。

その仕組みを使いつつI/Oしている量は、実はI/OバウンドとCPUバウンドと同じ量のレコード数を読んでいます。しかし、CPUバウンドは読んだレコードをメモリ上で5倍くらいにしています。そういったテクニックを使っているので、ぜひCPUバウンドなSQLを見てください。

これも連載53回目に書いてあります。こんなかたちでインパクトのあるSQL文を作っています。……少しネタバレしすぎですかね(笑)。

ということで、ミッションへいきましょう。ミッションは大きく5つ用意しています。1つ目は「処理状況を確認せよ!」です。

ミッションをすべて説明する前にもう1つ進めてしまいたい。ごめんなさいね。

最初に実行したのは、CPUバウンドなSQLを実行しておきました。「少し時間かかるよ」と言ったのが1分45秒ですね。もう1つ、I/OバウンドなSQLも流しておきますね。「@query_IO」と。こちらは70行いかないくらいなんですけど、これもちょっと流しておきましょう。

Mission#1 処理状況を確認せよ!

それでは、本題に戻ります。本日ご用意しているミッションは5つです。まず今、問題となっているSQLが2つありますけれども、これらの処理状況を一緒に確認していきましょう。「どうやって?」をご紹介します。

そのあと「パーティション化で処理量削減を狙え!」です。そして3つ目が「パラレル化で複数CPUコアを使いこなせ!」。4つ目が「データ圧縮で更なるI/O量の削減を狙え!」。5つ目が「インメモリ化で1秒の壁を越えろ!」。

若干もう答えを言ってしまっている感じなんですけども。これを本当に1秒超えられるのかを、みなさんと一緒にこの場で体験していきたいと思っています。

ということで、まず1つ目の「処理状況を確認せよ!」です。

さっそくですけれども、簡単なものはやはり「Enterprise Manager」ですね。Enterprise Managerのほうにアクセスしていただいて、パフォーマンスやSQLモニタリングと、これだけですね。そうすると、直前で実行しているSQLがバババッと一覧に出てきます。

みなさんがこの会場にお越しになる前にいくつかテストをしていました。それをたくさん出ていますけれども、今このセミナーが始まってから実行したのがこの上の2つです。

ステータスがくるくる回っているのは、先ほど直前で実行したSQLが実行中だということですね。これはI/OバウンドなSQLとご紹介しました。その1つ前、こちらがCPUバウンドなSQLでしたね。こちらを開いてみましょう。

CPUバウンドなSQLと、みなさんも覚えておいてください。SQLを今回は書き換えないので、CPUバウンドなSQLと言ったら、SQLIDは「cq2m」で始めるSQLIDになります。これが最後まで変わらないですからね。ちゃんと見ててくださいね。

CPUバウンド、SQLモニタを照会しながらいきますけど。SQLモニタだと……SQL文が切れてしまっているんですけど、ここを押すと全SQLがダーッと見えます。

これは便利そうですね。保存もできるので、実際にアプリケーション側からSQLを実行したタイミングで問題があると、ソースコードからSQL文を取り出すのが面倒ですよね。ここからSQL文を取り出せます。便利です。使ってみてください。

CPUバウンドなSQLの実行時間は1.8分

そして、CPUバウンドなSQLですね。この実行時間は1.8分ですね。1.8分×60秒なので、108秒。そんな感じですね。100秒超えです。先ほどは1分45秒と出てたので、105秒です。誤差ですね。実行時間が100秒超えのSQLになっています。

そのデータベース時間が、105秒という内訳になります。右のバーをクリックしていただくと、CPU=81%と出ています。この1.8分の内訳として、CPUを使っていた時間は1.4分、81%ですよ。

一方で、青いバーにマウスを置いてみるとUSER I/Oで18%です。そのため、私は「CPUをよく使うSQLですよ」と言いました。I/Oより、CPUをよく使っていますよね。だからCPUバウンドなSQLと呼んでいます。

実際にI/Oした量は、これさらに右にいくと、I/Oバイト数31GBと書いてあります。これも非常に便利ですね。

もう少しマニアックな方は、実際にこのI/O31GBがどんなI/Oサイズで行われたのか。平均I/Oサイズ1,022KB、だいたい1MBでI/Oされていることもわかります。そのI/Oの回数が3万1,750回ということで、合計だいたい31GB、32GBくらいのデータ量を読んでいるといった話です。

1MBでI/Oしているのは、これはフルスキャンですよね。フルスキャンでマルチブロックリード。普通なら、データベースブロックは8Kですからね。8KごとにI/Oしていたら埒があかない。1MB単位で表のデータを一気に読んでいく、マルチブロックリードしているというのは、ここらへんの平均I/Oサイズからもピンッとわかってしまう。

「では実際にこの31GBをどのデータ表から読んだの?」は、下にある実行計画のところから見ていきましょう。

ということで、そんな難しくありません。ここに「I/Oバイト数」という列があるんですね。これをピーンと下がっていくと、31GBで読んでいる行があります。

これを左側にずっといくと、SALES表に対してTABLE ACCESS FULLをする。そして31GBを読んでいるんですよ、ということが、ひと目でわかっちゃうんですね。SQLモニタリング、ぜひ使ってください。がんばらなくて済みます。これがCPUバウンドなSQLです。

「CPUを使った時間」「I/Oしてる時間」を一発で見ることができる

(話を)戻しましょう。もう1つ実行していたI/OバウンドなSQLがあります。こちらも見ておきましょう。I/OバウンドなSQLというのは、こちらのSQLIDで覚えてください。「dhz」で始まるSQLIDです。

こちらの実行時間は、1分36秒。だいたい96秒。これは100秒切るぐらいのSQL実行時間だとわかります。

EMを開きます。こちらは1.6なので、先ほどのCPUバウンドよりは実行時間が短いです。私は言いましたね。I/Oバウンドがもとにあって、それのCPUをよく使わせるようにしたので、I/OバウンドよりCPUバウンドが遅いとわかりますよね。そのとおりの結果が出ています。

実際に1.6分の内訳は、先ほどCPUのほうが使用率が8割近くありましたけど、こちらはUser I/Oが8割を占めています。一方で、CPUを使っていた時間は21パーセント。まるで逆ですよね。CPUとI/Oの使う比率がまったく逆という状況になっています。

データ量はどうでしょう? 31GBと、同じですね。こちらは、先ほどから何度もご紹介しているとおり、同じデータを読んでいます。メモリ上で行を複製してCPUをよく使わせるようにしていることは、このあたりからもわかるかと思います。

……失礼しました。少し待ってくださいね。緊張しているんですよね。手元が怪しいです。

こちらも同じようにI/Oバウンドですね。読んでいるデータは同じです。SQLは、基本的には同じです。そのため、SALES表に対するTABLE ACCESS FULLで31GB分を読んでいることがわかります。

ということで、こちらが「処理状況を確認せよ!」です。ぜひリアルタイムSQL監視を使ってみてください。こちらは復習になるので、スライドはパッパと進みますね。

リアルタイムSQL監視、非常に便利そうですよね。こちらを見ていただいたとおり、実際にSQLが実行された経過時間や「そのうちCPUを使った時間は?」「I/Oしてる時間は?」を一発で見ることができます。非常に便利です。

実行計画を各ステップごとで見てみます。例えば、先ほどSALES表をTABLE ACCESS FULLしている31GBというような、どの部分でデータをI/Oしているのか、さらにどの部分どういう待機イベントで待機しているのかを見ることができます。

SQL文に「MONITOR」とヒントを入れて監視対象に

「これはオーバーヘッドがあるんじゃないの?」という話ですけれども、このリアルタイムSQL監視が動くタイミングは、監視方法は次のどちらかの条件を満たす場合です。

デフォルトでは、自動的にSQL監視を開始するのは、SQL文がパラレルで実行されている場合です。そのため、たくさんCPUパワーを使うような処理をするときなので、オーバーヘッドはまったく気にしないぐらいですよね。

あとは、1回の実行で5秒以上CPUまたはI/O時間を消費している場合に、SQL監視がトラッキングされていきます。そのため、非常に実行時間がかかるものが自動的にトラッキングされていくため、あまりオーバーヘッドは気にしないでください。

例えばオンライン・トランザクションのシステムで、1回のSELECT文が例えば0.1秒で終わるなどです。そういったSQLが数千とか数万SQL/秒、1秒間に数千回や数万回ほど実行されるようなデータベースの場合には、そういったSQLを一つひとつを監視してたらものすごいオーバーヘッドになってしまいます。0.1秒とか0.数秒はSQL監視が動きませんから、オーバーヘッドはありませんので気にしないでください。

もちろん明示的に、そういった非常に短いSQLもリアルSQL監視で見たい場合には、明示的にそのSQL文に「MONITOR」とヒントを入れてください。そうすると監視対象になります。

そのほか、SQL監視のアクティブ・レポートに関する例とかFAQは、下に書いてあるURLに便利そうに書いてありますので、ぜひ参考にしてみてください。

ということで、実行時間・現在の状況はわかりました。これを各Oracle Databaseの機能を使って改善していきましょう。Mission#2です。けっこう時間が経ってしまいましたね。がんばります。

Mission#2 パーティション化で処理量削減を狙え!

Mission#2は「パーティション化で処理量削減を狙え!」になっています。

パーティションはみなさんご存じですかね。けっこう有名な機能だと思います。

まず左下の四角のところにある「Table 1」です。少しビデオから消えていますが、「Table 1」ですね。Table 1は、JanuaryからMarchといった言葉で書いてあるとおり、すべてのデータが1つの表に入っています。これが非パーティション表ですね。

これを「Table 2」のほうは、January、February、Marchといったかたちで3つの枠に囲まれていると思います。これは1つ表なんですけれども、内部的に部屋が区切られている。もしくは区切る。これをパーティションと言います。

上のほうにある工場や家みたいな絵は索引を意味しています。しかし、少しわかりづらいので今日は説明しません。

こういったかたちで、1つの表なんだけれども、ルールによって中でデータを区切る。そのルールが、みなさんから見て右側にあるListのパーティションであったり、Rangeのパーティション、Hashのパーティションと、大きく分けて3つの基本的な区分けの方法があります。

左で説明したように、日付でJanuary、February、Marchのレコードで区切るのは、真ん中にあるレンジ・パーティションですね。日付で区切っていく。毎日毎日、新しいデータが入ってくる。そういったものをレンジのかたちで区切っておくと非常に便利ですね。

過去のデータを消すときに、これは便利ですよ。なぜならば、この次のデータだけDROP PARTITIONにすればいいのです。DELETEはいらないですからね。

DELETEするとけっこう時間かかりますよね。しかし、DROP PARTITIONでパーンと過去のデータを消せますから、非常に便利です。

あとはリスト・パーティションです。例えば、これはマニュアルからの絵なんですけれども、New YorkやEast Sales Region、West Sales Region、Central Sales Regionと、地域ごとに入れ物を分けるケースはリスト・パーティション。

さらには、もうあまり入れ物は気にしないで、均等にパーティションが分散されるようにするのが一番右側のハッシュ・パーティション。

これらの3つの方法を組み合わせることもできます。レンジ・リストやレンジ・ハッシュなど、そんなかたちで2つを組み合わせることもできます。パーティションは非常に便利な機能なので、ぜひ学んでください。

パーティション・プルーニングの大きな効果

もう少しいきましょう。パーティション・プルーニングが今回一番大きく期待される効果です。

例えば、真ん中の上にOracle Clientとありますよね。ここからSQL文を投げます。どんなSQL文かというと、「select * from TABLE1」に対して「where COLOR = ‘RED’」で、color列が赤、REDな行だけTABLE1から引っ張りなさいよ、といったようなクエリを投げたとします。

これが左手の非パーティション表の場合は、全データをバーっと並べて、索引があれば索引使う。また、索引を使っても「赤」というレコードがたくさんあった場合には非効率ですから、TABLE ACCESS FULLになったりするわけですね。

こういった場合に、赤のレコードだけじゃなくて、GRAYやYELLOWなど、そこらへんのレコードもすべて読むんですね。読んだ上でDBサーバのCPUで赤のレコードだけフィルタリングするわけです。

I/Oを全部します。DBサーバをCPUを使います。非常に非効率ですよね。これをパーティションを使うことで入れ物を分けておきます。

REDの箱とGRAY、YELLOWというかたちで、「もともとの入れ物にはREDしか入ってませんよ」「GRAYしか入ってませんよ」「YELLOWしか入ってませんよ」というかたちで表の中で分割して格納していく。これがパーティションですね。これはいわゆるリスト・パーティションみたいな感じですね。

これをすると、同じSELECT文を投げた場合に、Oracle Databaseが自動的にこのREDに格納されている領域しか読まなくて済むようになります。ということは、無駄なGRAYやYELLOWを読まなくていいんですね。I/O量が減るのは一目瞭然です。

かつ、持ってきたものから「赤だけ」とフィルタリングする必要ないですよね。赤しか持ってきていないので。そのため、I/Oも減ります、そしてCPUも減ります。そのようなかたちで非常に効果がある。これがパーティション・プルーニングの効果です。

SALES表とパーティション化している表を入れ替える

これを今回のクエリに対してパーティション化したいんだよね、となるんですけれども。どうしますか? SELECT文を見て「どうパーティション切ったら便利かな?」を考えるのは面倒ですよね。

ということで、EMに任せましょう。EMで「アドバイザ」があります。「アドバイザ・ホーム」です。そのアドバイザの中に「SQLアドバイザ」があります。「SQLアドバイザ」の中に「SQLアクセス・アドバイザ」があるんですね。こちらにして、アクセス構造、パーティションのアドバイスを受けたいということです。こちらをセットをします。

現在と最近のSQLのアクティビティを、今、実行しましたよね。SELECT文ですね。これに対してどんなパーティションがいいのかを調べたいので、このままNextします。パーティションなので、推奨するアクセス構造、パーティショニングをアドバイスしてくださいね、というだけですね。あとは次へ次へとやるだけです。

そうするとEnterprise Managerの直近で実行されているSELECT文を分析をして、どの表をどんなパーティションしたらいいのかというアドバイスを出してくれます。ただ、ここはサボります。パワーポイントのほうにします。

こんなかたちでEnterprise Managerでアドバイスしていくんですけれども。結果としては、こんなグラフ画面が出てきます。

左側に赤いバーと青いバーがありますけれども、推奨が出てるんですね。その推奨を適用すると、もともと赤いコストから新規のコスト……要するに、あの推奨を適用するとコストが下がる様子は少し見ればわかります。しかし、これはあまり数字は重要ではありません。とはいえけっこう下がるよね、という感覚を持ってください。

実際に推奨されたアドバイスが、右側に書いてある赤い枠に囲まれているところなんですけれども。少し小さいので大きくしてみましょうか。

アクションとしてはPARTITION_TABLEにしてください。オブジェクトはSALES表です。SALES表はSHスキーマですね、と言っています。

「どこをどんなパーティションにすればいいの?」がここに書いていて、SALES表のTIME_IDをパーティションキーにしてください。そして「PARTITION BY Range」なので、「レンジ・パーティションにしてください」というところまでアドバイスしてくれるんですね。

さらに……このスライドにはないんですけれども、実際にこのパーティション化するためのSQL文も、このEnterprise Managerは作り出しています。それを実行すればパーティション化されます。

こちらは、「ではパーティション化してみましょう」という話です。実行時間を短縮したいので、パーティション表を用意してあります。そのため、パーティション表に切り替えちゃいます。ピッ、はい終わり。

サボりました。レコード量は変わらないですからね。そこは信じてください(笑)。パーティション表を用意していたので、元のSALES表とパーティション化している表を入れ替えました。名前を変えました。許してください。

非パーティション表からパーティション表へ変換する場合は?

そんな感じで実行してみればいいんですけど、順番が……。とりあえず、実行しておきましょうか。

ということで、本当にパーティション化によって効果があるのかを見ていきましょう。「@query_CPU」を取りましょう。SQLをそのまま投げます。少し時間かかるかもしれないので、スライドに戻ります。

もう1つ……少し脱線しますけれども、Oracle Database 12c Release 2から非常に便利な機能が実装されています。非パーティション表からパーティション表へ変換する場合にはどうでしょう。変換中は、みなさんだったらどう変換します? 非パーティション表をパーティション表化するといったら。

パーティション表をCREATE TABLEして、データをINSERT SELECTするとなってしまう。作業中はもとの非パーティション表にアプリケーションからアクセスさせたくないですよね。更新できないですよね。

そのあたりを改善するのが、表のオンライン再定義など前からあったんですけれども。表のオンライン再定義、使ったことがあるは方いますか?

(会場挙手)

けっこう面倒ではないですか。面倒ですよね。プロシージャを何度も叩かないといけなくて面倒ですけど。それが12.2だとalter table文でmodify句が提供されて、これを使うことで非パーティション表からパーティション表へ一発で変換することができると思います。

しかも「online」というキーワードをつけていただくと、このalter table文実行中にもかかわらず、元表のほうに更新することができます。DELETE、UPDATE、INSERT処理も行うことができます。パーティション表になったものにもきちんとそれが反映されているといったかたちです。

そのため、ぜひ12.2を使える方は、alter table、modify partition句を活用してはいかがでしょうか。

ということで、1つ終わりましたね。

I/Oの時間が大幅に減った

CPUバウンドなほうは1分10秒に改善しています。速くなってますよね。1分45秒だったのが、1分10秒です。

もう1つ、query_l/O、I/O側をやっていきましょう。みなさん、EMを見ましょうね。EMに戻っていきます。実際にパーティション化をすることでどのような変化が起きたのか、それを見ていきましょう。

もともとCPUバウンドなSQLは、こちらですね。1.8分かかっていたものが、1.2分まで改善したことは、ここからもわかります。I/Oバウンドはなんと1.6分かかっていたのが20秒まで縮まっていますね。速い。本当ですかね?

CPUから見てみましょう。CPUバウンドはSQLIDが変わっていませんね。実行時間はなんと1.2まで縮んでいます。内訳はどうでしょう。CPU時間、I/O時間。I/O時間が圧倒的に減っているのが見ていただけるかと思います。

さらに右を見ると一目瞭然、I/Oバイト数がなんと31GBから6GBまで減っている。5分の1まで減っている。これが要はI/O時間が大幅に減ったので、SQLの実行時間が短くなったと言えると思います。

もう1つのI/Oバウンドは、20秒で終わってしまいました。「どうなっちゃってるの?」という話ですけれども、でもそのとおりですよ。I/Oバウンドなほうは20秒で終わっています。

この内訳は、I/O時間が非常に削減されて12秒で、CPUが7.3秒。I/Oバイト数も、こちらも31GBから6GBまで下がっている。そのためI/O時間が短くなって、SQLの実行時間が非常に短くなっている。

もともとI/OバウンドですからI/O時間が非常に長いんですね。だから、I/Oの時間が大幅に減ったという効果が、I/Oバウンドなほうが非常に大きいということですね。