Upgrade to Pro — share decks privately, control downloads, hide ads and more …

実践!PHPWebアプリケーション パフォーマンスチューニング/Practice_PHP_Web_Application_Performance_Tuning

実践!PHPWebアプリケーション パフォーマンスチューニング/Practice_PHP_Web_Application_Performance_Tuning

速いは正義、アプリケーションは速くあるべきです

話者はPHPerKaigi2021にてパフォーマンスチューニングに関する登壇を行いました
https://speakerdeck.com/seike460/php-web-application-performance-tuning

時間の関係上、理論のみのお話となってしまったのですが、今回は加えて実践結果に触れていきます。
そこで説明した理論を実際に適用すると、どのようにパフォーマンスが向上していくかを
例題のPHPアプリケーションに対して計測を行い、原因を特定して、改善した実際の様子と合わせてお伝えします

E7151ab8219e76672f7a7691dd2c88e6?s=128

shiro seike

May 29, 2021
Tweet

Transcript

  1. 実践!PHPWebアプリケーション
 パフォーマンスチューニング
 PHPカンファレンス沖縄2021
 2021.5.29
 清家史郎
 1


  2. 速いは正義、アプリケーションは速くあるべきです
 インターネットの高速化やハードウェア性能の向上により 
 アプリケーションはよりパフォーマンスを求められるようになってきています 
 PHPの力を最大限発揮して、より高速なインターネット体験を提供しましょう 
 2


  3. 自己紹介
 清家 史郎
 @seike460
 - ID
 - GitHub:seike460 
 -

    Twitter:@seike460 
 - Work at
 - 株式会社 Fusic (フュージック) 
 技術開発本部/技術開発第一部門 
 - チームリーダー/エバンジェリスト/プリンシパルエンジニア 
 - Skill
 - PHP/Go/AWS 
 - Personal
 - PHPカンファレンス福岡2020 幻の実行委員長 
 - Fukuoka.php Organizer 
 - 琉球大学工学部 電気電子工学科卒 
 3

  4. Agenda
 4
 1. パフォーマンスチューニング概要 
 2. PHPにおけるチューニング要素
 3. 要素ごとの計測
 4.

    計測、考察、対策のサイクルを回す 
 5. まとめ

  5. 01 パフォーマンスチューニング
 概要


  6. なぜパフォーマンチューニングをするのか
 6
 
 時は2021年、世界のインターネット高速化は留まるところを知りません。
 コンピュータの性能向上、ネットワーク回線自体の高速化も相まって、
 システムが遅いことは利用率にそのまま影響を与える致命的な弱点になりうる要素です。
 
 僕たち技術者はこの時代に沿ったアプリケーションのパフォーマンスを出すことは
 システムとして実現すべき使命の一つであると言えます。
 


    今回は私達が取り組むべきパフォーマンスチューニングの具体的な内容に触れていきます。

  7. ハイパフォーマンスであることの定義
 7
 • スループット
 単位時間当たりの処理能力
 一般的に1秒間に返せるリクエストを表すrps(Request Per Second)が用いられます
 • レイテンシー


    リクエストからレスポンスまでに生じる通信の遅延時間
 一般的に単純な時間であるmsが用いられます

  8. スループットを上げるためには
 8
 パフォーマンスを下げる原因となる処理能力が低いポイント 
 「ボトルネック」を探し、改善することが鍵となります。 
 経験を積んでいくと「この現象はこれっしょ」という推測が立つようになる 
 この長年の勘に頼ることは再現性がなく不確実 


    - Webサーバ?
 - PHP?
 - DB?

  9. 推測するな、計測せよ
 ボトルネックを特定するまで推測で行動しない 
 計測した「データ」を元に原因を特定して、対策を講じる 
 9


  10. 長年の勘ではなく、理論に従う
 10
 信頼のおけるデータを正しく理解し、理論的な対策を行う事で 再現性を確保する。
 そのようなパフォーマンス改善には次のステップを踏む必要があります。 
 - 現在の状況を把握するために、計測を行う 
 -

    計測で集まったデータを元に考察して、原因を特定する 
 - 特定した原因に対して、正しく改善する 
 愚直にこのサイクルを回す事が重要

  11. 02 PHPにおける
 チューニング要素


  12. PHPWebアプリケーション構成要素
 12
 一般的なWebアプリケーションの構成は大きく分類して 
 以下の用に分類される事が多く、それぞれに対してパフォーマンスを改善を行います 
 
 - Webサーバ
 -

    アプリケーションサーバ(PHP)
 - DBサーバー

  13. Webサーバ
 13
 ウェブブラウザに対して、HTMLや画像などの表示を提供したり、 
 アプリケーションサーバへのリバースプロキシを受け持ちます。 
 ※Webサーバーがボトルネックになることは少ないです。 


  14. アプリケーションサーバー
 14
 Webサーバからのリクエストに応じて処理を行い、 
 DBサーバとやり取りをしてレスポンスを構築する 


  15. DBサーバー
 15
 リレーショナルデータベース(RDB) 
 PHPからの要求に応じて、データ登録、更新、削除、参照を行います 
 ※ストレージとしてRDB以外を選択することもあります 


  16. ISUCON5 予選
 16
 ISUCON(iikanjini speed up contest)とはお題となるWebサービスを、 
 決められたレギュレーションの中で限界まで高速化を図るチューニングバトル。 


    
 今回はその第5回予選の問題を利用して、チューニングの様子をお話致します。 

  17. 03 要素ごとの計測


  18. ベンチマーク計測
 18
 スループットを測定するためのツールとして 
 普段はLocustやApacheBench等のツールを利用します。 
 
 今回はISUCONが用意したベンチマークツールがありますので 
 そちらを利用して速度(スコア)の計測を行います。

    
 
 ※今回はコードの変更に関しては基本的に触れません。 
  インフラミドルウェアをチューニングすることで対応します。 

  19. サーバ負荷による
 ボトルネックサーバーの特定


  20. 負荷の確認
 20
 スループットの閾値に到達した際に、閾値を決定づけるボトルネックを探します。 
 ボトルネックになってるサーバを探す際に各サーバのリソース状況を参照します。 
 
 - 処理待ち状況、CPUの負荷
 -

    ディスクの負荷
 - メモリの負荷

  21. 処理待ち状況、CPUの負荷
 21
 サーバの処理待ちの状況を確認するにはLoad Averageを確認します。 
 初期アプローチとしては処理コストが少ない「uptime」を使うのが適切です。 
 
 CPU状況も合わせて確認するには「htop」を使うと便利です。 


    CPUのコア数以上の処理待ちがある場合、 
 そのサーバーがボトルネックになっている可能性が高いです。 

  22. ディスクI/Oの状況
 22
 ディスクはデータの読み書きをする上で、一番低速な機器となります。 
 ディスクI/Oが大きい時は、パフォーマンス劣化の原因になっている事が多いです。 
 
 「vmstat」でディスクの読み込み、書き込みを確認する事が出来ます(bi / bo)

  23. メモリの負荷
 23
 メモリを100%使い切っている場合、スワップメモリを利用することになります。 
 スワップメモリは、ディスクをメモリ領域として確保して利用する為、 
 実質ディスクI/Oを発生させる事になります。 
 ディスクは非常に低速なデータ領域なのでボトルネックになる可能性が高いです。 


    
 「htop」や「vmstat」でスワップメモリの状況を確認出来ます。 

  24. ボトルネック処理の特定


  25. PHPのパフォーマンス測定
 25
 PHPに原因がありそうな事がわかった場合、 
 どの関数で問題が発生しているのかをプロファイラーを用いて確認します。 
 
 OSSになっているXdebugが比較的気軽に導入することが出来ます。 
 


    費用に余裕がある場合は、DatadogやBlackfire等の 
 SaaSを利用することも選択肢にあがります。 

  26. Xdebug x Webgrind
 26
 Xdebugにてプロファイルを取得して 
 プロファイル解析が可能なWebgrindを利用し、Graphvizを介して可視化します。 
 具体的な利用方法はFusic Tech

    Blogをご参照ください。 
 PHP8のプロファイリングにXdebug3とWebgrindを使う 
 
 右図の用に図が生成出来ます。 
 - どの関数から呼び出されているか 
 - 処理を専有している関数
 - 繰り返し実行されている回数
 N+1問題等を発見して解消していきます 

  27. RDBでのパフォーマンス測定
 27
 PHPプロファイリングにて時間がかかっているのがRDB操作の関数であったり、 
 DBサーバのリソース枯渇している場合、RDBにボトルネックがあることがわかります 
 
 スロークエリなどを元に、原因となるSQLを特定し 
 単一のSQLが原因になっているのであれば実行計画を見ていきます。

    
 DB設計ミスなのか、Index設定ミスなのか、 
 もしくは必要なリソースをRDB割り振れていないのかを判断していきます。 

  28. 計測、考察、対策
 28
 このようにサーバのリソース計測したり、プロファイラを利用することで 
 WebサーバやPHP、RDBの状態を正しく認識します。 
 
 現状を正しく認識したら原因について考察し、対策を実施します。 
 


    対策後、新たなボトルネックが判明したら、再度計測を行い繰り返し改善します。 

  29. 04 計測、考察、対策の
 サイクルを回す


  30. ファーストベンチマーク


  31. サーバーの情報
 31
 今回はAWSのM5.large
 vCPU : 2cpu
 メモリ:8GiB
 を以下の分類で割り振りました
 - ベンチマークサーバ


    - Web-PHPサーバ
 - DBサーバ

  32. ベンチマーク結果
 32
 差分を明確化するため、
 チューニングをしていない状況でベ ンチマークを実施します。
 
 スコアとしては
 「754.6」でした。
 
 更にスコアを伸ばすことを


    目標にチューニングします。

  33. 初期のベンチマーク
 33
 Nginx + PHP ほぼ負荷なし MySQL メモリ枯渇なし LA増加なし CPUに負荷

  34. スロークエリのチェック


  35. スロークエリ設定
 35
 デフォルトではスロークエリは表示されない為、 
 以下の設定を投入してMySQLを再起動し、遅いクエリを炙り出して行きます。 


  36. 実行計画
 36
 炙り出されたスロークエリに対して、実行計画を取ります。 
 
 Indexが使えていない様子が確認出来ましたので、 
 Indexを貼る事で解消しないかを考えます。 
 


    WHEREの対象になっている、「one」と「another」にIndexを貼ります。 

  37. 実行計画改善後
 37
 Index設定後の実行計画も変わっており、 
 正しくIndexが利用できている事が確認出来るので、 
 一度ベンチマークを実行して、スコアが向上するかを確認します。 


  38. ベンチマーク結果
 38
 スコアが「754.6」→「1240.0」に上がりました。 
 先程のチューニングが効いた事になります。 
 
 この時点でもまだスロークエリは発生しているので、 
 スロークエリが出なくなるまでIndexの調整をして、

    
 再度ベンチマークを行います。 

  39. Index改善後
 39
 スコアが「1240.0」→「2342.1」に上がりました。 
 
 またスロークエリが発生しなくなりました。 
 コードを変更することなく、 
 3倍のスコアを叩き出した事になります。

    
 
 ここでボトルネックが移っていないかを確認します。 

  40. スロークエリ改善後のベンチマーク
 40
 Nginx + PHP LA微増 CPU微増 メモリ微増 MySQL LA増加なし

    メモリ枯渇なし CPUに負荷低下
  41. MySQLパラメータチューニング
 41
 パラメータチューニングに関しては、 
 次の記事とスライドが非常に参考になりますのでご参考にしてください。 
 ◎僕が心の拠り所にしているQiita MySQLパフォーマンスチューニング -my.cnfの見直し- 


    ◎@mamy1326 さん作成の元ネタ
 初めてのMySQLサーバーチューニング -データベースは怖くない!- 
 
 こちらの記事を参考に、次ページの様にMySQLのパラメーターを追加します。 

  42. MySQLチューニング
 42
 主にメモリを限界まで利用する様に
 パラメーターをチューニング
 
 この状態でベンチマークを実施します。


  43. ベンチマーク結果
 43
 スコアが「2342.1」→「3697.6」に上がりました。 
 
 コードを変更することなく、 
 5倍のスコアを叩き出した事になります。 
 


    ここでボトルネックが移っていないかを確認します。 

  44. パラメーターチューニング後のベンチマーク
 44
 Nginx + PHP LA微増 CPU増加 メモリ変化なし MySQL LA増加なし

    メモリ使用率増加 CPUに負荷低下
  45. PHP-FPM
 45
 PHPからMySQLに対して
 負荷を掛けきれていない
 
 長年の勘が働き、
 PHPのプロセス数を
 増やせば良いはずと判断
 
 PHPのプロセス数を


    増やして負荷を掛ける

  46. ベンチマーク結果
 46
 スコアが「3697.6」→「3707.2」 
 誤差範囲の増加
 
 長年の勘が外れた為、しっかり計測する事に戻ります 
 
 PHPの状況を知るために、

    
 Xdebugによるプロファイリングを取ります。 

  47. # cd /usr/local/src # wget https://www.php.net/distributions/php-8.0.6.tar.gz # tar zxf php-8.0.6.tar.gz

    # cd php-8.0.6 # ./configure --enable-fpm --with-mysqli --enable-mbstring --with-pdo-mysql=mysqlnd # make -j2 # make install Installing shared extensions: /usr/local/lib/php/extensions/no-debug-non-zts-20200930/ Installing PHP CLI binary: /usr/local/bin/ Installing PHP CLI man page: /usr/local/php/man/man1/ Installing PHP FPM binary: /usr/local/sbin/ Installing PHP FPM defconfig: /usr/local/etc/ Installing PHP FPM man page: /usr/local/php/man/man8/ Installing PHP FPM status page: /usr/local/php/php/fpm/ ~省略〜 Installing PDO headers: /usr/local/include/php/ext/pdo/ # php -v PHP 8.0.6 (cli) (built: May 29 2021 06:49:02) ( NTS ) Copyright (c) The PHP Group Zend Engine v4.0.6, Copyright (c) Zend Technologies OPCahceが読み込まれてないので読み込ませる PHP8のインストール
 47

  48. # php -i | grep ini Configuration File (php.ini) Path

    => /usr/local/lib # vim /usr/local/lib/php.ini OPcacheの有効化
 48
 # php -v PHP 8.0.6 (cli) (built: May 29 2021 06:49:02) ( NTS ) Copyright (c) The PHP Group Zend Engine v4.0.6, Copyright (c) Zend Technologies with Zend OPcache v8.0.6, Copyright (c), by Zend Technologies
  49. # vim /etc/systemd/system/isuxi.php.service PHP-FPMの入れ替え
 49
 # systemctl restart isuxi.php.service Warning:

    isuxi.php.service changed on disk. Run 'systemctl daemon-reload' to reload units. # systemctl daemon-reload # systemctl restart isuxi.php.service
  50. ベンチマーク結果
 50
 スコアが「3707.2」→「4564.7」 
 
 さすがPHP8、速い
 PHP8の目玉機能でもあるJITを有効化します。 


  51. # vim /usr/local/lib/php.ini JITの有効化
 51
 # systemctl restart isuxi.php.service

  52. ベンチマーク結果
 52
 スコアが「4564.7」→「5497.8」 
 
 ここまで効果が出る事は予想外でしたが、 
 JITによる効果は大きそうです。 
 


    ここでボトルネックが動いていないかを確認します。 
 

  53. PHP8入れ替え後のベンチマーク
 53
 Nginx + PHP LA変化なし CPU低減 メモリ変化なし MySQL LA増加なし

    メモリ使用率増加 CPUに負荷増加
  54. # cd /usr/local/src # wget https://xdebug.org/files/xdebug-3.0.4.tgz # tar zxf xdebug-3.0.4.tgz

    # cd xdebug-3.0.4 # phpize # ./configure --enable-xdebug # make -j2 # make install ~ 省略 ~ | NOTE: Please disregard the message | You should add "extension=xdebug.so" to php.ini | that is emitted by the PECL installer. This does not work for | Xdebug. | +----------------------------------------------------------------------+ Xdebugのインストール
 54

  55. プロファイリング用ベンチマーク
 55
 スコアが「5497.8」→「3322.2」 
 
 プロファイリング情報を収集しながら動作するのと、 
 100k 〜 1Mサイズのプロファイリングファイルの

    
 書き出しを行うので動作としてはかなり影響が出ます 
 
 運用時はXdebugを切ることを忘れないように 
 気をつける必要があります。 

  56. ▪描画用ツール # apt-get install graphviz ▪webgrind # cd /usr/local/src #

    git clone https://github.com/jokkedk/webgrind.git # php -S 0.0.0.0:8080 index.php PHP 8.0.6 Development Server (http://0.0.0.0:8080) started webgrindの設定
 56

  57. 関数コールの描画
 57
 db_executeによる、SQLの実行が52回も行われており、 get_userという関数がtemplatesファイルから50回も呼ばれている。 こちらの処理を見直して SQL発行を低減する事で、 処理の改善が見込める事が考察出来ます。 ※実際の改善はPHPの処理変更になるので割愛します。

  58. Webサーバー


  59. Nginxパラメータチューニング
 59
 Webサーバーはいかにリクエストを受け付けるのかが鍵 
 ※メモリの消費に直結するのでサーバーリソースと相談しながら 
 - worker_processes
 - 受け付けるNginxのプロセス数を決める。auto

    = CPU数がオススメ 
 - worker_connections 
 - ひとつのワーカーが開けるConnection数 
 - worker_rlimit_nofile 
 - 開けるファイルディスクリプタの上限値 
 - Unix ドメインソケット 
 - Unixドメインソケットを利用する事で接続時のオーバーヘッドを減らす 

  60. ファイルキャッシュ
 60
 ◎PHP以降の処理が重い
 HTMLやCSSをWebサーバーにキャッシュしてしまうのも手です。 
 PHPでSSRをする場合、結果が変わらない場合はWebサーバー側で 
 PHPのレンダリング結果をメモリ上にキャッシュしてしまい、 
 PHPへのRequestを減らすことで、低負荷で高速に結果を返すことが可能です。

    
 
 但しキャッシュクリアのタイミングを失敗すると想定している結果を返せず、 
 手動でキャッシュをクリアするなど障害が発生してしまう可能性があります。 

  61. Webサーバーのアクセスログ解析
 61
 今回行っている範囲ではWebサーバーは特にボトルネックにはなりませんでした。 
 ですが、やれることが無い訳ではありません。 
 アクセスログを解析することで、よりシステムを深く計測する事が出来ます。 
 
 @matsuuさんのkataribeを利用してカジュアルにアクセスログを解析を行えます。

    
 
 kataribeだけでなく、ISUCONの環境をかんたんに作れる AWSのAMIや、
 ansible、vagrantを公開してくれてる神です。

  62. kataribe用の設定
 62
 次の様に時間付きのLogFormatを用意して、 
 kataribeのデフォルト設定にて解析できるようにする 
 設定ファイルを生成して、設定ファイルが置いてあるディレクトリにて、 
 kataribeに対してアクセスログを投げる 


    # kataribe -generate
 # cat /var/log/nginx/access.log | kataribe
 その後、nginxを再起動すると共にnginxのアクセスログを初期化する 
 # cat /dev/null > /var/log/nginx/access.log

  63. 集計した結果から対策を講じる
 63


  64. 05 まとめ


  65. まとめ
 Point 3
 ボトルネックを解消する方法は様々な方法があります。知識を蓄え適切に対処しましょう。 
 65
 推測するな、計測せよ。再現性のある対策を行いましょう。
 Point 1
 Point

    4
 ISUCON11はそろそろ申し込みです!みなさん、高速化やっていきましょう! 
 
 リソースを正しく把握して、ボトルネックを特定しましょう。
 Point 2

  66. ご清聴いただきありがとうございました
 Thank You We are Hiring !
 https://recruit.fusic.co.jp/