web-dev-qa-db-ja.com

大量に書き込まない場合のRAID-5での連続書き込みパフォーマンスが悪い

RAID5 + crypt + ext4で許容できる読み取り/書き込みパフォーマンスを得るのにいくつかの問題があり、最終的に次の問題まで追跡することができました。

ハードウェア

  • ハードディスク4xWD RED 3 TB WDC WD30EFRX-68EUZN0 as/dev/sd [efgh]
  • sdeとsdfは、3 Gbps/s SATAリンクを使用してコントローラーAを介して接続されます(6 Gbpsが使用可能であったとしても)
  • sdgとsdhは、6 Gbps/sSATAリンクを使用してコントローラーBを介して接続されます

シングルディスクのパフォーマンス

ディスクごとに4回テストを書き込みます(私が期待したすべて)

# dd if=/dev/zero of=/dev/sd[efgh] bs=2G count=1 oflag=dsync
sde: 2147479552 bytes (2.1 GB) copied, xxx s, [127, 123, 132, 127] MB/s
sdf: 2147479552 bytes (2.1 GB) copied, xxx s, [131, 130, 118, 137] MB/s
sdg: 2147479552 bytes (2.1 GB) copied, xxx s, [145, 145, 145, 144] MB/s
sdh: 2147479552 bytes (2.1 GB) copied, xxx s, [126, 132, 132, 132] MB/s

Hdparmとddを使用してテストを読む(私が期待したすべて)

# hdparm -tT /dev/sd[efgh]
# echo 3 | tee /proc/sys/vm/drop_caches; dd of=/dev/null if=/dev/sd[efgh] bs=2G count=1 iflag=fullblock

(sde)
Timing cached reads:   xxx MB in  2.00 seconds = [13983.68, 14136.87] MB/sec
Timing buffered disk reads: xxx MB in  3.00 seconds = [143.16, 143.14] MB/sec
2147483648 bytes (2.1 GB) copied, xxx s, [140, 141] MB/s

(sdf)
Timing cached reads:   xxx MB in  2.00 seconds = [14025.80, 13995.14] MB/sec
Timing buffered disk reads: xxx MB in  3.00 seconds = [140.31, 140.61] MB/sec
2147483648 bytes (2.1 GB) copied, xxx s, [145, 141] MB/s

(sdg)
Timing cached reads:   xxx MB in  2.00 seconds = [14005.61, 13801.93] MB/sec
Timing buffered disk reads: xxx MB in  3.00 seconds = [153.11, 151.73] MB/sec
2147483648 bytes (2.1 GB) copied, xxx s, [154, 155] MB/s

(sdh)
Timing cached reads:   xxx MB in  2.00 seconds = [13816.84, 14335.93] MB/sec
Timing buffered disk reads: xxx MB in  3.00 seconds = [142.50, 142.12] MB/sec
2147483648 bytes (2.1 GB) copied, xxx s, [140, 140] MB/s

Sd [efgh]のパーティション

4x 32 GiBテスト用

# gdisk -l /dev/sd[efgh]
GPT fdisk (gdisk) version 0.8.10

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.
Disk /dev/sde: 5860533168 sectors, 2.7 TiB
Logical sector size: 512 bytes
Disk identifier (GUID): xxx
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 5860533134
Partitions will be aligned on 2048-sector boundaries
Total free space is 5793424237 sectors (2.7 TiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048        67110911   32.0 GiB    FD00  Linux RAID

RAIDアレイ

# mdadm --create --verbose /dev/md0 --level=5 --raid-devices=4 --chunk=256K /dev/sd[efgh]1
(some tests later ...)
# mdadm --grow --verbose /dev/md0 --layout=right-asymmetric
# mdadm --detail /dev/md0
/dev/md0:
    Version : 1.2
  Creation Time : Sat Dec 10 03:07:56 2016
     Raid Level : raid5
     Array Size : 100561920 (95.90 GiB 102.98 GB)
  Used Dev Size : 33520640 (31.97 GiB 34.33 GB)
   Raid Devices : 4
  Total Devices : 4
    Persistence : Superblock is persistent

    Update Time : Sat Dec 10 23:56:53 2016
          State : clean
 Active Devices : 4
Working Devices : 4
 Failed Devices : 0
  Spare Devices : 0

         Layout : right-asymmetric
     Chunk Size : 256K

           Name : vm:0  (local to Host vm)
           UUID : 80d0f886:dc380755:5387f78c:1fac60da
         Events : 158

    Number   Major   Minor   RaidDevice State
       0       8       65        0      active sync   /dev/sde1
       1       8       81        1      active sync   /dev/sdf1
       2       8       97        2      active sync   /dev/sdg1
       4       8      113        3      active sync   /dev/sdh1

現在の状況

アレイは、連続的な読み取りと書き込みでおよそ350〜400 MB/sのパフォーマンスを期待していました。ボリューム全体の読み取りまたは書き込みは、実際にはこの範囲内で完全に結果をもたらします。

# echo 3 | tee /proc/sys/vm/drop_caches; dd of=/dev/null if=/dev/md0 bs=256K
102975406080 bytes (103 GB) copied, 261.373 s, 394 MB/s

# dd if=/dev/zero of=/dev/md0 bs=256K conv=fdatasync
102975406080 bytes (103 GB) copied, 275.562 s, 374 MB/s

ただし、書き込みパフォーマンスは、書き込まれるデータの量に大きく依存します。予想どおり、転送速度はデータの量とともに増加しますが、2 GiBに達すると停止し、サイズをさらに増やすとゆっくりと回復します。

# dd if=/dev/zero of=/dev/md0 bs=256K conv=fdatasync count=x
count=1: 262144 bytes (262 kB) copied, xxx s, [3.6, 7.6, 8.9, 8.9] MB/s
count=2: 524288 bytes (524 kB) copied, xxx s, [3.1, 17.7, 15.3, 15.7] MB/s
count=4: 1048576 bytes (1.0 MB) copied, xxx s, [13.2, 23.9, 26.9, 25.4] MB/s
count=8: 2097152 bytes (2.1 MB) copied, xxx s, [24.3, 46.7, 45.9, 42.8] MB/s
count=16: 4194304 bytes (4.2 MB) copied, xxx s, [5.1, 77.3, 42.6, 73.2, 79.8] MB/s
count=32: 8388608 bytes (8.4 MB) copied, xxx s, [68.6, 101, 99.7, 101] MB/s
count=64: 16777216 bytes (17 MB) copied, xxx s, [52.5, 136, 159, 159] MB/s
count=128: 33554432 bytes (34 MB) copied, xxx s, [38.5, 175, 185, 189, 176] MB/s
count=256: 67108864 bytes (67 MB) copied, xxx s, [53.5, 244, 229, 238] MB/s
count=512: 134217728 bytes (134 MB) copied, xxx s, [111, 288, 292, 288] MB/s
count=1K: 268435456 bytes (268 MB) copied, xxx s, [171, 328, 319, 322] MB/s
count=2K: 536870912 bytes (537 MB) copied, xxx s, [228, 337, 330, 334] MB/s
count=4K: 1073741824 bytes (1.1 GB) copied, xxx s, [338, 348, 348, 343] MB/s <-- ok!
count=8K: 2147483648 bytes (2.1 GB) copied, xxx s, [168, 147, 138, 139] MB/s <-- bad!
count=16K: 4294967296 bytes (4.3 GB) copied, xxx s, [155, 160, 178, 144] MB/s
count=32K: 8589934592 bytes (8.6 GB) copied, xxx s, [256, 238, 264, 246] MB/s
count=64K: 17179869184 bytes (17 GB) copied, xxx s, [298, 285] MB/s
count=128K: 34359738368 bytes (34 GB) copied, xxx s, [347, 336] MB/s
count=256K: 68719476736 bytes (69 GB) copied, xxx s, [363, 356] MB/s <-- getting better

(2未満GiB最初の測定値は、読み取りキャッシュの使用状況を示しているようです)

2 GiB以上を転送しているときに、iotopで何か奇妙なことが観察されました:

  • フェーズ1:最初の「合計ディスク書き込み」と「実際のディスク書き込み」はどちらも約「400MB /秒」です。 ddのIO値は約85%ですが、他のすべての値は0%です。このフェーズは、転送が大きいほど長く続きます。
  • フェーズ2:送信が完了する数秒(〜16秒)前に、kworkerがジャンプし、/ steals/30-50パーセントポイントのIO from dd。分布は30:50%から50:30%の間で変動します。同時に、「TotalDISKWRITE」は0B/sに低下し、「ActualDISKWRITE」は20〜70 MB/sの間でジャンプします。このフェーズは一定時間続くようです。
  • フェーズ3:最後の3秒以内に、「実際のディスク書き込み」は400 MB /秒を超えてジャンプし、「合計ディスク書き込み」は0 B /秒のままです。 ddkworkerは両方とも0%のIO値でリストされています
  • フェーズ4:ddのIO値は1秒間で最大5%ジャンプします。同時に転送が完了します。

さらにいくつかのテスト

# dd if=/dev/zero of=/dev/md0 bs=256K count=32K oflag=direct
8589934592 bytes (8.6 GB) copied, 173.083 s, 49.6 MB/s

# dd if=/dev/zero of=/dev/md0 bs=256M count=64 oflag=direct
17179869184 bytes (17 GB) copied, 47.792 s, 359 MB/s

# dd if=/dev/zero of=/dev/md0 bs=768M count=16K oflag=direct
50734301184 bytes (51 GB) copied, 136.347 s, 372 MB/s <-- peak performance

# dd if=/dev/zero of=/dev/md0 bs=1G count=16K oflag=direct
41875931136 bytes (42 GB) copied, 112.518 s, 372 MB/s <-- peak performance

# dd if=/dev/zero of=/dev/md0 bs=2G count=16 oflag=direct
34359672832 bytes (34 GB) copied, 103.355 s, 332 MB/s

# dd if=/dev/zero of=/dev/md0 bs=256K count=32K oflag=dsync
8589934592 bytes (8.6 GB) copied, 498.77 s, 17.2 MB/s

# dd if=/dev/zero of=/dev/md0 bs=256M count=64 oflag=dsync
17179869184 bytes (17 GB) copied, 58.384 s, 294 MB/s

# dd if=/dev/zero of=/dev/md0 bs=1G count=8 oflag=dsync
8589934592 bytes (8.6 GB) copied, 26.4799 s, 324 MB/s

# dd if=/dev/zero of=/dev/md0 bs=2G count=8 oflag=dsync
17179836416 bytes (17 GB) copied, 192.327 s, 89.3 MB/s

# dd if=/dev/zero of=/dev/md0 bs=256K; echo "sync"; sync
102975406080 bytes (103 GB) copied, 275.378 s, 374 MB/s
sync
  • bs=256K oflag=direct-> 100%IO、kworkerが存在しない、パフォーマンスが悪い
  • bs=1G oflag=direct-> <5%IO、kworkerが存在しない、正常なパフォーマンス
  • bs=2G oflag=direct- >> 80%I​​O、kworkerが時々ジャンプし、パフォーマンスは良好
  • oflag=dsync-> <5%IO、kworkerは時々ジャンプします。許容可能な速度を得るには巨大なブロックサイズが必要ですが、2Gを超えるとパフォーマンスが大幅に低下します。
  • echo "sync"; sync-> conv=fdatasyncと同じ; syncはすぐに戻ります

質問

両方のプロセスがIOのために戦っているように見えるその神秘的なフェーズ2は何ですか?

フェーズ3で誰がデータをハードウェアに転送しますか?

そして最も重要なこと:アレイが提供できると思われる完全な400 MB/sを取得するために、奇妙な影響を最小限に抑えるにはどうすればよいですか? (または、XY問題を尋ねることさえありますか?)

ボーナス

現在の状態に先立って試行錯誤の長い物語があります。スケジューラーをcfqからnoopに切り替え、RAIDチャンクサイズを512kから256kに減らして、わずかに良い結果を得ました。 --layout=right-asymmetricに変更しても、何も変更されませんでした。ハードドライブの書き込みキャッシュを一時的に非アクティブ化すると、パフォーマンスが低下しました。

最初の文で言及されているクリプトレイヤーは現在完全に存在せず、後で再導入されます。

# uname -a
Linux vm 3.16.0-4-AMD64 #1 SMP Debian 3.16.36-1+deb8u2 (2016-10-19) x86_64 GNU/Linux
1
Kai Giebeler

表示されているのは、特にconv=fdatasyncオプションからのddコマンドラインのアーティファクトです。 manページ から:

各CONVシンボルは次のとおりです。
.。
fdatasync:終了する前に出力ファイルデータを物理的に書き込む
.。

conv=fdatasyncは基本的にddに、再起動する前に単一の最後のfdatasyncシステムコールを実行するように指示します。ただし、書き込みはddの実行中にキャッシュされます。I/Oフェーズは次のように説明できます。

  1. ddは、実際にディスクに触れることなく、ページキャッシュにすばやく書き込みます
  2. ページキャッシュがほぼいっぱいになり、kworkerカーネルがディスクのフラッシュを開始します。ページキャッシュのフラッシュ中、ddは一時停止します(結果としてiowaitが高くなります)。一部のページキャッシュが解放された後、ddは操作を回復できます
  3. iotopでの合計ディスク書き込みと実際のディスク書き込みの違いは、ページキャッシュがそれぞれどのように埋められ、フラッシュされるかによって異なります。
  4. サイクルは続く

要するに、ここでは問題はありません。キャッシュされていない動作を観察したい場合は、conv=fdatasyncoflag=directに置き換えます。このフラグを使用すると、ページキャッシュを完全にバイパスできます。

キャッシュを監視しながら同期された動作を行うには、conv=fdatasyncoflag=syncに置き換えます。このフラグを使用して、dd call fdatasync各ブロックがディスクに書き込まれるとき。

I/Oスタックを微調整することでさらに最適化できます(つまり、I/Oスケジューラ、マージ動作、ストライプキャッシュ、ecc)が、これはまったく別の問題です。

1
shodanshok