dentryキャッシュとは?Linuxのパス名検索を高速化する仕組みを徹底解説

「Linuxサーバーのメモリが気づいたら減っている」
slabtopを見たらdentryが大量にある」
「そもそもdentryって何?」

こんな経験をしたことはありませんか。
dentryキャッシュはLinuxカーネルの中でも地味ながら重要な仕組みで、ファイルアクセスの速度に直接影響します。
この記事では、dentryキャッシュの役割・仕組み・メモリへの影響・運用上の対処法まで、一通り解説します。


スポンサーリンク

dentryとは何か

dentryとは、directory entry(ディレクトリエントリ)の略で、LinuxカーネルのVFS(Virtual File System、仮想ファイルシステム)が使う構造体のひとつです。

VFSはext4・XFS・Btrfsなど異なるファイルシステムを統一的に扱うための中間層で、dentryはその中で「ファイル名・ディレクトリ名とinode(実体)を結びつける」役割を持ちます。

inodeとdentryの違い

Linuxのファイルシステムには、大きく分けて2種類の管理情報があります。

構造体役割保持する情報の例
inodeファイルの実体を管理ファイルサイズ、パーミッション、タイムスタンプ、ディスク上の位置など
dentry名前と実体を結びつけるファイル名・ディレクトリ名、親dentryへの参照、inodeへの参照

重要なのは、ファイル名はinodeではなくdentryが持つという点です。
inodeはファイルの実体の情報だけを管理し、「どんな名前でアクセスされているか」は知りません。
dentryがその橋渡しをすることで、ハードリンクのように「複数の名前が同じinodeを指す」構造も実現できています。


dentryキャッシュが必要な理由

Linuxのディレクトリ構造でも触れているとおり、Linuxのパスは/var/log/nginx/access.logのように階層構造になっています。

このパスにアクセスするとき、カーネルは内部で以下の処理を行います。

  1. /(ルート)のdentryを確認する
  2. varのinodeをディスクから検索してdentryを作成する
  3. logのinodeをディスクから検索してdentryを作成する
  4. nginxのinodeをディスクから検索してdentryを作成する
  5. access.logのinodeをディスクから検索してdentryを作成する

階層が深くなるほど、ディスクI/Oが何度も発生します。
ディスクアクセスはメモリアクセスと比べて数百倍〜数千倍遅いため、毎回これをやっていては処理速度が大幅に落ちます。

そこでLinuxカーネルは、一度生成したdentryをメモリ上のキャッシュ(dentryキャッシュ)として保持し、同じパスへの2回目以降のアクセスはキャッシュから返すようにしています。
実際にls -R /を実行すると、キャッシュが温まった2回目以降は10倍以上高速になることが確認されています。


dentryキャッシュの仕組み

ハッシュテーブルで管理

dentryキャッシュはハッシュテーブルで管理されています。
検索キーは「親dentryのアドレス + ファイル名のハッシュ値」の組み合わせです。
これによりあるディレクトリ内の特定のファイル名を高速に検索できます。

検索関数はd_lookup()と呼ばれ、ヒットした場合は参照カウントを増やしてdentryを返します。
キャッシュに存在しない場合はNULLを返し、カーネルはディスクから実際に検索します。

dentryの状態

dentryは以下の3つの状態を持ちます。

  • 使用中(in-use):いずれかのプロセスが現在参照している状態。解放されない
  • 未使用(unused):参照カウントが0だが、キャッシュとして保持している状態。メモリ不足時に最初に回収される
  • ネガティブ(negative):存在しないパスに対して作られたdentry(後述)

ネガティブdentryとは

stat /etc/hoge_not_existのように存在しないパスへのアクセスが発生した場合、Linuxはディスクへの問い合わせの結果「ファイルが存在しない」という情報も、空のdentry(ネガティブdentry)としてキャッシュします。

なぜ存在しないものをキャッシュするかというと、「このパスは存在しない」という事実を覚えておくことで、次回同じパスにアクセスされたときにディスクI/Oを省略できるからです。
DNSのネガティブキャッシュと同じ発想です。

ただし、ランダムなファイル名を大量に生成・削除するアプリケーション(画像変換ツールなど)では、同じパスが再利用されないためネガティブdentryが溜まる一方になり、メモリを圧迫する原因になります(詳細は後述)。


Slabアロケータとdentryの関係

dentryはカーネルのSlabアロケータによって管理されます。
Slabアロケータとは、同じサイズのオブジェクトをまとめて確保・再利用するカーネルのメモリ管理機構です。

/proc/meminfoSlabフィールドがその使用量の合計で、slabtopコマンドでdentryの内訳を確認できます。

# Slabキャッシュの内訳を確認(要root権限)
sudo slabtop

出力例(上位項目):

 OBJS ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME
123456 120000  97%    0.19K   3090       40     24720K dentry
 56789  55000  96%    0.61K   1828       31     29248K inode_cache

RHELではdentry_cache(RHEL4・5)またはdentry(RHEL6以降)という名前で表示されます。


dentryキャッシュの確認方法

/proc/sys/fs/dentry-state

/proc/sys/fs/dentry-stateを読むと、dentryキャッシュの現在の状態を数値で確認できます。

cat /proc/sys/fs/dentry-state

出力形式:

nr_dentry  nr_unused  age_limit  want_pages  dummy  dummy
  • nr_dentry:現在確保されているdentryの総数
  • nr_unused:参照カウントが0のdentry数(解放候補)
  • want_pages:カーネルがdentryの縮小を要求しているページ数

/proc/meminfoでSlabを確認

Linuxのスペック確認コマンドでも紹介しているとおり、/proc/meminfoでSlab全体のサイズを確認できます。

cat /proc/meminfo | grep -E "Slab|SReclaimable|SUnreclaim"
  • Slab:Slabキャッシュの合計
  • SReclaimable:メモリ不足時に回収できる部分(dentryキャッシュはここに含まれる)
  • SUnreclaim:回収できない部分

dentryキャッシュの肥大化と対処法

肥大化が起きるケース

dentryキャッシュは通常、メモリが不足してくるとカーネルが自動で回収します。
ただし、以下のような状況では想定外に大量のdentryが蓄積することがあります。

  • 大量の一時ファイルをランダムな名前で作成・削除し続けるアプリ(画像変換、ログローテーションなど)
  • 大量のファイルがあるディレクトリをfindなどで全走査するバッチ処理
  • 存在しないパスへのアクセスが多発するアプリケーション

いずれも大量のdentry(特にネガティブdentry)が生成され、回収が追いつかない状態になります。

対処法1:vfs_cache_pressureを調整する

vm.vfs_cache_pressureはカーネルがdentryとinodeキャッシュをどの程度積極的に回収するかを制御するパラメータです。

動作
0絶対に回収しない(OOMの原因になりうる)
100(デフォルト)ページキャッシュと同等の優先度で回収
100より大きいより積極的に回収する
# 現在の値を確認
cat /proc/sys/vm/vfs_cache_pressure

# 一時的に変更(再起動で元に戻る)
sudo sysctl -w vm.vfs_cache_pressure=200

# 恒久的に変更(/etc/sysctl.confに追記)
echo "vm.vfs_cache_pressure=200" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

対処法2:drop_cachesで強制解放する

/proc/sys/vm/drop_cachesに値を書き込むと、キャッシュを強制的に解放できます。

# 実行前にsyncで書き込みを確定させる(重要)
sync

# 2:dentryキャッシュとinodeキャッシュを解放
echo 2 | sudo tee /proc/sys/vm/drop_caches

# 3:ページキャッシュ・dentryキャッシュ・inodeキャッシュをまとめて解放
echo 3 | sudo tee /proc/sys/vm/drop_caches

注意:drop_cachesは本番環境での常用は推奨されません。
解放直後はキャッシュが空になるため、ファイルアクセスのたびにディスクI/Oが発生し、一時的にパフォーマンスが低下します。
根本対策(後述)を先に検討してください。

対処法3:tmpfsを使う

一時ファイルを大量に作成・削除するアプリが原因の場合、tmpfs(メモリ上のファイルシステム)に一時ディレクトリを移すと効果的です。

tmpfsではすべてのデータがメモリ上に保持されるため、ディスクへのI/Oが不要でネガティブdentryもキャッシュされません。
unlinkでファイルを削除するとdentryも即座に破棄されます。

# /dev/shmはデフォルトでtmpfsがマウントされている
export TMPDIR=/dev/shm

# アプリの一時ディレクトリをtmpfsに変更する例
java -Djava.io.tmpdir=/dev/shm MyApp

対処法4:根本原因を特定して解消する

肥大化の根本は不要なファイルの蓄積過剰なパス検索であることが多いです。
特定のアプリがどのパスを大量に作成・アクセスしているかは、SystemTapbpftraceといったトレースツールで調査できます。

ログファイルなどが原因なら、定期的なローテーション・削除バッチを見直すと解決するケースが多いです。


まとめ

dentryキャッシュはLinuxのパス名検索を高速化するVFSの仕組みです。
ポイントを整理します。

  • dentryはファイル名・ディレクトリ名とinodeを結びつける構造体
  • 一度アクセスしたパスはキャッシュとして保持され、2回目以降のI/Oを省略する
  • 存在しないパスはネガティブdentryとしてキャッシュされ、無駄なディスクI/Oを防ぐ
  • キャッシュはSlabアロケータが管理し、slabtop/proc/meminfoで確認できる
  • 大量のランダムな一時ファイル作成・削除が原因で肥大化するケースが多い
  • 対処法はvfs_cache_pressureの調整・drop_caches・tmpfsへの移行・根本原因の特定

通常はカーネルが自動で回収するため意識する機会は少ないですが、サーバーのメモリが徐々に圧迫される現象に遭遇したときは、まずslabtopでdentryキャッシュの状況を確認してみてください。

ディスク容量の確認方法はLinuxのストレージ容量確認コマンド完全ガイドも参考にしてください。


参考情報源:

コメント

タイトルとURLをコピーしました