Red Hatの森若です。
ddやcatで大きめのファイルを操作して、dfでファイルシステムの統計情報を確認する簡単な実験をします。
(注意: 諸条件によりお手元で同じ操作をしても再現しない場合があります)
## 1GBのファイルhogeを作ります # dd if=/dev/zero of=/hoge bs=1GiB count=1 1+0 records in 1+0 records out 1073741824 bytes (1.1 GB, 1.0 GiB) copied, 0.474922 s, 2.3 GB/s # df -h / Filesystem Size Used Avail Use% Mounted on /dev/mapper/rhel_rhel84-root 17G 7.2G 9.9G 42% / ## catで同じ内容のファイルhugaを作ります。 # cat hoge > huga # df -h / Filesystem Size Used Avail Use% Mounted on /dev/mapper/rhel_rhel84-root 17G 8.2G 8.9G 48% / ## catでファイルhugaの末尾に1GB追記します。 # cat hoge >> huga # df -h / Filesystem Size Used Avail Use% Mounted on /dev/mapper/rhel_rhel84-root 17G 11G 6.9G 60% / ## ↑ 1GBしか消費しないはずなのに2GB消費されています ## catでさらにファイルhugaの末尾に1GB追記します。 # cat hoge >> huga # df -h / Filesystem Size Used Avail Use% Mounted on /dev/mapper/rhel_rhel84-root 17G 11G 6.9G 60% / ## ↑ 今度は使用量が変わりませんでした
予想外の動作だという人も多いかと思います。
ファイルのフラグメンテーション
本題に入る前に前提知識です。
伝統的なファイルシステムの仕事として、ファイルのフラグメンテーションを予防することがあります。 ファイルのフラグメンテーションとは1つのファイルがブロックデバイス上でバラバラの位置に配置されることで、 特にハードディスクではファイルの読み書きのパフォーマンスに大きな影響があります。 (近年はNVMe やSSDの普及によって影響が小さくなってきました)
フラグメンテーションの逆に、ファイルをブロックデバイス上で連続して配置できると、良いことがあります。
- あとで参照・更新するときに連続でアクセスできるので速い
- ファイル内のどこのデータがブロックデバイス上のどこにあるかの対応関係を管理するデータが少なくて済む
このような背景から、ファイルシステムにはファイルのフラグメンテーションを予防するための工夫が含まれています。
XFSのspeculative preallocation
RHELのデフォルトのファイルシステムであるXFSにはフラグメンテーションを予防するための仕組みとして speculative preallocation(直訳すると "投機的な事前割り当て")と呼ばれる機能があります。 仕組みは簡単で、ファイル末尾への書き込みを契機として本来必要なファイル末尾までを格納するブロックより あとの連続したブロックをあらかじめファイルに割りあてます。 もし同じファイルへ追記すると、割りあてられたブロックが利用されます。 割りあてたブロックはデフォルトで5分間使われなければ回収されます。
さきほどと同じ実験をして、消費が1GB増えるはずが2GB増えたときにファイルの状態をstatで見てみます。
## ファイル huga は 2GB、512バイトのブロックが 4194304 個のはずが 6291584個割りあてられている # stat huga File: huga Size: 2147483648 Blocks: 6291584 IO Block: 4096 regular file Device: fd00h/64768d Inode: 1256334 Links: 1 Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root) Context: unconfined_u:object_r:etc_runtime_t:s0 Access: 2022-11-23 05:51:54.801681431 +0900 Modify: 2022-11-23 05:52:02.397067737 +0900 Change: 2022-11-23 05:52:02.397067737 +0900 Birth: - ## しばらく待ってからstatすると Block が減っている # stat huga File: huga Size: 2147483648 Blocks: 4194304 IO Block: 4096 regular file Device: fd00h/64768d Inode: 1256334 Links: 1 Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root) Context: unconfined_u:object_r:etc_runtime_t:s0 Access: 2022-11-23 05:51:54.801681431 +0900 Modify: 2022-11-23 05:52:02.397067737 +0900 Change: 2022-11-23 05:52:02.397067737 +0900 Birth: -
speculative preallocationは様々なコマンドを呼び出すシェルスクリプトやログの出力などでみられる、 「ファイルをopen、末尾へ出力、ファイルをclose」を間欠的に複数のプロセスから繰り返すパターンにうまく対応します。 あらかじめ確保するサイズはファイル容量によって調整され、ファイルシステム全体の空き容量も考慮されます。
speculative preallocationの制限
ファイルシステムの統計情報を元に何らかの処理をおこなうプログラムが speculative preallocationにうまく対応できない場合があります。 このような場合 speculative preallocationを禁止することはできませんが、 あらかじめ確保するサイズを小さくして影響を小さくすることができます。
XFSのマウントオプション allocsize= でspeculative preallocationのサイズを指定できます。
## fstabの例 /dev/mapper/rhel_rhel84-root / xfs defaults,allocsize=64k 0 0
まとめ
XFSには独特な工夫があり、統計情報を見ているとびっくりすることもあります。 ほとんどの場合に害はありませんが、問題がある場合には制限をかけることもできます。
関連リンク
linux kernelのxfsドキュメント mjmwired.net
RHELのナレッジ記事 access.redhat.com