web-dev-qa-db-ja.com

一部のクエリでAWS RDSのMySQL 100%CPU

AWSにマネージドMySQL(5.7.19)インスタンスがあります。一般的に、物事はかなりうまくいきます。 CPU使用率は一貫して4%前後で、バースト可能なインスタンス(t2.micro)のIOPS制限をかなり下回っています。ただし、RAMから外れている可能性が高く、ディスク上にある)テーブルのインデックスを使用しないクエリを実行すると、MySQLは1分ほど「ロックアップ」します。 IOPSは増加しますが、通常はクレジットプールに浸るほどではありません。クエリが完了するまで、CPUは100%でスタックします。通常実行中のサービスからの他の接続はキューに入れられます(60以上の接続が表示されます)。そして多くは最終的にタイムアウトします。

これは、データベースをほぼ1分間ロックしたクエリの例です。

SELECT  *
    FROM  mydb.PurchaseDatabase
    WHERE  Time between '2018-11-20 00:00:00' AND '2018-11-23 00:00:00'
      and  ItemStatus=0
      and  ItemID="exampleitem";

このクエリを実行したときのRDSダッシュボードのメトリックは次のとおりです。

RDS dashboard metrics

2回目にクエリを実行すると、ほぼ瞬時に完了します(おそらく、同じテーブルに対する最近のクエリのRAMのようになります)。同様のクエリもまた高速です(0.173秒など)。 )スロークエリロギングとエラーロギングを有効にし、1日後に同じ30秒の遅延でクエリを作成しました(テーブルがページングされているか、RAMから何かが取り出されていました)。ただし、スロークエリテーブルには何も書き込まれませんでした。エラーログ。遅いクエリを実行すると、次のようなメッセージが表示されます。

2018-11-28T06:21:05.498947Z 0 [注] InnoDB:page_cleaner:意図された1000ミリ秒のループには37072ミリ秒かかりました。設定が最適でない可能性があります。 (その間、flushed = 4およびevicted = 0になります。)

これは根本的な問題の別の症状である可能性があると思います。つまり、インスタンスがディスクからの読み取り/書き込みに問題を抱えているということです。 SSDベースのストレージを使用していますが、EBSボリュームのバーストバランスは、これらの遅いクエリの影響を受けません。クエリの前後にたくさんのクレジットがあります。

それから私は、愚かなことに、古い記録を一掃することによってデータベースを助けることを試みることに決めました。私はそのように削除を実行しました:

DELETE FROM mydb.PurchaseDatabase
    WHERE Time between '2018-01-01 00:00:00'
      and '2018-07-31 00:00:00'
      and ItemStatus=0;

これは、190kのテーブル行の約50kに影響しました。このクエリは、0.505秒でMySQL Workbenchに「戻りました」が、実際にはデータベースを約8分間停止しました。この間、RDSインスタンスはログやCloudwatchに書き込むことさえできませんでした。

Cloudwatch fails to get metrics during slow query

約6MB相当のデータベース行を解放するのに8分かかりました(この間、CPUは100%で固定されます)。私は一般的にCPU使用率、およびインスタンスサイズのIOPSをかなり下回っています。 t2.microは本当にこれらのタイプのワークロードを処理することができないのですか?何が起こっているのかをよりよく理解するために私ができることはありますか?パフォーマンスログも書き出そうとしましたが、実際にはこの8分のダウンタイム中に書き込めなかったため、問題を実際に確認することはできませんでした。

このダウンタイムの後、エラーログには次の警告が含まれていました。

2018-11-28T18:35:59.497627Z 0 [警告] InnoDB:長いセマフォ待機:-スレッド47281889883904がsrv0srv.cc行1982で250.00秒間待機しましたセマフォ:RWラッチのXロックが0x2b00a8fcf258で作成されましたファイルdict0dict.cc行1184ライター(スレッドID 47281896187648)がリーダーのモード専用数で予約しました0、ウェイターフラグ1、lock_Word:0最後の読み取りはファイルでロックされましたrow0purge.cc行862最後の書き込みはファイル/ localでロックされました/mysql-5.7.19.R1/storage/innobase/dict/dict0stats.cc 2375行

注:これにより、PurchaseDatabaseテーブルだけでなく、データベース全体がダウンします。接続キューは、最終的にキューがいっぱいになり、それ以上の接続が受け入れられなくなるまで、サービスされていないクエリでいっぱいになります。古い接続は最終的にタイムアウトします。

これはある種のEBS/RDS相互作用であると思いますが、3000 IOPSまでバーストできるはずなのかわかりませんが、30 IOPの読み取りバーストを管理することすらできません。根本的な原因がわからないので、通常のワークロード中にこれらの問題が発生し始めるのではないかと心配しているので、どんな提案も非常にありがたいです。

この例では、次のステートメントを使用してPurchaseDatabaseが作成されます。

CREATE TABLE PurchaseDatabase (
    ID BIGINT UNSIGNED, 
    TitleID VARCHAR(127), 
    TransactionID BIGINT UNSIGNED, 
    SteamID BIGINT UNSIGNED, 
    State TINYINT UNSIGNED, 
    Time DATETIME, 
    TimeCreated DATETIME, 
    ItemID VARCHAR(127), 
    Quantity INT UNSIGNED, 
    Price INT UNSIGNED, 
    Vat INT, 
    ItemStatus TINYINT UNSIGNED, 
    PRIMARY KEY (ID, TitleID)
);

参考までに、ここにこのデータベースのパフォーマンスダッシュボードを示します。これは、予想されるピーク負荷の約50%に相当します(ユーザーが特定のタイムゾーンでプレイするためにログインすると、24時間の強力な昼夜サイクルが見られます)。

MySQL Workbench Performance Dashboard

また、この問題はPurchaseDatabaseテーブルのみが原因ではないことに注意してください。任意のテーブルに対するクエリは、これと同じ問題を引き起こす可能性があります。

ログを見ると、CPUがシステムによって消費されていることがわかります。そのため、mysqldは、メモリ内のバッファプールから提供されていたはずのリクエストを処理するためのCPU時間を取得していない可能性があります。それはチャンスがなかっただけです:

mysqld cpuUsedPc: 0.22
OS processes cpuUsedPc: 507.87
RDS processes cpuUsedPc: 2.03

OSがEBSへの読み書きに追いついているようです。これがその時のディスクIOです:

"diskIO": [ 
   { 
      "writeKbPS": 807.33, 
      "readIOsPS": 25.92, 
      "await": 15.95, 
      "readKbPS": 105.8, 
      "rrqmPS": 0, 
      "util": 17.75, 
      "avgQueueLen": 63.77, 
      "tps": 66.62, 
      "readKb": 6348, 
      "device": "rdsdev", 
      "writeKb": 48440, 
      "avgReqSz": 13.71, 
      "wrqmPS": 0, 
      "writeIOsPS": 40.7 
   } 
],

そのため、IOPSのバースト制限に達していなくても、IOバインドされているようです。これは正しく見えますか?

"avgQueueLen":63.77、-痛い!

5
Giawa

交換でした!結局、データベースを同じハードウェアに複製し、データベース上のライブトラフィックをエミュレートするスクリプトをいくつか作成しました。また、いくつかの大きなクエリを実行してバッファプールをいっぱいにし、レプリカデータベースが運用データベースのメトリックとほぼ一致することを確認しました。次に、それに対して大きなクエリを実行してみましたが、インデックスが適用されていてもロックされました。制作サービスを停止することなく問題を再現できたので、思い通りに壊せるようになりました。

レプリカデータベースの有効期間の早い段階で同じ大きなクエリを実行しても問題は発生しなかったことに気付き、ロックアップが始まるポイントを追跡しました。これは、バッファプールがt2.microインスタンスでスワップするデータ(OSなど)をプッシュするのに十分な大きさになった直後に発生します。以下は、解放可能なメモリが約50MB未満に低下した後にスワップが拡大するCloudwatchの画像です。

Image of swap being utilized as freeable memory drops.

大きなクエリ(インデックスの有無にかかわらず)は、赤い丸の後にデータベースのロックを開始します。 DELETEを実行するためにデータベースを1分間ロックすると、5分間の合計CPU使用率を確認できます。

この理論を念頭に置いて、2つの解決策を試しました。

1)innodb_buffer_pool_sizeの値を375Mに変更しました(デフォルトのAWS値の3/4の代わりに、インスタンスRAM size)。これにより、バッファプールの最大サイズが減少し、データベースがメモリフットプリントはOS/etcをスワップにプッシュするほど大きくなりません。

2)より大きなインスタンス(2GiBのRAM)でデータベースを実行してみました。これもうまくいきました!

これらのソリューションはどちらも機能しますが、(1)の利点は、余分なお金を費やす必要がないことです。私はinnodb_buffer_pool_sizeの値を調整して、スワップを引き起こさずにできるだけ大きくなるように取り組んでいます。 1.2秒で同じDELETEクエリを実行したところ、データベースは応答を続けました。以下のスクリーンショットは、データベースがこれらの長いクエリ中に応答を停止するため、ダッシュボードが更新されず、最終的に接続が失われるため、運用データベースでは不可能でした。

Screenshot of the MySQL dashboard while running a DELETE query

5
Giawa

まず第一に、より良いインデックスを持っています。 SELECTの場合:

INDEX(ItemID, ItemStatus,   -- in either order
      `Time`)               -- then the range

DELETEの場合:

INDEX(ItemStatus, `Time`)   -- in this order

(いいえ、両方のクエリに最適な単一のインデックスはありません。)

これは、パフォーマンス(CPU、IOPS、「ロックアップ」)に役立つはずであり、page_cleaners

テーブルの行数は? (現在14万ですか?)その結果セットでSELECT

もっと...

innodb_file_per_tableはディスクレイアウトの問題です。関係ありません。主な調整パラメータは、RAMのキャッシュです:innodb_buffer_pool_size。ただし、RAMが1GBしかないため、そのキャッシュはかなり小さくなければなりません。これにより、190K行のテーブルにIOPが必要になる理由が説明されます。

これがシナリオです...クエリはbuffer_poolでのみ実行されます。つまり、行の読み取り(または書き込みまたは削除)を行うには、その行が存在する(または存在する)16KBブロックを最初にbuffer_poolに取り込みます。上記の表は約50MBになる場合があります。 (知らないうちに)buffer_pool_sizeが約100MBに設定されていると思います。議論中のSELECTDELETEは全テーブルスキャンを必要としたため、50MBがbuffer_poolにプルされ、必要に応じて他のテーブルのブロックを押し出します。これらのテーブルで大きな問題が発生した場合も同様です。

プランA:推奨インデックスを追加します。キャッシュ可能性を改善するためにディスクのフットプリントを縮小する他の方法があるかもしれません。たとえば、BIGINTは8バイトです。 INTはわずか4バイトですが、数十億の制限があります。ただし、MEDIUMINT(3バイト、数百万)などでも十分です。

プランB:より大きなRDSセットアップを取得します。

2
Rick James

1秒あたりのレート= AWSパフォーマンスグループで検討するRPSの提案

read_rnd_buffer_size=256K  # from 512K reduce RAM/connect & handler_read_rnd_next RPS of 68
innodb_avg_flushing_loops=5  # from 30 to reduce innodb_buffer_pool_pages_dirty of 2,493
innodb_io_capacity=1900  # from 200 to use more of SSD IOPS capacity
thread_cache_size=100  # from 8 to reduce threads_created of 1,082
max_connections=96  # from 66 to reduce connection_errors_max_connections of 1678
innodb_log_buffer_size=24M  # from 8M to support ~8 min logging
innodb_change_buffer_max_size=15  # from 25 percent since only 3% used

ALLは動的変数であり、サービスの停止/開始は必要ありません。

追加の提案については、私のプロファイル、連絡先情報のネットワークプロファイルを表示してください。テーブルKEYを管理してパフォーマンスを向上させた後でも、さらに多くのチューニングの機会があります。

0
Wilson Hauck