PHPおよびrealpath_cache

翻訳者から:先日、サービスのデプロイ後にエラーが発生したことを理解すると、PHPでファイルステータスをキャッシュするメカニズムに関するこのすばらしい記事に出会いました。 コミュニティに翻訳を提供します。

PHP関数realpath_cache_get()およびrealpath_cache_size()について聞いたことがありますか? または、php.iniのパラメータrealpath_cache_sizeおよびrealpath_cache_ttlについてですか?

realpathキャッシュは、覚えておくべき非常に重要なPHPメカニズムです。 特に、プロジェクトの展開時など、シンボリックリンクを使用する必要がある場合。 リアルパスキャッシングを設定すると、サーバーのパフォーマンスとサーバーサブシステムのディスク負荷に大きな影響を与える可能性があります。 このパラメーターは、最初のPHPフレームワークが登場し始めたバージョン5.1で導入されました。

次に、すべてが内部でどのように機能するのか、それとどのように共存するのかを理解します。 猫の下には、ソースへの多くのリンクがあります。


stat()システムコールを覚えておいてください


システムの仕組みを知っていますか? あなたの記憶をリフレッシュさせてください。 pathを使用する場合、システムカーネルとファイルシステムは、それらから何を望むかを理解する必要があります。 パスを使用してファイルにアクセスする場合、ライブラリまたはシステムカーネルで許可する必要があります。 パスの許可は、それに関する情報を取得することです。それはファイル、ディレクトリ、またはリンクでしょうか?

これを行う1つの方法は、ファイルタイプについてシステムに問い合わせることです。 リンクを取得した場合は、ターゲットファイルについて調べてください。 相対パス(「 ../hey/./you/../foobar 」など)を使用する場合は、最初に絶対パスを取得し、次に宛先ファイルに関する情報を取得する必要があります。

通常、相対パスの解決にはrealpath() C関数が使用されます。 次に、彼女はstat()システムコールを行います。

stat()呼び出しはかなり重いです。 まず、割り込みとコンテキストの切り替えを伴うシステムコールです。 第二に、遅いディスク上のデータを処理します。 コードでは、inode-> getattr()ファイルシステムの呼び出しを見つけることができます。 通常、カーネルは独自のキャッシュ( buffer-cache )を使用するため、パフォーマンスへの影響はほとんどありません。 ただし、ロードされたサーバーでは、キャッシュに必要な情報が含まれていない場合があり、ディスクサブシステムの負荷が増加します。 したがって、このような動作を防ぐことは私たちの利益になります。

PHPは何をしますか?


通常、PHPで作成されたプロジェクトは多くのファイルに保存されます。 今日、私たちは大量のファイルを意味する大量のクラスを使用します(各クラスにファイルを使用するため)。 自動ロードメカニズム(autoload)を使用するかどうかに関係なく、これらのファイルをすべて接続して内部のコードを読み取り、これを行うには、 stat()を呼び出してファイルに関する情報を取得する必要があります。 したがって、PHPからファイルにアクセスすると、最初にパスとリンクを解決し、次にstat()システムコールを介してファイルに関する情報を受け取り、結果をrealpath cacheと呼ばれる独自のキャッシュに保存します。

PHPは、 realpath()関数がrealpath()ている場合にのみこのキャッシュを使用します。 所有者、グループ、アクセス権、タイムスタンプなど、ファイルに関する他のすべての情報は、別のキャッシュ- access cache保存されaccess cache 。 ソースを見てみましょう。ファイルにアクセスすると、php_resolve_path()関数が呼び出されます。 この関数はtsrm_reapath()を呼び出し、 virtual_file_ex()およびtsrm_realpath_r()を内部的に実行します。

そして、ここで興味深いことが起こります: realpath_cache_find()のような関数は、要求されたファイルのキャッシュに保存されたデータを見つけるために呼び出されます。 情報を保存するために、大きなデータパケットをカプセル化するrealpath_cache_bucket構造が使用されます。

 typedef struct _realpath_cache_bucket { unsigned long key; char *path; int path_len; char *realpath; int realpath_len; int is_dir; time_t expires; #ifdef PHP_WIN32 unsigned char is_rvalid; unsigned char is_readable; unsigned char is_wvalid; unsigned char is_writable; #endif struct _realpath_cache_bucket *next; } realpath_cache_bucket; 

キャッシュにデータが見つからない場合、 php_sys_lstat()関数が呼び出されます 。これは、lastat()システムコールのプロキシです。 この呼び出しの結果は、realpathキャッシュに保存されます。

PHP設定


そのため、PHP側から、リアルパスキャッシュについていくつかのことを知る必要があります。 開始するには、 php.ini構成します。
realpath_cache_size
realpath-cache-ttl

ドキュメントには、ソースコードがほとんど変更されないサーバーでこれらのパラメーターを増やすことについての注意があります。 また、標準の16KBキャッシュサイズはごくわずかであることも考慮する価値があります。 Symfony2のようなフレームワークを使用すると、1回のリクエストですべてが使い果たされます。 キャッシュサイズの設定を最新に維持するには、 realpath_cache_get()関数の出力に従う必要があります。 使用可能なボリュームがすぐに使い果たされる場合、これはキャッシュサイズを最大1MBまで増やす明確な理由です。 キャッシュがオーバーフローすると、PHPはstat()呼び出しを乱用し始め、パフォーマンスに直接影響します。 必要なキャッシュサイズを十分な精度で計算することは困難です。 ソースを調べてみると、キャッシュ内の各エンティティは次と等しい場所を占めると結論付けることができます: `sizeof(realpath_cache_bucket) + - + 1`
64ビットシステム(LP64)の場合、sizeof(realpath_cache_bucket)= 56バイト。

別の機能があります。 PHPは、実行中に遭遇するすべてのパスを解決し、断片に分割します。 ファイル/home/julien/www/fooproject/app/web/entry.phpをリクエストすると、PHPはルートから始まる最大数の利用可能なパスにファイルを/home/julien/www/fooproject/app/web/entry.phpします。 したがって、最初にキャッシュ/home 、次に/home/julien 、次に/home/julien/wwwなどに格納されます。

なんで? まず、パスの各レベルへのアクセスを検証するためにこれが必要です。 第二に、多くのユーザーは連結を使用してパスを作成するため、PHPは既にキャッシュされたエンティティをリクエストするたびに、部分的にパスをチェックできます。 キャッシュへのアクセスは非常に高速で、詳細はtsrm_realpath_r()のソースにあります。 それは
パスの各要素でデフォルトで呼び出される再帰関数。

全体として、前の段落からの最初の結論:キャッシュは良いです!

2番目-レイアウト後にサイトのいくつかのページを「プル」する-サイトへのパブリックアクセスを開く前に必要なタスク。 これにより、OPcodeキャッシュがリセットされるだけでなく、システムカーネルのリアルパスキャッシュとページキャッシュも更新されます。

realpathキャッシュをクリアする方法は? このタスクを実行する機能は、pr索好きな目から隠されています。 realpath_cache_clear() ? いいえ、そのような関数は存在しません:(。しかし、PHPの最良の伝統では、 clearstatcache(true)がありますtrueパラメーターは非常に重要で、 $clear_realpath_cacheと呼ばれます。明らかに、目的に役立ちます。


天井から簡単な例を見てください^

 <?php $f = @file_get_contents('/tmp/bar.php'); echo "hello"; var_dump(realpath_cache_get()); 

それは彼が私たちをもたらすものです
 hello array(5) { ["/home/julien.pauli/www/realpath_example.php"]=> array(4) { ["key"]=> float(1.7251638834424E+19) ["is_dir"]=> bool(false) ["realpath"]=> string(43) "/home/julien.pauli/www/realpath_example.php" ["expires"]=> int(1404137986) } ["/home"]=> array(4) { ["key"]=> int(4353355791257440477) ["is_dir"]=> bool(true) ["realpath"]=> string(5) "/home" ["expires"]=> int(1404137986) } ["/home/julien.pauli"]=> array(4) { ["key"]=> int(159282770203332178) ["is_dir"]=> bool(true) ["realpath"]=> string(18) "/home/julien.pauli" ["expires"]=> int(1404137986) } ["/tmp"]=> array(4) { ["key"]=> float(1.6709564980243E+19) ["is_dir"]=> bool(true) ["realpath"]=> string(4) "/tmp" ["expires"]=> int(1404137986) } ["/home/julien.pauli/www"]=> array(4) { ["key"]=> int(5178407966190555102) ["is_dir"]=> bool(true) ["realpath"]=> string(22) "/home/julien.pauli/www" ["expires"]=> int(1404137986) 


何が見えますか? スクリプトへの完全なパスは、最初から部分的に許可されています。 ファイル/tmp/bar.phpは存在しないため、キャッシュエントリはありません。 ただし、 /tmpへのパス/tmp許可されているため、添付ファイルへの後続の各要求は、初回よりもわずかに高速になります。

realpath_cache_get()によって返される配列には、レコードのrealpath_cache_get()などの重要な情報が表示されます。 この値は、ファイルアクセス時間とrealpath_cache_ttl設定に基づいて計算されます。

keyフィールドは、許可されたパスのハッシュです。 FNVアルゴリズムのバリアントが使用されます。 これは、実際的な意味で必要になる可能性が低い内部情報です。 ハッシュは、 INT_MAXのサイズに応じて、intまたはfloatのいずれかINT_MAX

現在clearstatcache(true)が呼び出されている場合、この配列は無効化され、PHPは、以前にキャッシュされた要求された各ファイルに対して再びstat()システムコールを行います。

OPcodeキャッシュについて話しましょう


別の落とし穴の準備はできましたか?

realpathキャッシュは特定のプロセスに関連付けられており、共有メモリには保存されません。

これは、キャッシュアイテムが期限切れ、変更、またはキャッシュが手動でクリアされるたびに、実行中のプロセスごとにこれを実行する必要があることを意味します。 このため、ユーザーは多くの場合、OPCodeキャッシュを使用するサーバーにアプリケーションを展開するのが困難です。

通常、プロジェクトの計算中に何が起こりますか? 多くの場合、シンボリックリンクをあるバージョンから別のバージョンに、たとえば/www/deploy-aから/www/deploy-b置き換えるだけです。 そして、ここでは、OPcodeキャッシュ(少なくともOPCacheとAPC)が内部realpathキャッシュに依存していることを誰もが忘れています。 したがって、OPcodeのキャッシュメカニズムはシンボリックリンクの変更を認識せず、キャッシュが廃止されたときにのみキャッシュを更新します。 さて、あなたはすでにすべてを知っています:)

この副作用を防ぐための最良の解決策は、PHPワーカーのプールを別に用意し、バランサーをそれに切り替えることで、古いワーカーが正常にシャットダウンできるようにすることでした。 これにより、2つのバージョンを相互に分離できるため、無関係なキャッシュを使用できなくなります。 realpathキャッシュとOPCodeキャッシュを含むすべての環境が新しくなります。 このトリックは、少なくともLighttpdとNginxを使用している場合に利用可能です。 そして彼は本番環境での作業に成功しています。

終わり


realpathキャッシュについて数行書くように頼まれました。 最も可能性が高いのは、コードのレイアウト時に発生した問題です。 さて、あなたはそれがどのように機能し、どのように管理するかを知っています。

翻訳者からのPS:
古代の php-internalsのメーリングリストから
ただ考えて、clearstatcache()はキャッシュのリセットを強制すべきですか? ディレクトリツリーをその場で再構築する多くの状況を考えることはできませんが、PHPユーザーに何を期待すべきか決してわかりません。

Source: https://habr.com/ru/post/J266909/


All Articles