MySQLがシリアル同期I / Oを行うのはなぜですか?


8

何度も実行するのに長い時間がかかるMyISAMテーブルで特に厄介なクエリを見ると、MySQLはかなり奇妙なI / Oパターンを公開しているように見えることに気づきました。 I / Oの量(たとえば、テーブルスキャンの場合、または結果としてキャッシュが空にecho 3 > /proc/sys/vm/drop_cachesなるため、最初にインデックスをディスクからロードする必要がある場合)、基礎となるブロックデバイスのキューサイズは値1に近く、パフォーマンスは非常に低いわずか4〜5 MB /秒:

root@mysql-test:~# iostat -xdm 5 /dev/sda
Linux 3.2.0-40-generic (mysql-test)  04/30/2014      _x86_64_        (4 CPU)

Device:         rrqm/s   wrqm/s     r/s     w/s    rMB/s    wMB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.14    24.82   18.26   88.79     0.75     4.61   102.56     2.83   26.39   19.29   27.85   2.46  26.31

Device:         rrqm/s   wrqm/s     r/s     w/s    rMB/s    wMB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.00    69.29  151.52   72.73     5.31     0.59    53.95     1.21    5.39    7.84    0.29   4.39  98.51

Device:         rrqm/s   wrqm/s     r/s     w/s    rMB/s    wMB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.00   153.06  144.29  174.69     4.96     1.36    40.54     1.39    4.36    8.91    0.60   3.15 100.49

Device:         rrqm/s   wrqm/s     r/s     w/s    rMB/s    wMB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.00   105.75  150.92  109.03     4.53     0.85    42.41     1.29    4.96    8.15    0.54   3.90 101.36

Device:         rrqm/s   wrqm/s     r/s     w/s    rMB/s    wMB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.00    48.89  156.36   51.72     5.28     0.76    59.38     1.28    6.16    8.02    0.55   4.77  99.23

150 IOPS は、指定された構成の単一ディスクがランダムI / Oに関して配信できるものにすぎませんが、MySQLが読み取りとフェッチのために非同期I / Oを実行できることを期待しているので、結果は依然として私を本当に驚かせます大量のブロックを1つずつ読み取って評価するのではなく、同時にブロックし、RAID構成で利用できる並列化の効果を事実上無視します。これに責任があるのは、どの設計決定または構成オプションですか?これはプラットフォーム固有の問題ですか?

私はこれを大規模なMyISAMテーブルでテストしましたが、同じテーブルをInnoDBに変換しても同様の効果が見られます(それほど悪くはありませんが、サンプルクエリは20〜30秒かかり、ほとんどの時間がディスクの読み取りに費やされています)キューの長さは1)mysqlデーモンを再起動した後、バッファープールが空です。同じ問題が5.6 GAおよび現在の5.7マイルストーン14でも持続することを確認しました。単一のクエリスレッドを使用している限り、MySQLはクエリ処理に必要なI / O操作を並列化できないようです。


リクエストに応じて、シナリオに関するいくつかの追加の詳細。この動作は、さまざまなクエリタイプで確認できます。私は次のようないくつかのテストを行うために任意に1つ選択しました。

SELECT herp.id, herp.firstname, herp.lastname, derp.label, herp.email, 
(SELECT CONCAT(label, " (", zip_code, " ", city,")" ) FROM subsidiaries WHERE subsidiaries.id=herp.subsidiary_id ) AS subsidiary, 
(SELECT COUNT(fk_herp) from herp_missing_data WHERE fk_herp=herp.id) AS missing_data
FROM herp LEFT JOIN derp ON derp.id=herp.fk_derp
WHERE (herp.fk_pools='123456')  AND herp.city LIKE '%Some City%' AND herp.active='yes' 
ORDER BY herp.id desc LIMIT 0,10;

最適化の余地はあることは承知していますが、いくつかの理由からそれをそのままにして、予期しないI / Oパターンの一般的な説明を見つけることに集中することにしました。

使用されているテーブルには、大量のデータが含まれています。

mysql> select table_name, engine, table_rows, data_length, index_length from information_schema.tables WHERE tables.TABLE_SCHEMA = 'mydb' and tables.table_name in ( 'herp', 'derp', 'missing_data', 'subsidiaries');
+-------------------------+--------+------------+-------------+--------------+
| table_name              | engine | table_rows | data_length | index_length |
+-------------------------+--------+------------+-------------+--------------+
| derp                    | MyISAM |      14085 |     1118676 |       165888 |
| herp                    | MyISAM |     821747 |   828106512 |    568057856 |
| missing_data            | MyISAM |    1220186 |    15862418 |     29238272 |
| subsidiaries            | MyISAM |       1499 |     6490308 |       103424 |
+-------------------------+--------+------------+-------------+--------------+
4 rows in set (0.00 sec)

これらのテーブルに対して上記のクエリを実行すると、システムが1つのスレッドでディスクからデータを読み取るためにビジー状態になっているように見えますが、実行時間が1分を超えています。

サンプルクエリ実行のプロファイル(この例では1分9.17秒かかりました)は次のようになります。

mysql> show profile for query 1;
+--------------------------------+-----------+
| Status                         | Duration  |
+--------------------------------+-----------+
| starting                       |  0.000118 |
| Waiting for query cache lock   |  0.000035 |
| init                           |  0.000033 |
| checking query cache for query |  0.000399 |
| checking permissions           |  0.000077 |
| checking permissions           |  0.000030 |
| checking permissions           |  0.000031 |
| checking permissions           |  0.000035 |
| Opening tables                 |  0.000158 |
| init                           |  0.000294 |
| System lock                    |  0.000056 |
| Waiting for query cache lock   |  0.000032 |
| System lock                    |  0.000116 |
| optimizing                     |  0.000063 |
| statistics                     |  0.001964 |
| preparing                      |  0.000104 |
| Sorting result                 |  0.000033 |
| executing                      |  0.000030 |
| Sending data                   |  2.031349 |
| optimizing                     |  0.000054 |
| statistics                     |  0.000039 |
| preparing                      |  0.000024 |
| executing                      |  0.000013 |
| Sending data                   |  0.000044 |
| optimizing                     |  0.000017 |
| statistics                     |  0.000021 |
| preparing                      |  0.000019 |
| executing                      |  0.000013 |
| Sending data                   | 21.477528 |
| executing                      |  0.000070 |
| Sending data                   |  0.000075 |
| executing                      |  0.000027 |
| Sending data                   | 45.692623 |
| end                            |  0.000076 |
| query end                      |  0.000036 |
| closing tables                 |  0.000109 |
| freeing items                  |  0.000067 |
| Waiting for query cache lock   |  0.000038 |
| freeing items                  |  0.000080 |
| Waiting for query cache lock   |  0.000044 |
| freeing items                  |  0.000037 |
| storing result in query cache  |  0.000033 |
| logging slow query             |  0.000103 |
| cleaning up                    |  0.000073 |
+--------------------------------+-----------+
44 rows in set, 1 warning (0.00 sec)

より詳細に説明できる再現可能な(理想的には単純な)テストケースはありますか?たとえば、この動作を生成するクエリですか?どのような状況で?「echo 3> ...」と「mysqlデーモンを再起動する」から始めましたが、詳細には触れませんでした。
Scott Leadley、2014

@ScottLeadleyご覧いただきありがとうございます。「シンプル」にすることはできないと思います。この問題は、1つのクエリ大量のデータを読み取る必要があり、ほとんどがランダムI / Oである場合にのみ観察できます。テーブルとクエリはかなり簡単です。DDLとクエリのテキストを投稿することはできますが、テーブルやインデックスのデータが数百メガバイトに達しない限り、誰でもすぐに再現できるとは思えません。
syneticon-dj 2014

先ほど触れたように、読み取りを待つ5ミリ秒は、1台の5400 RPMディスクの平均回転待ち時間と一致しています。「大量のデータ...ほぼランダムなI / O」を読み取るときに競合を探してください。RAIDについては、あなたはそれについて述べましたが、この特定の構成の詳細については何も述べていません。
Scott Leadley 2014

私はあなたの設定を実行しないので、私が直接あなたを助けることができるかわかりません。しかし、StackExchangeの経験則では、本当に良い質問は賞金よりも注目されます。完璧な質問を書く
スコットリードリー2014

@ScottLeadleyの5ミリ秒の待機時間は、主に採用されているストレージシステムの遅延によるものです。私はさまざまなシナリオでこれをテストしました-単純な4ディスクRAID10から16ディスクシェルフとSSDバッキングを備えた階層型ストレージファイラーまで、結果は一貫してI / O負荷が並列化されていないため、レイテンシが制限されていることを示しています。これは根本的に間違っていると思います。質問にクエリの詳細を追加しましたが、それらが大きな助けになるとはまだ確信していません。
syneticon-dj 2014

回答:


8

最初に、MyISAMが非同期I / Oを実行しないことを確認して明確にしますが、InnoDBは、MySQL 5.5からデフォルトで実行します。5.5より前は、ワーカースレッドを使用して「シミュレートされたAIO」を使用していました。

3つの状況を区別することも重要だと思います。

  1. 一度に実行される複数のクエリ
  2. 並列実行される単一のクエリ
  3. 次のページがよく知られているテーブルスキャン/クリアの場合の、ある種の論理先読み。

(1)の場合、I / Oはこのために並列で実行できます。MyISAMにはいくつかの制限があります。テーブルのロックとkey_buffer(インデックスキャッシュ)を保護するグローバルロックです。MySQL 5.5以降のInnoDBは、まさにここで輝いています。

(2)の場合、これは現在サポートされていません。良い使用例は、パーティション分割であり、パーティション分割された各テーブルを並行して検索できます。

(3)の場合、InnoDBは> 56ページが読み取られた場合(これは設定可能です)、全範囲(64ページのグループ)を読み取るための線形先読みを備えていますが、さらに拡張する余地があります。Facebookはブランチに論理リードヘッド実装することについて書いています(テーブルスキャンのパフォーマンスが10倍向上)。


おかげで、これは私が見ているもののいくつかの追加の理解を私に与えます。これは一般的に、MyISAMがシングルスレッドのロードに複数のディスクに相当するIOPSを利用できないことを意味しますか?ドキュメントでこれに関する参照を見つけることができません-何か便利なものがありますか?
syneticon-dj 2014

はい。これがどこにあるのか、ドキュメント内の場所を考えることはできません。
Morgan Tocker 2014年

2

missing_data空のMyISAMテーブルには通常1024バイトがあるため、MyISAMではないことを望みます.MYI。MyISAMにはゼロ以外のバイトサイズが必要です。ゼロバイト.MYIは少し気味が悪いように聞こえます。

このメタデータクエリを実行する場合

select table_name, table_rows, data_length, index_length, engine
from information_schema.tables
WHERE tables.TABLE_SCHEMA = 'mydb'
and tables.table_name = 'missing_data';

そのテーブルのエンジンはMyISAMであり、修復する必要があります。

SIDE注:場合engineNULL、それはビューです。ビューまたはMyISAMでない場合は、残りの投稿を無視して、その情報を質問に追加してください。テーブルがMyISAMの場合は、次を読んでください...

メタデータクエリによると、missing_data.MYD約46Mです。

まず、これを実行します

SHOW CREATE TABLE mydb.missing_data\G

テーブルの説明か、次のようなエラーメッセージが表示されます。

ERROR 126 (HY000): Incorrect key file for table ...

テーブルの説明を取得し、それがMyISAMである場合は、実行してください

OPTIMIZE TABLE mydb.missing_data;

断片化なしでテーブルを再作成し、新しいインデックス統計を計算します。それが機能しない場合は、以下を試してください。

REPAIR TABLE mydb.missing_data;

MyISAMのインデックスページが再生成されます。

安全のために(MySQL 5.6を使用している場合)、修復後にこれを実行します

FLUSH TABLES mydb.missing_data;

あなたの質問

MySQL Query Optimizerが使用しないことを決定した場合、テーブルのインデックスがメモリにロードされない可能性があります。WHERE句で、大量の行をインデックスから読み取る必要があると指示された場合、MySQL Query Optimizerは、EXPLAINプランを構築するときに、代わりに全テーブルスキャンを使用することを決定します。

MyISAMテーブルは構成できないため、並列I / O操作は実行できません。

InnoDBは、そのようなパフォーマンスを向上させるように調整できます。


私はそれを再び強調する必要があります:mydb.missing_dataがMyISAMであり、ゼロバイトのインデックスがある場合、何かが間違いです。
RolandoMySQLDBA 2014年

データをより一貫性のあるものに更新しました。単一のホストからのMyISAMのみの結果が表示されるので、混乱することはありません。
syneticon-dj 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.