RAMディスク:RAMディスクの利点とパフォーマンスの謎

RAMディスク:RAMディスクの利点とパフォーマンスの謎

ラムディスク(RAMディスクとも呼ばれます)とは、メモリの一部をディスクとして扱う方法です。少し考えてみると、そのメリットとデメリットは明らかです。

  • RAM は最速のディスクよりもはるかに高速であるため、RAM ディスクでの操作は、NVMe、SSD、そしてもちろん回転ディスクを使用する場合よりもはるかに高速です。
  • しかし、RAMは揮発性です。サーバーが再起動したりクラッシュしたりすると、RAMディスク上のデータはすべて失われます。

RAMディスクは、キャッシュ、セッションファイル、その他の一時データを保存するのに最適な場所です。データベースジャーナル(Postgresの先行書き込みログ、OracleのREDOログなど)をRAMディスクに保存し、システムのシャットダウン/起動時に永続ストレージにコピーして復元したり、クラッシュに備えて頻繁にバックアップを実行したりする設定も行っています。

RAMディスクの設定と使い方を見てみましょう。楽しみのために、大容量の24GB Linodeシステムを起動してみました。

 root@bigmem:~# free -m
               合計        使用済み        無料      共有  バフ/キャッシュ  利用可能
メモ:          24049          65      23913          0          70      23732
スワップ:            511          0        511

システムを再構成して再起動する必要があると思われるかもしれませんが、実際はもっと簡単です。

 root@bigmem:~# mkdir /ramdisk
root@bigmem:~# マウント -t tmpfs -o サイズ=16G ramdisk16 /ramdisk
root@bigmem:/ramdisk# df -h /ramdisk
ファイルシステム      サイズ  使用済み 利用可能 使用率 マウント済み
ramdisk16        16G    0  16G  0% /RAMディスク

本当にこれだけです。ここでの「ramdisk16」は私が勝手に付けた名前です。このパラメータは、マウントコマンドの一般的な形式(「mount [type] [options] [device] [mountpoint]」)のために必要だと考えています。「device」という名前自体がないので、プレースホルダーを使用しています。

「ramdisk」という用語は少し誤解を招く可能性があることに注意してください。ramdisk は、ファイルシステムなどを作成するための何らかの /dev/sdX などのデバイスを取得するものと考えるからです。ramdisk は、実際には、事前に作成されたディスク上の「ram ファイルシステム」と考えることができます。

/ramdisk では、ディレクトリの作成、ファイルの追加など、やりたいことは何でもできるようになりました。しかし、アンマウント(またはシステムの再起動)すると、すべてが失われてしまいます。/etc/fstab にエントリを永続化することはできますが、もちろんこれは、再起動時に RAM ディスクが空のマウントとして再作成されることを意味します。

 ramdisk16 /ramdisk tmpfs デフォルト、サイズ=16G 0 0

それで、本当に速くなるのでしょうか? 見てみましょう。そして、頭を悩ませる話もお楽しみに!

ioping

 ioping ツールを使用して、SSD ディスクは次のようになります。

root@bigmem:/# ioping 。
4 KiB <<< . (ext4 /dev/sda 19.2 GiB): リクエスト=1 時間=82.2 us (ウォームアップ)
4 KiB <<< . (ext4 /dev/sda 19.2 GiB): リクエスト=2 時間=933.8 us
4 KiB <<< . (ext4 /dev/sda 19.2 GiB): リクエスト=3 時間=304.4 us
4 KiB <<< . (ext4 /dev/sda 19.2 GiB): リクエスト=4 時間=306.1 us
4 KiB <<< . (ext4 /dev/sda 19.2 GiB): リクエスト=5 時間=279.4 us
4 KiB <<< . (ext4 /dev/sda 19.2 GiB): リクエスト=6 時間=337.5 us
^C
--- . (ext4 /dev/sda 19.2 GiB) ioping 統計 ---
5 件のリクエストが 2.16 ミリ秒で完了、20 KiB の読み取り、2.31 k iops、9.04 MiB/s
5.63 秒で 6 件のリクエストを生成しました。24 KiB、1 iops、4.26 KiB/s
最小/平均/最大/平均偏差 = 279.4 us / 432.2 us / 933.8 us / 251.4 us
一方、RAM ディスクでも同じことを実行します。

root@bigmem:/# cd /ramdisk/
root@bigmem:/ramdisk# ioping 。
4 KiB <<< . (tmpfs ramdisk16 16 GiB): リクエスト=1 時間=1.76 us (ウォームアップ)
4 KiB <<< . (tmpfs ramdisk16 16 GiB): リクエスト=2 時間=8.43 us
4 KiB <<< . (tmpfs ramdisk16 16 GiB): リクエスト=3 時間=12.1 us
4 KiB <<< . (tmpfs ramdisk16 16 GiB): リクエスト=4 時間=11.8 us
4 KiB <<< . (tmpfs ramdisk16 16 GiB): リクエスト=5 時間=10.2 us
4 KiB <<< . (tmpfs ramdisk16 16 GiB): リクエスト=6 時間=9.30 us
4 KiB <<< . (tmpfs ramdisk16 16 GiB): リクエスト=7 時間=9.66 us
^C
--- . (tmpfs ramdisk16 16 GiB) ioping 統計 ---
6 件のリクエストが 61.5 マイクロ秒で完了、24 KiB の読み取り、97.6 k iops、381.3 MiB/s
6.48 秒で 7 件のリクエストを生成しました。28 KiB、1 iops、4.32 KiB/s
最小/平均/最大/平均偏差 = 8.43 us / 10.2 us / 12.1 us / 1.32 us

パーセントの差を計算するつもりでしたが、それは必要ないと思います。

dd 書き込みと読み取り

1GB のファイルを送信してみましょう。

 root@bigmem:/ramdisk# time dd if=/dev/zero of=/ramdisk/1gbfile bs=1MB count=1024
1024+0件のレコード
1024+0 レコード出力
1024000000バイト(1.0 GB、977 MiB)をコピー、0.38423秒、2.7 GB/秒
実数 0分0秒450秒
ユーザー 0m0.000s
システム 0分0.448秒

さて、ここで興味深い点があります。2GBのファイルは2倍くらい時間がかかると予想していましたが、実際その通りです。しかし、4GBのファイルだとどれくらい時間がかかるのでしょうか?あるいは8GBだと?明らかに直線的に時間がかかるわけではありません。例えば、以下のような場合です。

RAMディスクファイルサイズddで割り当てる時間
1GB約0.4秒
2GB約0.75秒
4ギガバイト約32秒
8GB約145秒

私の理論では、物理ホストサーバーは1GBか2GBのRAMならかなり簡単に確保でき、おそらくそのくらいの容量のメモリが余っているはずです。しかし、8GBの空きメモリを見つけるには、より集中的に空きメモリを探す必要があります。私の頭の中での例えは、駐車場に行って車を1台停めたいのと、32台駐車したいのとでは違います。駐車場には十分なスペースがあるのですが、空きスペースを見つけるのに少し時間がかかります。

私はコミュニティに説明を求めました

ラムディスクに戻りましょう。ラムディスクとNVMeを比較してみましょう。ラムディスク:

 root@bigmem:/ramdisk# for run in 1 2 3 ; do time dd if=/dev/zero of=/ramdisk/2gbfile bs=1MB count=2048 ; done
2048+0件のレコード
2048+0 件のレコードが出力されました
2048000000バイト(2.0 GB、1.9 GiB)をコピーしました。0.727246秒、2.8 GB/秒

実数 0分0.853秒
ユーザー 0m0.000s
システム 0分0.851秒
2048+0件のレコード
2048+0 件のレコードが出力されました
2048000000バイト(2.0 GB、1.9 GiB)をコピーしました。0.729331秒、2.8 GB/秒

実数 0分0.855秒
ユーザー 0m0.000s
システム 0分0.853秒
2048+0件のレコード
2048+0 件のレコードが出力されました
2048000000バイト(2.0 GB、1.9 GiB)をコピーしました。0.726534秒、2.8 GB/秒

実数 0分0.853秒
ユーザー 0m0.000s
システム 0分0.851秒

つまり約0.85秒です。NVMe:

 root@bigmem:/ramdisk# for run in 1 2 3 ; do time dd if=/dev/zero of=/2gbfile bs=1MB count=2048 ; done
2048+0件のレコード
2048+0 件のレコードが出力されました
2048000000バイト(2.0 GB、1.9 GiB)をコピー、1.86907秒、1.1 GB/秒

実数 0分2秒059秒
ユーザー 0m0.000s
システム 0分1.441秒
2048+0件のレコード
2048+0 件のレコードが出力されました
2048000000バイト(2.0 GB、1.9 GiB)をコピー、1.66434秒、1.2 GB/秒

実数 0分1秒855秒
ユーザー 0m0.000s
システム 0分1.437秒
2048+0件のレコード
2048+0 件のレコードが出力されました
2048000000バイト(2.0 GB、1.9 GiB)をコピー、1.69488秒、1.2 GB/秒

実数 0分1秒887秒
ユーザー 0分0.004秒
システム 0分1.424秒

では、次の読み取りを試してみましょう。

 root@bigmem:/ramdisk# for run in 1 2 3 ; do time dd if=/ramdisk/2gbfile of=/dev/null bs=1024k ; done
1953+1の記録
1953+1のレコードがリリース
2048000000バイト(2.0 GB、1.9 GiB)をコピーしました。0.183827秒、11.1 GB/秒

実数 0分0.185秒
ユーザー 0m0.000s
システム 0分0.185秒
1953+1の記録
1953+1のレコードがリリース
2048000000バイト(2.0 GB、1.9 GiB)をコピーしました。0.18002秒、11.4 GB/秒

実数 0分0.181秒
ユーザー 0m0.000s
システム 0分0.181秒
1953+1の記録
1953+1のレコードがリリース
2048000000バイト(2.0 GB、1.9 GiB)をコピーしました。0.180192秒、11.4 GB/秒

実数 0分0.181秒
ユーザー 0m0.000s
システム 0分0.181秒
root@bigmem:/ramdisk# for run in 1 2 3 ; do time dd if=/2gbfile of=/dev/null bs=1024k ; done
1953+1の記録
1953+1のレコードがリリース
2048000000バイト(2.0 GB、1.9 GiB)をコピーしました。0.172908秒、11.8 GB/秒

実数 0分0.174秒
ユーザー 0m0.000s
システム 0分0.174秒
1953+1の記録
1953+1のレコードがリリース
2048000000バイト(2.0 GB、1.9 GiB)をコピーしました。0.235156秒、8.7 GB/秒

実数 0分0.236秒
ユーザー 0m0.000s
システム 0分0.236秒
1953+1の記録
1953+1のレコードがリリース
2048000000バイト(2.0 GB、1.9 GiB)をコピーしました。0.171044秒、12.0 GB/秒

実数 0分0.172秒
ユーザー 0m0.000s
システム 0分0.172秒

これらの数値を見ると、OSのキャッシュがディスクに大きく貢献していると思われます。これは良いことです。レイテンシと書き込み速度は、予想通りRAMディスクの方がはるかに優れています。特に書き込み速度は、CPUオペコード(RAMディスク)を1つ実行するのと、ストレージデバイスとやり取りしてデータのコミットを要求し、応答を待つのとでは大きく異なります。もちろん、これらはすべて非常に高速ですが、メインメモリの外部に一切アクセスしないものほど高速ではありません。

おすすめの記事