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

PHP Web Application Performance Tuning

PHP Web Application Performance Tuning

E7151ab8219e76672f7a7691dd2c88e6?s=128

shiro seike

March 27, 2021
Tweet

Transcript

  1. PHPWebアプリケーション
 パフォーマンスチューニング
 PHPerKaigi 2021
 2021.3.27
 清家史郎
 1


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


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

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

  4. Agenda
 4
 1. PHPWebアプリケーション構成要素 
 2. 計測
 3. パフォーマンス改善
 4.

    まとめ
 5. Appendix

  5. 01 PHPWebアプリケーション
 構成要素


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

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

  7. Webサーバ
 7
 ウェブブラウザに対して、HTMLや画像などの表示を提供したり、 
 アプリケーションサーバへのリバースプロキシを受け持ちます 


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


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


  10. ハイパフォーマンスであること
 10
 スループット
 単位時間当たりの処理能力
 一般的に1秒間に返せるリクエストを表すrps(Request Per Second)が用いられます
 レイテンシー
 リクエストからレスポンスまでに生じる通信の遅延時間
 一般的に単純な時間であるmsが用いられます


  11. ボトルネック
 11
 パフォーマンスを下げる原因となる処理能力が低いポイント 
 様々なボトルネックがあります
 - Webサーバパラメータ
 - PHPコードの問題
 -

    DB設計ミス
 経験を積んでいくと「この現象はこれっしょ」という推測が立つようになる 
 この長年の勘に頼ることは再現性がなく不確実 

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


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

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

  14. 02 計測


  15. サーバ負荷の確認


  16. スループット測定
 16
 スループットを測定するためのツールとして今回はLocustを利用します。 
 Python製ではありますが、少しづつ負荷を上昇させることが出来るため、 
 そのWebアプリケーションのスループットの閾値を測定することが出来ます。 


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

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

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


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

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

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


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

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


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


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

  23. Xdebug x Webgrind
 23
 Xdebugにてプロファイルを取得して 
 プロファイル解析が可能なWebgrindを利用し、Graphvizを介して可視化します。 
 具体的な利用方法はこちらの記事をご確認ください。
 


    すると右図の用に図が生成出来ます。 
 - どの関数から呼び出されているか 
 - 処理を専有している関数
 - 繰り返し実行されている回数
 
 N+1問題等を発見して解消していきます 

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

    
 もしくは必要なリソースをRDB割り振れていないのかを判断していきます。 

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


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

  26. 03 パフォーマンス改善


  27. DB


  28. 実行計画①
 28
 ◎あるSELECT SQLの実行が遅い場合
 EXPLAIN(ANALYZE)をSELECTの前に付けて、実行計画を取ります。
 - Seq Scan … 全レコード探索
 - Index

    Scan … Indexとテーブルに交互にアクセスして探索
 - Bitmap Scan … メモリ上に展開されたBitmapを使ってAndOrの探索
 - Index Only Scan … Indexのみからデータを探索(条件有)
 意図したIndexが利用できているかを確認します。

  29. 実行計画②
 29
 テーブル結合にも注目します。
 Nested Loop … 外部テーブルをスキャンして、内部テーブルに結合 
 Indexの概念があるので、意図したIndexが効いてるかを考慮 
 Merge Join … 外部テーブル、内部テーブルのキーをソートして結合

    
 Indexがないテーブルではコストが大きい 
 Hash Join … メモリ上にハッシュテーブルを作成してハッシュを比べて結合 
 メモリ上にハッシュテーブルが収まれば高速に結合 

  30. 非正規化
 30
 必要以上にJoinが発生してしまう場合などは、 
 正規化しているテーブルの非正規化も検討を行います。 
 
 データの追加・更新・削除が発生しないテーブルに関して、 
 非正規化が有効で、Joinを減らすことでSQLのコストを下げることが出来ます。

  31. パラメータチューニング
 31
 ◎DBサーバのリソースが有向活用出来ていない場合 
 CPUの使用率は高いが、メモリの使用率が低い場合などは、 
 RDBにメモリを十分に与える事で解決する可能性があります。 
 
 メモリを利用するソートやジョインが高速化され、

    
 不必要なディスクI/Oを発生させずに結果を返すことが出来ます。 
 ディスクにデータを書き込むタイミングを調節することで、 
 ディスクI/Oを減らす事が出来ます。 
 
 RDBパラメータチューニングの内容はここでは話しきれないので、 
 Appendixを是非ご覧ください

  32. Source、Replica
 32
 ◎DBサーバ複数台構成が組める場合 
 データ更新のディスクのI/Oが原因でSELECTが遅くなってしまう可能性があります。 
 
 そもそもの設計の話になりますが複数台構成を取り、データの更新はSourceに行い、 
 データの参照はReplicaに行うことでデータの参照を遅延させない事が出来ます。

  33. PHP


  34. PHPバージョンアップ
 34
 ◎問答無用でパフォーマンスアップの可能性 
 PHPは日々進歩しています。
 PHPバージョンアップにより高速化する可能性は十分にあります。 
 SwooleTwitterアカウント (@php_swoole)より引用


  35. オペコードキャッシュ
 35
 オペコードキャッシュの機構を使う事で更にPHPを高速化させる事が可能です。 
 OPcacheを利用することで、コンパイルしたコードをメモリ上にキャッシュし、 
 高速化することが出来ます。
 
 初期設定では定期的にPHPコードの変更をチェックして、 


    変更があればファイルから再読み込みが行われます。 

  36. JIT
 JIT(ジャストインタイム・コンパイラー) 
 PHP8から導入された機能
 JITはOPCcacheの中間コードの重要な部分をネイティブコードまで変換します。 
 コンパイルをバイパスするおかげで、 
 パフォーマンスとメモリ使用状況を大幅に改善させる事が可能です。 


    36
 php.netより引用

  37. ユーザーキャッシュ
 37
 ◎DBサーバ負荷が高い
 ユーザーキャッシュの機構を使いKey Valueのキャッシュを利用することで、 
 ディスクアクセスを減らす事が可能です。 
 但しディスクのデータが更新された際のキャッシュクリアが必要になります。 


    - APCu
 - PHP単体で利用
 - Redis
 - キャッシュサーバーとして複数台で利用 

  38. PHP
 38
 ◎Webサーバーからのリクエストを受けきれない 
 フォークするプロセス数を増やす事で受け取るリクエスト数を増加させます。 
 - pm.max_children:同時に処理するプロセスの最大数 
 -

    pm.start_servers:起動時に生成されるプロセス数 
 - pm.min_spare_servers:待ち状態の子プロセス最小数 
 - pm.max_spare_servers:待ち状態の子プロセス最大数 
 サーバーのリソースとmemory_limitの上限に相談して、 
 プロセスを増やすことで処理性能を上げる事が出来ます。 
 Apache + mod_phpの場合はMaxClientの設定に相当します。 
 パラメータチューニングの詳細は Appendixをご覧ください。

  39. Webサーバー


  40. Webサーバーのパフォーマンス
 40
 ◎クライアントからのリクエストを受けきれない 
 何らかの要因でスパイクアクセスが発生した際に、 
 WebサーバはそのHTTPリクエストを受け切る必要があります。 
 ApacheのMaxClientや、Nginxのworker_processesを調整します。 


    OSパラメータであるulimit等の値を調節して、 
 Too Many Open Fileが発生しない用にするなどの調整が必要になります。 
 設定例はAppindexへ

  41. リソース圧縮
 41
 ◎ネットワークトラフィック
 HTMLやCSS等のリソースファイルを返還する際に、 
 データを圧縮して転送してブラウザに解凍してもらう事で 
 ネットワークトラフィックを少なくすることも出来ます。 
 


    ブラウザで解凍する必要がありますので、 
 クライアントに若干の負荷をかける事になりますが、速度の向上に繋がります。 

  42. HTTP2
 42
 ◎HTTP/2を利用する
 利用するプロトコルをHTTP/2に変更します。 
 1つのコネクション内で同時に並行して複数のリクエスト/レスポンスを処理出来る為 
 複数のリソースファイルを1つのコネクションで返す事が出来ます。 


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

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

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

    4
 話せないことがたくさんあった、Appendix見ていってください 
 
 リソースを正しく把握して、ボトルネックを特定しましょう。ディスクアクセスは遅い。
 Point 2

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


  46. Appendix
 05

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

    = CPU数がオススメ 
 - worker_connections 
 - ひとつのワーカーが開けるConnection数 
 - worker_rlimit_nofile 
 - 開けるファイルディスクリプタの上限値 

  48. PHPパラメータチューニング
 48
 Nginxからプロキシされたリクエストを受け切る必要がある 
 僕はパフォーマンスを追い求める時(ISUCON時など) 
 サーバがリソース許せばプロセスフォークする負荷を発生させない為に、 
 待機プロセスを最大化しておく(1プロセスがmemory_limit分メモリを使うのに注意) 


    max_children * memory_limit ≒ サーバーメモリ + OS稼働分 + 余白 
 FastCGI Process Manager (FPM):設定 
 pm.max_children、pm.start_servers:起動、最大のプロセスを制御 
 pm.min_spare_server、pm.max_spare_server:フォーク後に待ち受けるプロセスを制御 
 ※mod_phpの場合はMaxClientsなどが対応します。 

  49. RDBパラメータチューニング
 49
 詳しい内容は下記から得た知識になります。 
 ◎僕が心の拠り所にしているQiita MySQLパフォーマンスチューニング -my.cnfの見直し- 
 ◎@mamy1326 さん作成の元ネタ


    初めてのMySQLサーバーチューニング -データベースは怖くない!- 
 こちらを見て是非理解を深めてください。 
 
 次のページからはPostgreSQLの設定をいくつかご紹介します 

  50. RDSパフォーマンスチューニング
 50
 増やしたPHPのリクエストを受け切る為に接続数を上げます(max_connections) 
 ただしその分だけメモリとディスクI/Oが増加する恐れがあるため、 
 あまりにも多くし過ぎると接続を許しながら 
 LoadAverageをひたすら増加させる恐れがあります。 


    潔くtoo many clientsを出してLoadAverageの過剰な増加を防ぐのも一手です。 
 ※PHP側で正しく判定する必要が出てきます。 

  51. RDSパフォーマンスチューニング
 51
 利用するメモリを増やすことでメモリキャッシュを行ったり、
 ソートやジョインで利用する際のメモリを増やす事で高速化を行います。
 
 effective_cache_size … 単一の問い合わせで利用できるディスクキャッシュの実効容量
 shared_buffers … データベースサーバが使用する共有メモリバッファ
 work_mem … 一時ディスクファイルに書き込む前に、
         内部並べ替えとハッシュテーブル操作が使用するメモリ容量


  52. RDSパフォーマンスチューニング
 52
 ログ先行書き込み(WAL)はデータの一貫性を確実にするための標準的な手法です。
 ディスクに書き込む前にメモリ上に保管しておく事が出来、
 メモリに保持する量や頻度を調節することにより、ディスクへの負荷を低減出来る
 checkpoint_timeout、max_wal_size、wal_buffers(-1で大抵の場合妥当な結果となる)
 等が調整の対象となる。
 29.4. WALの設定


  53. 距離
 53
 データ転送も距離の問題があります。 
 データやコンテンツとの通信の距離を短く設定する為にDNSやCDNで 
 高速化の仕組みを提供します。 
 - DNS:位置情報ルーティングポリシー(Route53など)

    
 - CDN:エッジロケーション(Akamai、CloudFrontoなど) 

  54. CDN
 54
 CDNはコンテンツキャッシュの機能も有するので、 
 コンテンツをオリジン(配信元)から受け取った後にキャッシュして、 
 後続のサーバーへの負荷を減らすことが出来ます。(例のごとくTTLに注意) 


  55. JAMstack
 55
 JavaScript/APIs/Markupの頭文字をとったフロントエンドスタック (PHPでは無いですが)
 FusicのTechBlogはJAMstackを利用したSSGで運用しています。 
 パフォーマンス、スケーラビリティ、セキュアなブログ・サイトを運用出来てます。 
 
 弊社の浦田の登壇資料が分かりやすいのでご参照ください。

    
 

  56. フロントエンド
 56
 本筋とはズレますが、フロントエンドのチューニングもあります。 
 - Rendering
 - CSS(CSSOMツリー)
 - HTML(DOMツリー)


    - Scripting
 - JavaScript
 - Painting
 - 2Dグラフィックエンジン、ピクセル描画など 
 私が解説出来るほど収めておらず次の本をオススメします。 
 Webフロントエンド ハイパフォーマンス チューニング 
 (著:久保田 光則)にて詳しく解説されています。 
 ここでも開発者ツールのパフォーマンスタブを利用した 
 「推測するな、計測せよ」という思想について触れられています。