Amazon Auroraを真に理解するための性能検証

今回は、まだ全然底が見えていないAuroraのガチンコ検証となります。公式資料に、発表当初の簡単な検証数値もありますが、自分でやらないと理解できない部分が多くあるためです。

既にAuroraにするだけで従来より速くなる説は有力ですが、なぜ速くなるのか、どのような点に注意を払って運用すべきなのか、といったことを理解するために、より局所的な検証をいくつか行って考察していきたいと思います。



目次

楽しい検証になって長くなりましたので、目次を置いておきます。

  • はじめに
  • クエリのレスポンスタイム
  • クエリキャッシュ
  • CPU利用率とIOPSの性質
  • データ容量とストレージ性能の関係
  • インスタンスタイプとストレージ性能の関係
  • 運用面の色々
  • 何がボトルネックになるか


  • はじめに

    いくつか前提的なものを。

  • ベンチマークは全て、sysbench を使ってテストデータ作成・ランダム参照/更新クエリを実行しています
  • データ容量は Data+Index が 約1GB になるように作成し、テーブルコピーして増加させています
  • クライアントがボトルネックにならないよう、1台vCPU36あたり32接続までとし、それ以上は台数を増やしています
  • 表のデータ条件は一部は倍々で増えて いない ので注意してください
  • 私はAWSの回し者ではないです、善良な一般ユーザーです


  • クエリのレスポンスタイム

    デフォルト設定+1GBのデータがオンメモリの状態で計測した結果です。

    RDSのMultiAZの特徴

    これは前回のデータと同じですが、RDSは単体に比べてMultiAZ構成時は、同期処理のために更新クエリが多めにみて 20ms以上、遅くなると読み取れます。しかし、MultiAZはほぼ必須なのでそれを悲観しても仕方ない、というのを含めおさらいでした。

    AuroraのReadReplica有りの特徴

    まずは軽くReadReplica(=Reader)について復習ですが、Auroraは単体で障害が起きても、フェイルオーバー開始から15分以内に自動復旧するとされています。しかし、Readerが1台あれば、1分以内に復旧するとされています。実際には障害検知の時間もあるでしょうから、もう少し長くなる可能性がありますが、主にインスタンス起動時間の分が短縮されるというわけです。

    また、RDSのMultiAZ用インスタンスと違い、1台目のReaderから参照クエリを実行することができます。

    DBデータの共有方法は、従来のレプリケーションではなく、直接同じストレージを利用しているためコピー処理は発生していません。MASTER(=Writer)との遅延時間も数十ミリ秒とされています。

    ──などを踏まえた上で読み取れることの1つめですが、その仕組み上、Readerの有無にクエリ速度は影響されないことがわかります。もう1つは、AZ間のクエリ速度は単にネットワーク的なRTTが上乗せされるだけ、というのを確認しています。

    そのため、以降の検証では節約というか無意味なので、全てWriter単体でのものとしています。

    RDSとAuroraの比較

    データ保全を前提にしてMultiAZと比較すると、Auroraの更新クエリが速いことがわかります。これだけでは全然判断できませんが、移行メリットの良い匂いが立ち込めてきました。


    クエリキャッシュ

    1GBのデータがオンメモリの状態で、クエリキャッシュサイズを変動させた参照クエリの結果です。

    Auroraのクエリキャッシュは従来と全く別物である、と謳っているだけに、この検証をスルーするわけには参りませぬ。

    RDSの特徴

    従来のMySQL使いにはわりと常識レベルかと思いますが、普通はQueryCacheはOFFにしてしまいます。これは、昨今のアプリケーションのデータ量やクエリ性質から、例えば1GBのQueryCacheSizeをこさえたとしても、キャッシュヒット率が全く上がらずに、キャッシュを保存するオーバーヘッドだけが残るという理由や、高スループットになるとCPUのContextSwitchなどが跳ね上がってボトルネックになるためです。

    で、久々にONにしてみた結果、やはりたいした効果は望めませんでした。

    Auroraの特徴

    ハイきました、なんじゃコレはの一言。公式資料でも改善をアピールしていますが、冗談は事業利益だけにせぇよという感じですね。いったい、どういう仕組みになっているのでしょうか。

    設定ではデフォルトで
     query_cache_type = ON
     query_cache_size = {DBInstanceClassMemory/24}

    となっています。
    DBInstanceClassMemoryの比率はインスタンスタイプ毎に異なるかもしれませんが、
     innodb_buffer_pool_size = {DBInstanceClassMemory*3/4}
    と合わせて確認すると、DBInstanceClassMemory は総メモリの約66% とわかります。

    で、結果として r3.large の Mem:15GB の場合、query_cache_size = 420MB となります。あまり大きくはない値に思えますが、ベンチマーク結果をみるにそれでも十分な効果があります。場合によっては、innodb_buffer_pool_size の次に可能な限り大きく確保しても良さそうな香りすらします。

    Auroraの query_cache_size はパラメータグループを編集するだけで即反映されるので、とりあえずデフォ値で運用を開始し、少しずつ増やしてみるのがよさげです。


    CPU利用率とIOPSの性質

    デフォルト設定+1GBのデータがオンメモリの状態で、接続数を変動させた結果です。

    参照クエリからわかること

    Auroraは並列性能を向上させるために、常に高いCPU利用率となるような特殊な仕組みになっている、という前情報があります。そのため、従来のMySQLのようにQPSに比例するようなCPU変動にならない可能性が高いです。

    検証結果としては見ての通り、CPU利用率が高めに推移し、レスポンスタイムが劣化しつつも、並列数に対応するようにQPSとしては向上しています。CPU/QPS/レスポンスタイム/キャッシュヒット率 あたりがキャパシティ予測に関係しそうですが、従来の感覚で対処しようとすると時期尚早になる場合もありそうです。

    更新クエリからわかること

    CPU/QPS/ResTimeの性質は参照と同様ですので、ここではWriteIOPSに着目します。更新QPSが600~4600に変動しても、WriteIOPSは1770~2000という狭い範囲に留まっています。これはQPSの伸びからすると非常に不自然ですが、公式ページから何か仕込みが入っていることが伺えます。

    従来のデータベースエンジンと違い、Amazon Aurora はストレージレイヤーに更新データベースページをプッシュせず、結果的に I/O 消費を節約しています。


    IOPSについては以降の検証でさらに明らかになるので、ここではIOPSがスループットに全く比例しないことがある、というように認識できればよいかと思います。


    データ容量とストレージ性能の関係

    pool sizeを最小限、QCacheをOFFにしてIOPSを発生しやすく演出し、接続数を固定にして、データ容量の増加による変化を確認します。

    参照の特徴

    容量が大きくなるほどQPSが劣化していくのですが、CPUとReadIOPSが一定を保っています。これだけで断定できるわけではないですが、ひとまず参照のReadIOPSがボトルネックとなっていると仮定しておいてよさそうです。この仮定が正しければ、データ容量が大きくなるほどIOPSを多く必要とするので、I/O waitによってQPSが劣化していくのは当然といえます。

    更新の特徴

    参照と同様、QPSの劣化に対してCPUとIOPSがほぼ横ばいです。が、参照と違って10GB以上からReadIOPS:2600 あたりがボトルネックとなり、容量増加に必要なIOPSが足りずにQPSが劣化しているようです。WriteIOPSは単にQPSが落ちた分だけ減少しています。参照も更新もIOPSがボトルネックと仮定すると、参照と更新でIOPSの限界値が異なっている可能性があると推測できます。

    また、一般的には、ストレージ容量が大きくなるほどIOPSも性能向上する傾向にあるのですが、Auroraでは容量頼りのIOPSキャパシティではなく、意図的な制限がかけられている感触を得られました。64TBまで自動拡張なので、当然といえば当然でしょう。


    インスタンスタイプとストレージ性能の関係

    同上でIOPSを発生しやすく演出し、接続数と容量を固定にして、インスタンスタイプ変更による変化を確認します。

    ここまでIOPS周りでグッとくるものがなく、この検証はダメ押しにもう1本的な感じでやったのですが、いい感じに当たりました。そのため、xlarge, 4xlarge はスルーしてますが、公式ネットワークパフォーマンス的には 中/高/10Gbit と良い区分になっております(汗

    IOPSの考察

    r3.large では完全にイッパイイッパイだった流量でしたが、タイプを上げるだけで ReadIOPS/WriteIOPS の上限が開放され、QPSとResTimeが向上しているのが見て取れます。今回は上位スペックでさらに流量を増やす検証はしなかったので、各スペックでのIOPS上限は不明なままですが、インスタンスタイプとIOPS上限が連動していることがわかりました。もしかしたらスペックを1つ上げる毎に倍々に上限が上がる可能性もあるでしょう。

    CPU利用率の考察

    従来のMySQLならば、スペックを倍にしたらCPU利用率は1/2に近い値になるものでしたが、見ての通り、r3.largeから4倍・16倍と上げてもCPUはさほど下がりません。その背景にはQPSをより多く捌けるようになった部分もありますが、それを差っ引いてもまだ高めという印象の比率です。

    このことからも、やはりCPU利用率は高めに推移する、ということが確認できます。本番運用にてスペックを上げたあとのグラフ感覚で、変な悲観や謎解き検証に走らないよう、認識を改める必要がありそうです。


    運用面の色々

    容量を把握しづらい

    今回のテストデータは sysbench でData+Indexが約1GBになるように作成しました。が、ストレージ利用量をOSで直接確認できないため、SHOW TABLE STATUS や information_schema.tables で確認したのですが、この情報は統計情報チックで正確じゃないので、微調整に少し泣きました。

    一応、監視グラフには、費用となるデータ容量(GB)があるのですが、常に現時点のデータ総容量を表してくれるものではないので、そのグラフだけは欲しいかな、と思いました。

    再起動後にデータがオンメモリ

    RDSとAuroraを同時に再起動して、それぞれテーブル全体をカウントする参照クエリを実行してみると、RDSでは一回目は5秒程度のものが、Auroraではコンマ秒でした。で、公式を確認してみると……

    Aurora では、データベースがシャットダウン後に起動したとき、または障害発生後に再起動したときに、バッファプールキャッシュを “ウォームアップ” します。つまり、Aurora では、メモリ内ページキャッシュに保存された既知の一般的なクエリのページを使用してバッファープールを事前にロードします。これにより、通常のデータベースの使用からバッファープールを “ウォームアップ” する必要性をバイパスすることでパフォーマンスが向上します。

    あれ?俺的事前情報では、再起動してもバッファプールキャッシュが失われない、って感じだったんだけど、キャッシュを作りなおしてから使えるようにするイメージのようです。まぁ、障害時は保てるわけないし、そりゃそうか。とにかくポカポカに暖まってるのは確認できました。

    再起動時の鬼WriteIOPS

    これは再起動した時のWriteIOPSのグラフです。WriteIOPSといえば、通常はせいぜい数千程度までの値ですが、このグラフ値はなんと 7000万 とフリーザ様の最終形態クラスの数値を叩き出しています。



    これを見た瞬間、気になる脇汗課金額はというと……



    フィー…… さすがAmazon、ナイスジョーク!! といったところでしょうか。

    データ容量やパラメータ値を変更しても、必ずこの値が記録されるので、何の処理かは謎です。実害はないのでいいのですが、少なくとも再起動後1時間は現実的な値の変化が見えない無意味なグラフになるので考えものかと思います(カーソルを合わせれば詳細数値は把握できるけども)。


    何がボトルネックになるか

    AWSの中の人とも少し話したのですが、ストレージ容量は64TBまで自動拡張されますし、64TBまでを想定した作りになっているため、基本的にはインスタンスタイプを上げたスケールアップだけで対応できるようになっており、さらに突き詰めると垂直・水平分割をすることになる、ということでした。

    今回の検証で、スケールアップする意味はCPU/メモリ/ネットワーク性能の向上だけではなく、(おそらく)IOPSにも関与していることが判明したので、提供側的にはインスタンスタイプ毎にサービス規模はこのくらいで、必要なリソースはこのくらいだろう、とある程度想定して作られていると見受けられます。そして、その想定値は概ね十分であるという印象です。

    では、いつ、何を基準にスケールアップやスケールアウトの時期を見極めたらよいのか、という課題が残ります。

    CPU利用率を基準にすると、50%では実はまだまだ余力があって、80%を超えてからでも十分かもしれない。IOPS基準にすると、どうやらインスタンスタイプ毎に見えない上限があって認識しづらいし、さらにIOPSの変動がほとんどないのに、よりクエリ数を捌けてしまうという現象にも遭遇するでしょう。

    そこで、今回採取したデータから計画するならば、クエリの平均レスポンスタイム(または平均レイテンシ)が1つの指標にはなりそうです。CPU利用率は高いまま・IOPSは変わらない・でもQPSは伸び続ける、という状況において、おそらくレスポンスタイムが徐々に悪化するであろうことが予想できます。ただ、困ったことにレスポンスタイムはサービスごとに平均値・健全値が異なり、結局はどの数値を超えたらスケールアップすべきかの判断が難しいことになるでしょう。そのため、あくまで無いよりはマシ、程度の1つの指標となります。

    備え付けの監視項目には、各クエリ種ごとのLatencyもありますので、この値の特徴を掴み、ゆくゆくはそれを元にアラート設定することも効果的かもしれません。

    また、ボトルネック以前に、Auroraでの各SHOW STATUS値の特徴の把握や、QueryCache周りを筆頭にパラメータの調整によるチューニングも控えています。


    上手に扱えれば、お値段以上の成果を優に発揮できるポテンシャルを秘めていますので、当分はインターネッツの皆でノウハウを集約していく日々が続くということになるのでしょう。私の分はこれにて以上ということで、相互扶助よろしくお願い申し上げます。