前回は AWS RDS サービスの1つである Aurora MySQL(以下、Aurora)のクエリキャッシュが改善したという検証結果を紹介しました。
結果として Aurora と RDS for MySQL を比較すると、Aurora のクエリキャッシュがパフォーマンスに対して有効に機能しているという結果となりました。
今回はその要因について推察して行きたいと思います。
Aurora のクエリキャッシュが高速となった理由
情報取得 performance_schema の設定
MySQLでは、performance_schema でより詳細な MySQL 内部のパフォーマンス統計が取得可能です。
今回のテストでは、performance_schema の以下 instruments を有効化し、クエリキャッシュ関連のパフォーマンスメトリクスを取得しました。
有効化したinsturments
- wait/synch/%Query_cache%
- stage/sql/checking privileges on cached query
- stage/sql/checking query cache for query
- stage/sql/invalidating query cache entries (table)
- stage/sql/invalidating query cache entries (table list)
- stage/sql/storing result in query cache
- stage/sql/Waiting for query cache lock
Aurora 版クエリキャッシュ改変の推察
performance_schema や MySQL ステータス変数の結果を分析したところ、「クエリキャッシュのロック競合」が Aurora では発生しておらず、「クエリキャッシュの断片化」が発生が少ないことがわかりました。
1. クエリキャッシュのロック競合
MySQL のクエリキャッシュは、クエリキャッシュに対するいかなるアクセスでもクエリキャッシュ全体を対象にした mutex ロックが発生します。
そこでロック競合の様子を詳しく知るために、以下2つの sys スキーマのテーブルのメトリクスを収集しました。
- x$host_summary_by_stages :SQL ステートメント実行に要した時間を詳細に確認するため
- x$waits_by_host_by_latency :待機イベントを詳細に確認するため
RDS for MySQL の x$host_summary_by_stages を見ると、「Waiting for query cache lock」のイベントが発生していましたが、Auroraではこのイベントの発生が確認出来ませんでした。
「Waiting for query cache lock」は、文字通りセッションのクエリキャッシュロック取得待ちイベントを示します。
つまり Aurora ではSQLステートメント実行で、クエリキャッシュロック待ちが発生しなかった事がわかります。
上述の点から、待機イベントに関する統計テーブルでもクエリキャッシュロックについて同じ傾向を示すはずです。
x$waits_by_host_by_latency のテーブルでは、クエリキャッシュ全体を対象とした mutex ロックを示す「Query cache structure guard mutex」と、アクセス対象のテーブルが更新中に取得されるRWロックを示す「Query cache query lock」が記録されるように設定されています。
そして実際に確認するとやはり、RDS for MySQL では上述の2つの待機イベントで待機時間が発生していましたが、Aurora では発生していませんでした。
sys スキーマ クエリキャッシュ関連 total_latency
Aurora | RDS for MySQL | |||||||
---|---|---|---|---|---|---|---|---|
Query Cache Size | 1.5GB | 1GB | 500MB | 250MB | 1.5GB | 1GB | 500MB | 250MB |
Waiting for query cache lock(sec) | 0 | 0 | 0 | 0 | 41,414 | 41,820 | 42,165 | 41,584 |
Query_cache_query::lock(ms) | 0 | 0 | 0 | 0 | 1,989 | 2,101 | 2,400 | 3,556 |
Query_cache::structure_guard_mutex(ms) | 0 | 0 | 0 | 0 | 174,094 | 158,382 | 168,506 | 208,317 |
この結果から今回のワークロードでは、Aurora はクエリキャッシュのロック競合が発生しておらず、従来の MySQL に比べてクエリキャッシュのロック競合によるオーバーヘッドの削減に成功していることがわかりました。
2. クエリキャッシュの断片化について
次にクエリキャッシュの断片化について注目して見ていきます。
MySQL のクエリキャッシュの断片化は、クエリキャッシュが結果をキャッシュする仕組み上、発生してしまいます。
キャッシュを格納するブロックについて、MySQL はオンデマンドでシステム変数 query_cache_min_res_unit に指定された値の最小サイズで割り当てを行います。
クエリキャッシュのエントリは可変長であるため、必然的に断片化が発生します。
このクエリキャッシュの断片化については、MySQL のステータス変数として Qcache_free_blocks に注目します。
このステータス変数で、クエリキャッシュ内の空きメモリブロック数が取得出来ます。よって、この値が大きければ大きいほど断片化が多く発生しており、MySQL に与えた query_cache_size のメモリ領域が有効に利用されていないことになります。
前置きが長くなりましたが、以下のグラフを見ると Aurora がクエリキャッシュを有効利用していることが一目瞭然となりました。
Aurora は最大でも Qcache_free_blocks が約200blockとなったのに対して、RDS for MySQL は約11万blockとなっています。
sysbench から発行されているクエリにはランダム性がある事を差し置いても、この違いによって Aurora はクエリキャッシュの断片化が少なくなるよう改善されていると言えるでしょう。
まとめ
二回の記事に分けて Aurora のクエリキャッシュについて、RDS for MySQL と比較して検証を紹介しました。
結果として、Aurora のクエリキャッシュが MySQL と比べて挙動が違うことがわかりました。
そして、Aurora のクエリキャッシュサイズに比例してパフォーマンスの向上が確認できました。
その要因について、クエリキャッシュのロック競合と断片化に注目しました。
RDS for MySQL と比較して見ると、Aurora はクエリキャッシュのロック競合無いことでオーバーヘッドを削減しています。
またクエリキャッシュの断片化という点でも、Aurora は断片化が発生しているブロック数が少なく、クエリキャッシュ領域を有効に利用していると考えられます。
なお、検証実施2017年10月時点での最新版 Aurora での結果となります。
またクエリキャッシュは、ワークロードの傾向などによって効果が大きく異るため、実環境での設定は十分に検証を実施した上で行ってください。
以上となります。ここまで読んで頂きありがとうございました。