ホームページ  >  記事  >  バックエンド開発  >  Web の高速化: PHP セッションによる操作の速度低下を回避します。

Web の高速化: PHP セッションによる操作の速度低下を回避します。

WBOY
WBOYオリジナル
2016-06-23 13:58:311280ブラウズ

Web の高速化: PHP セッションによる実行速度の低下を回避します

1. 内容 - 同時アクセス、実行のブロック
1.1 セッションを使用しないでください

File Index.php:

<script type="text/javascript" src="http://code.jquery.com/jquery-2.0.3.min.js"></script><script type="text/javascript">$(document).ready(function(){$.ajax({url:"/ajax.php"});$.ajax({url:"/ajax2.php"});$.ajax({url:"/ajax3.php"});});</script>


ファイル ajax.php ajax2.php と ajax3.php の内容はすべて

<?phpsleep(1);echo "php script run";


各リクエストは 1 秒スリープします。 jq の ajax リクエストは非同期です。つまり、これら 3 つのリクエストは基本的に同時に発行されます。理論的には、ブラウザが 1 秒待って 3 つのインターフェイスすべてが戻るのが最良の状況です。

http://localhost にアクセスし、Chrome でテスト結果を表示します。

図 1.1 セッションなしのテスト結果

テスト結果は基本的に理論と一致しています

1.2 セッションを使用します

次に、ファイルを ajax に置きます.php 、 ajax2.php 、 ajax3.php はすべて

<?phpsession_start();sleep(1);echo "php script run";



これをやると何か違いはありますか?テスト結果を直接見てみましょう:

図 1.2 セッションを使用したテスト結果

各リクエストは 1 秒間しか実行されませんか? ajax2.php は 2 秒を消費し、ajax3.php は 3 秒を消費するのはなぜですか?

2. WHO -- セッション ロック

セッション ロックの正式な定義:

セッション データは通常、session_write_close() を呼び出す必要なくスクリプト終了後に保存されますが、同時書き込みを防ぐためにセッション データは 1 つのスクリプトのみにロックされます。

一般的な意味は、session_start() が呼び出されてから session_write_close() が呼び出されるか、スクリプトが終了するまで、セッション データはロックされ、同じ SESSIONID ユーザーからのリクエストです。ブロックされます。

図 1.2 のテスト結果から、ajax2.php と ajax3.php がそれぞれ 1 秒と 2 秒実行されたことがわかります。 session_start() 操作がブロッキングを引き起こしたことは明らかです。

index.phpがajax.php、ajax2.php、ajax3.phpに同時にアクセスすると、ajax.phpが先に実行されます。このとき、ajax2.phpとajax3.phpはajax.phpの解放を待っています。セッション ロック、両方とも 1 を消費します。

ajax.php が実行され、セッション ロックが解放されると、ajax2.php と ajax3.php が再びセッション ロックを競合します。同様に、ajax3.php はさらに 1 秒待機します。結果は次のようになります:

ajax.php は 1 秒を消費します

ajax2.php は 2 秒を消費します

ajax3.php は 3 秒を消費します

3、いつ - セッション ロックはいつトリガーされますか?

第 2 章のセッション ロックの定義セッション ロックは session_start() の開始時にトリガーされるため、ほとんどの既存の PHP フレームワーク (ネイティブ PHP セッションを使用する場合) では、セッション ロックが原因でユーザー リクエストがブロックされるという問題があります。 2 つのリクエストがあるとします。リクエスト A は結果を返すのに 3 秒かかり、リクエスト B は 10 ミリ秒しかかかりません。バックグラウンドが最初にリクエストを処理すると、リクエスト B は結果を返します。結果を返すまでにさらに 10 ミリ秒かかります。ただし、最適な状況は、リクエストが同時に開始され、リクエスト B が受信されて 10 ミリ秒後に返され、リクエスト A が受信されて 3 秒後に返されることです。

4. なぜ -- セッションの内部実行メカニズム

デフォルトでは、php セッションはサーバー側のストレージメディアとしてファイルを使用します。 PHP セッション モジュールのソース コードには、より重要な構造があります。


図 4.1 PHP セッション モジュールの構造 ps_module_struct

この構造内のいくつかの関数ポインターは、セッション操作のオープンとオープンにそれぞれ対応します。クローズ、読み取り、書き込み、破棄、GC リサイクルおよびその他の機能。これら 6 つの関数を見て、php の SessionHandlerInterface インターフェイス (バージョン >= php5.4) を思い浮かべますか?

図 4.2 php SessionHandlerInterface インターフェイス

このインターフェイスを session_set_save_handler とともに使用して、セッションのこれらの主要な操作を書き換えます。したがって、次のコードを使用して、リクエストのライフサイクルにおける php セッションの実行順序を調べることができます:

図 4.3 セッション実行順序のテスト コード

テスト結果:

テスト結果から、次のことができます。一般的なセッション実行を知る 連続して、 session_start() が呼び出されると、php は open 操作と read 操作を呼び出し、スクリプトの実行が完了した後 (php script run が出力されます)、 write 操作と close 操作を再度呼び出します。

ここで、PHP はセッションのオープンまたは読み取り操作中にセッション ロックをオンにし、書き込みまたはクローズ後にセッション ロックを解放するという大胆な推測を行ってもよいでしょう。この推測は、第 1 章のテスト結果とも一致します。

  为了验证我们的猜想,就需要去php的源码探个究竟了。在php源码文件/ ext / session / mod_files.c中可以看到默认session的6个重要操作的部分实现。在156行(点击打开链接),看到open操作有一个打开文件操作:flock(data->fd, LOCK_EX);该操作以互斥锁定的方式打开文件。在110行关掉文件close(data->fd);。

  看到这里,我们应该得到的结论是:

在默认情况下,所谓的php session锁其实就是文件锁

  所以,当我们使用session_set_save_handler来自定session操作,改用memcache或其他介质时,只要我们在SessionHandlerInterface的接口中没有锁的逻辑,那么session锁自然也不会存在。作者私下也做了这样的实验,实践证明也的确如此。

五、HOW -- 如何避免Session锁带来的阻塞现象

  首先,session锁不一定是坏事情,在一种情况下就非常好用,例如某接口对与同一个用户的请求默认同一时刻只能执行一次。这种时候,就可以用seesion_start()和session_write_close()把要阻塞的代码括起来。非常简单暴力实用。

  但是大部分时候我们还是要避免这种锁的存在,解决方案:

1、在用完session的时候就马上session_write_close()掉,释放session锁

2、采用没有锁的session操作,如章节4中所说的用session_set_save_handler来自定义一个没有锁的session操作。

3、再使用默认php session时,个人比较中意的一个方案:大部分情况下,我们对session的操作基本上都是读操作,写操作一般都比较少。这种时候,我们可以自己写一个session类。

构造函数:将session读入cache,关闭session锁

写操作:打开session锁,写入值,关闭session锁

读操作:直接读cache

部分代码如下:

//将session读入全局变量$_SEESIONstatic private function init(){if(self::$not_init){ session_start(); session_write_close(); self::$not_init = false;}}


//读sessionstatic public function get($name){self::init();return $_SESSION[$name];}


//写sessionstatic public function set($name, $val){session_start();$_SESSION[$name] = $val;session_write_close();}


注意:如果是写操作频繁的操作,就不适合使用该方法。

 

 


声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。