Amazon RDS で拡張メトリックスが発表されたので所感をまとめてみた

この記事は公開されてから半年以上経過しています。情報が古い可能性がありますので、ご注意ください。

4月に(2019.04.16) RDS のメトリックスについて拡張がアナウンスされましたが、私がメトリックスをどのような観点で見ているかについて思いつくままに書き出してみました。参考になれば幸いです。

なお、ここに書き出している見解は個人のものであり所属している組織とは異なります。

主に MySQL データベースを念頭に書き出していきますが、ディスク IO 等は他のデータベースにもおおむね妥当すると思います。

項目は RDS のマニュアル を参考にしています。

それではメトリックスのグループ毎に見ていきたいと思います。

General

ここで性能の観点から注目したいメトリックスは、numVCPUs です。cpuUtilization や loadAverageMinute などのグループを検討するときに参考にする。

cpuUtilization

各メトリックスは一定間隔、公表はされていませんがおそらく 1/100 秒間隔で、CPU の処理内容を確認し、カウントしているものだと見受けられる。

guest

仮想化ハイパーバイザで確認時にゲストOSが稼働していた時間。多分 RDS ではずっと 0 。軽くスルーする。

irq

確認時に割り込み処理が動いていた回数の割合。おもにネットワーク接続数が増えているときやディスクIOが多いときに増加する傾向がある。

system

確認時にシステムコール等カーネル処理が動いていた回数の割合。どのような処理が多いのかは別途分析が必要だが、RDS では難しいと思う。

wait

歴史的な理由から IO ウエィトと呼ばれていますが、実際に待っているわけではないので CPU 性能の観点からは無視します。確認時に実行中の IO があるとカウントされます。別途ディスク IO や、クエリの内容を確認する。

idle

確認時に idle プロセスが動作している場合にカウントします。オンプレならば多いに越したことは無いですが、クラウドだとお金がもったいないのであまりに多い場合はインスタンスサイズを再検討する必要があります。

user

ユーザプロセスが動作していた場合にカウントされます。DBMS 側の処理が多い場合に増加します。パフォーマンスインサイトなどで、クエリの実行状況の詳細を別途確認する必要があります。

total

個別の稼働状況に注意している場合は特に気にしなくてもいいと思う。

steal

ハイパーバイザや同一ハイパーバイザ上の他のゲストによって利用されていた回数です。あまりに多い場合はインスタンスの移動を検討する。

nice

優先制御されたプロセスが実行されていた回数です。RDS では特に何かできるわけではないですが、ユーザーと比較してあまりにも多くかつ通常優先度のプロセスに処理遅延が発生している場合は、優先度の見直しが必要になるかもしれません。

その他

個別 CPU のメトリックスが現在取得できていないため、100 % 以下であっても特定 vCPU を特定スレッド/プロセスを占有していて、そのスレッドに対応するクエリが遅延する可能性があります。

CPU 使用率が 100 % であっても必ずしもより大きいインスタンスに変更することが有効とは限りません。クエリの分割や、テーブルのパーティショニングがより費用対効果が大きい場合があります。あわせて、パフォーマンスインサイト や 後述の tasks の状況を確認する必要があります。 また system の割合が大きい場合は、メモリの使用状況やディスク IO の状況を確認する必要があります。

loadAverageMinute

cpuUtilization や tasks 等他の指標で代替できるので特に気にしない。

なおロードアベレージについては以下のブログエントリが大変参考になりかつ、面白いのでお時間のある時にご一読を。

Linux Load Averages: Solving the Mystery / Brendan Gregg’s Blog

memory

実はこの指標も当てにしていない。 特に DBMS ではテーブルキャッシュやクエリキャッシュの状態が性能に大きな影響を与えると考えているけれど、OS 側からはこれらの状況を把握することはできない。

なので、基本的に使用率 100 % になっていても無視する。

writeback

メモリから書き出した量。あまり急に増えると瞬間的にクエリが遅延する可能性がある。

hugePagesSurp,hugePagesRsvd,hugePagesFree,hugePagesSize

ヒュージページは使っていないはずなので無視する。

cached

レガシーな指標でかつ内容が包括的すぎるので無視する。

free

利用されていないメモリの量。通常少ないと遅延すると思われているがクエリのワークロードによっては、DBMS 側でキャッシュやリザーブされているメモリが利用されるので、OS が把握している空きメモリが少ないからといって必ずしも遅延するとは限らない。

むしろ空きが多い場合はインスタンスサイズの見直しが必要となる。

inactive,active

OS のメモリ管理的には意味があるのだけれどユーザサイドからはあまり意味がないと思っているので、無視しても大丈夫だと思う。

pageTables

64 bit のシステムならばそれほど気にする必要はないと思う。メモリ割り当て管理情報が格納されている領域。

dirty

メモリの領域の内、ディスクに書き戻す必要があるメモリの量。この指標が長期にわたって継続的に増加する場合は、ストレージシステムの見直しが必要になる可能性がある。

mapped

参考になる場合はもあるが得に気にしていない。mmap されている量。

total

仮想マシンへのメモリ割り当て量

slab

カーネルが管理している一時メモリ領域の量。詳細は slabtop 等のコマンドで確認しないと意味がないが RDS ではムリなので、無視する。

buffers

レガシーな指標でかつ包括的で具体性に欠けるので無視する。

tasks

多分私が遅いといわれたときに真っ先に確認する項目がここ。要するに遅いということはプロセスが何らかの待ち状態になっているはずなので、プロセスの状態を確認すると大体の状況が分かる。

sleeping

特に何もしていないプロセスの数。多い場合はメモリが無駄なので、DBMS 側で待機スレッド数を減らすこと検討する。そのうえで他のメトリックスを確認してインスタンスサイズの変更を検討する。

zombie,stopped

多分現在ではほとんど表示されないと思受けれど、定常的に増加する場合はバグの可能性が高いのでバージョンアップをを検討する。

running

実行可能状態のプロセスの数。もしこの値が vCPU の値を上回っていてかつ、クエリに遅延が発生している場合は、より多くの vCPU を搭載したインスタンスへのスケールアップを検討する。またクエリの分割や、インデックスの再編成なども有効

total

現在実行中のプロセスの合計

blocked

IO 待ちのプロセス数。この数が相対的に多く、クエリに遅延が発生している場合は、クエリの分割、メモリ搭載量の多いインスタンスへの変更、PIOPSやストレージ量・タイプの見直しを行う。

swap

出たら負けなので、メモリ搭載量の多いインスタンスへの変更が基本となる。

以下の状態が見受けられる場合は特に注意が必要。

  • free が継続的に減少する。
  • in,out が継続的に観測される。

network

これだけではわかりにくいので、他のメトリックスを参照したほうがいいと思う。 rx,tx は通信量だがあくまでも参考程度とする。セッション数やパケット数が分からないと単に rx,tx が飽和していないという理由だけでネットワークがクエリ遅延の原因ではないと言い切れない。

diskIO,physicalDeviceIO

集計区分が異なるだけで、内容は同じ。ストレージシステムが遅延の原因かどうかは、physicalDeviceIO を用いて、IO 発生の要因を切り分けるためには diskIO を用いる。

writeKbPS,readKbPS

いわゆる転送速度。ただこの値が高いからといって、必ずしも遅延するわけではなくまた低いからといって、遅延しないわけでもないので注意が必要。参考程度に見るのがよいと思う。

もし不足していると判断できる場合は、インスタンスサイズの変更や GP2 タイプのディスクを利用している場合はディスクサイズの増加、PIOPS の利用を検討する。

readIOsPS,writeIOsPS

いわゆる IOPS。おそらくディスクでは一番重要な指標。ディスクの性能では一番最初に飽和する。特に GP2 タイプの EBS では、バーストできる量に制限があるため、例えば DB のダンプを取得したり、大量の初期データを投入する場合には注意が必要。

await

平均待ち時間。ディスクに対して I/O コマンドを発行してから、I/O の完了通知を受け取る時間までの平均値。主にディスク性能の指標になる。

rrqmPS,wrqmPS

システム内部で発生した I/O リクエストはそのまま都度ディスク装置に送られる訳ではなく、I/O スケジューラによってある程度まとめて送られる。

readIOsPS,writeIOsPS とほぼ同じ指標だが、rrqmPS,wrqmPS は I/O 実施前にカウントされるのに対して、readIOsPS,writeIOsPS は I/O 完了後にカウントされるという違いがある。

util

CPU 時間に対して、I/O リクエストが実行中だった割合。数値が高い場合は、I/O 待ちのプロセスが多い可能性が高いが、必ずしも数値が高いからといって遅延しているわけではないので、参考程度

avgQueueLen

平均キュー長。ここでのキューは I/O リクエストのキュー。この値が高い状態が継続すると、処理遅延が発生していると判断する。より大きな IOPS が割り当てられるようにインスタンスサイズやディスクサイズ、PIOPS を割り当てるなどの処置が必要になる。

tps

rrqmPS,wrqmPS の合計。どっちが問題なのかわからないので、rrqmPS,wrqmPSのほうを見るようにする。ただ限界値かどうかを判断するにはこちらが見やすい。

avgReqSz

平均リクエストサイズ。この値が小さいとIOPS だけを消費して、全体としてのI/Oスループットが低下する。

device

デバイス名

writeKb、readKb

読み込み、書き込みしたバイト数。参考程度

fileSys

used

使用しているディスク容量。単位は KB

name

ファイルシステムの名前。通常の RDS 利用では多分表示されない。

usedFiles

ファイル数。たくさんテーブルを作ったりすると。足りなくなる可能性がある。

usedFilePercent

maxFiles に対する usedFiles の割合。

maxFiles

作成可能な最大ファイル数

mountPoint

マウントポイント

total

パーティションの容量

usedPercent

total に対する、used の割合

processList

主要なプロセスのリストと、統計値を得ることができる。

vss

仮想メモリサイズ。単位は KB。

name

プロセスの名前

tgid

スレッドグループID

parentID

親プロセスID

memoryUsedPc

搭載メモリ全体に対する、rss の割合

cpuUsedPc

該当プロセスがCPU 時間を消費した割合。100 % になっているからといって必ずしも CPU 不足とは限らないので注意が必要。またマルチプロセッサシステムでは、値が低いからといって、プロセス遅延が発生しないというわけでもないので、注意が必要。 個人的には貧乏性なので常に 100% がいいと思っている。

id

プロセスの識別子。

rss

物理メモリ使用量。 DBMS ではプロセスの内部的にメモリを管理しているので、100%近くなっていても必ずしも問題になるとは限らない。最近の流行ではできるだけメモリにキャッシュさせて、高速なレスポンスを返すことを期待する傾向なので、むしろ使用量が少なかったらそちらを問題にしたほうがいい。

vmlimit

仮想メモリの上限だと思われるがいずれにせよ、0 とか unlimited になっているので意味が不明

cloudwatchlogs の出力例

最後にcloudwatch logs の出力サンプルを載せておきます。

{
    "engine": "MYSQL",
    "instanceID": "metrixcheck",
    "instanceResourceID": "db-XXXXXXXXXXXXXXXXXXXXXXXXXX",
    "timestamp": "2019-04-16T03:04:46Z",
    "version": 1,
    "uptime": "00:27:10",
    "numVCPUs": 2,
    "cpuUtilization": {
        "guest": 0,
        "irq": 0,
        "system": 0.3,
        "wait": 0.1,
        "idle": 99,
        "user": 0.6,
        "total": 1.1,
        "steal": 0,
        "nice": 0.1
    },
    "loadAverageMinute": {
        "one": 0,
        "five": 0,
        "fifteen": 0
    },
    "memory": {
        "writeback": 0,
        "hugePagesFree": 0,
        "hugePagesRsvd": 0,
        "hugePagesSurp": 0,
        "cached": 1019124,
        "hugePagesSize": 2048,
        "free": 13576744,
        "hugePagesTotal": 0,
        "inactive": 701532,
        "pageTables": 6904,
        "dirty": 336,
        "mapped": 54476,
        "active": 1695496,
        "total": 16134712,
        "slab": 53152,
        "buffers": 29140
    },
    "tasks": {
        "sleeping": 117,
        "zombie": 0,
        "running": 0,
        "stopped": 0,
        "total": 117,
        "blocked": 0
    },
    "swap": {
        "cached": 0,
        "total": 8191996,
        "free": 8191996,
        "in": 0,
        "out": 0
    },
    "network": [
        {
            "interface": "eth0",
            "rx": 877.57,
            "tx": 4294.3
        }
    ],
    "diskIO": [
        {
            "writeKbPS": 0,
            "readIOsPS": 0,
            "await": 0,
            "readKbPS": 0,
            "rrqmPS": 0,
            "util": 0,
            "avgQueueLen": 0,
            "tps": 0,
            "readKb": 0,
            "device": "rdsdev",
            "writeKb": 0,
            "avgReqSz": 0,
            "wrqmPS": 0,
            "writeIOsPS": 0
        },
        {
            "writeKbPS": 33.53,
            "readIOsPS": 0,
            "await": 0.67,
            "readKbPS": 0,
            "rrqmPS": 0,
            "util": 0.12,
            "avgQueueLen": 0,
            "tps": 5.3,
            "readKb": 0,
            "device": "filesystem",
            "writeKb": 2012,
            "avgReqSz": 12.65,
            "wrqmPS": 0,
            "writeIOsPS": 5.3
        }
    ],
    "physicalDeviceIO": [
        {
            "writeKbPS": 0,
            "readIOsPS": 0,
            "await": 0,
            "readKbPS": 0,
            "rrqmPS": 0,
            "util": 0,
            "avgQueueLen": 0,
            "tps": 0,
            "readKb": 0,
            "device": "nvme1n1",
            "writeKb": 0,
            "avgReqSz": 0,
            "wrqmPS": 0,
            "writeIOsPS": 0
        }
    ],
    "fileSys": [
        {
            "used": 672668,
            "name": "",
            "usedFiles": 730,
            "usedFilePercent": 0.06,
            "maxFiles": 1310720,
            "mountPoint": "/rdsdbdata",
            "total": 20496340,
            "usedPercent": 3.28
        },
        {
            "used": 2089420,
            "name": "",
            "usedFiles": 74628,
            "usedFilePercent": 11.39,
            "maxFiles": 655360,
            "mountPoint": "/",
            "total": 10190104,
            "usedPercent": 20.5
        }
    ],
    "processList": [
        {
            "vss": 988528,
            "name": "OS processes",
            "tgid": 0,
            "parentID": 0,
            "memoryUsedPc": 0.27,
            "cpuUsedPc": 0.03,
            "id": 0,
            "rss": 43684,
            "vmlimit": 0
        },
        {
            "vss": 3572528,
            "name": "RDS processes",
            "tgid": 0,
            "parentID": 0,
            "memoryUsedPc": 1.7,
            "cpuUsedPc": 0.53,
            "id": 0,
            "rss": 275092,
            "vmlimit": 0
        },
        {
            "vss": 13072904,
            "name": "mysqld",
            "tgid": 4241,
            "parentID": 4237,
            "memoryUsedPc": 7.12,
            "cpuUsedPc": 0.05,
            "id": 4241,
            "rss": 1148300,
            "vmlimit": "unlimited"
        }
    ]
}