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

PHP Web Application Performance Tuning

PHP Web Application Performance Tuning

shiro seike

March 27, 2021
Tweet

More Decks by shiro seike

Other Decks in Programming

Transcript

  1. PHPWebアプリケーション

    パフォーマンスチューニング

    PHPerKaigi 2021

    2021.3.27

    清家史郎

    1


    View full-size slide

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

    インターネットの高速化やハードウェア性能の向上により

    アプリケーションはよりパフォーマンスを求められるようになってきています

    PHPの力を最大限発揮して、より高速なインターネット体験を提供しましょう

    2


    View full-size slide

  3. 自己紹介

    清家 史郎

    @seike460

    - ID

    - GitHub:seike460 

    - Twitter:@seike460 

    - Work at

    - 株式会社 Fusic (フュージック) 

    技術開発本部/技術開発第一部門 

    - チームリーダー/エバンジェリスト/プリンシパルエンジニア

    - Skill

    - PHP/Go/AWS 

    - Personal

    - PHPカンファレンス福岡2020 幻の実行委員長 

    - Fukuoka.php Organizer 

    - 46fm パーソナリティ 

    3


    View full-size slide

  4. Agenda

    4

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

    2. 計測

    3. パフォーマンス改善

    4. まとめ

    5. Appendix


    View full-size slide

  5. 01
    PHPWebアプリケーション

    構成要素


    View full-size slide

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

    6

    一般的なWebアプリケーションの構成は大きく分類して

    以下の用に分類される事が多く、それぞれに対してパフォーマンスを改善を行います


    - Webサーバ

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

    - DBサーバー


    View full-size slide

  7. Webサーバ

    7

    ウェブブラウザに対して、HTMLや画像などの表示を提供したり、

    アプリケーションサーバへのリバースプロキシを受け持ちます

    View full-size slide

  8. アプリケーションサーバー

    8

    Webサーバからのリクエストに応じて処理を行い、

    DBサーバとやり取りをしてレスポンスを構築する

    View full-size slide

  9. DBサーバー

    9

    リレーショナルデータベース(RDB)

    PHPからの要求に応じて、データ登録、更新、削除、参照を行います

    ※ストレージとしてRDB以外を選択することもあります

    View full-size slide

  10. ハイパフォーマンスであること

    10

    スループット

    単位時間当たりの処理能力

    一般的に1秒間に返せるリクエストを表すrps(Request Per Second)が用いられます

    レイテンシー

    リクエストからレスポンスまでに生じる通信の遅延時間

    一般的に単純な時間であるmsが用いられます


    View full-size slide

  11. ボトルネック

    11

    パフォーマンスを下げる原因となる処理能力が低いポイント

    様々なボトルネックがあります

    - Webサーバパラメータ

    - PHPコードの問題

    - DB設計ミス

    経験を積んでいくと「この現象はこれっしょ」という推測が立つようになる

    この長年の勘に頼ることは再現性がなく不確実

    View full-size slide

  12. 推測するな、計測せよ

    ボトルネックを特定するまで推測で行動しない

    計測した「データ」を元に原因を特定して、対策を講じる

    12


    View full-size slide

  13. 長年の勘ではなく、理論に従う

    13

    信頼のおけるデータを正しく理解して対策を行う事で再現性を確保する。

    パフォーマンス改善には次のステップを踏む必要があります。

    - 現在の状況を把握するために、計測を行う

    - 計測で集まったデータを元に考察して、原因を特定する

    - 特定した原因に対して、正しく改善する

    愚直にこのサイクルを回す事が重要


    View full-size slide

  14. サーバ負荷の確認


    View full-size slide

  15. スループット測定

    16

    スループットを測定するためのツールとして今回はLocustを利用します。

    Python製ではありますが、少しづつ負荷を上昇させることが出来るため、

    そのWebアプリケーションのスループットの閾値を測定することが出来ます。

    View full-size slide

  16. 負荷の確認

    17

    スループットの閾値に到達した際に、閾値を決定づけるボトルネックを探します。

    ボトルネックになってるサーバを探す際に各サーバのリソース状況を参照します。


    - 処理待ち状況、CPUの負荷

    - ディスクの負荷

    - メモリの負荷


    View full-size slide

  17. 処理待ち状況、CPUの負荷

    18

    サーバの処理待ちの状況を確認するにはLoad Averageを確認します。

    初期アプローチとしては処理コストが少ない「uptime」を使うのが適切です。


    CPU状況も合わせて確認するには「htop」を使うと便利です。

    CPUのコア数以上の処理待ちがある場合、

    そのサーバーがボトルネックになっている可能性が高いです。

    View full-size slide

  18. ディスクI/Oの状況

    19

    ディスクはデータの読み書きをする上で、一番低速な機器となります。

    ディスクI/Oが大きい時は、パフォーマンス劣化の原因になっている事が多いです。


    「vmstat」でディスクの読み込み、書き込みを確認する事が出来ます(bi / bo)

    View full-size slide

  19. メモリの負荷

    20

    メモリを100%使い切っている場合、スワップメモリを利用することになります。

    スワップメモリは、ディスクをメモリ領域として確保して利用する為、

    実質ディスクI/Oを発生させる事になります。

    ディスクは非常に低速なデータ領域なのでボトルネックになる可能性が高いです。


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

    View full-size slide

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


    View full-size slide

  21. PHPのパフォーマンス測定

    22

    PHPに原因がありそうな事がわかった場合、

    どの関数で問題が発生しているのかをプロファイラーを用いて確認します。


    OSSになっているXdebugが比較的気軽に導入することが出来ます。


    費用に余裕がある場合は、DatadogやBlackfire等の

    SaaSを利用することも選択肢にあがります。

    View full-size slide

  22. Xdebug x Webgrind

    23

    Xdebugにてプロファイルを取得して

    プロファイル解析が可能なWebgrindを利用し、Graphvizを介して可視化します。

    具体的な利用方法はこちらの記事をご確認ください。


    すると右図の用に図が生成出来ます。

    - どの関数から呼び出されているか

    - 処理を専有している関数

    - 繰り返し実行されている回数


    N+1問題等を発見して解消していきます

    View full-size slide

  23. RDBでのパフォーマンス測定

    24

    PHPプロファイリングにて時間がかかっているのがRDB操作の関数であったり、

    DBサーバのリソース枯渇している場合、RDBにボトルネックがあることがわかります


    単一のSQLが原因になっているのであれば実行計画を見ていきます。

    DB設計ミスなのか、Index設定ミスなのか、

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

    View full-size slide

  24. 計測、考察、対策

    25

    このようにサーバのリソース計測したり、プロファイラを利用することで

    WebサーバやPHP、RDBの状態を正しく認識します。


    現状を正しく認識したら原因について考察し、対策を実施します。


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

    View full-size slide

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


    View full-size slide

  26. 実行計画①

    28

    ◎あるSELECT SQLの実行が遅い場合

    EXPLAIN(ANALYZE)をSELECTの前に付けて、実行計画を取ります。

    - Seq Scan … 全レコード探索

    - Index Scan … Indexとテーブルに交互にアクセスして探索

    - Bitmap Scan … メモリ上に展開されたBitmapを使ってAndOrの探索

    - Index Only Scan … Indexのみからデータを探索(条件有)

    意図したIndexが利用できているかを確認します。


    View full-size slide

  27. 実行計画②

    29

    テーブル結合にも注目します。

    Nested Loop … 外部テーブルをスキャンして、内部テーブルに結合

    Indexの概念があるので、意図したIndexが効いてるかを考慮

    Merge Join … 外部テーブル、内部テーブルのキーをソートして結合

    Indexがないテーブルではコストが大きい

    Hash Join … メモリ上にハッシュテーブルを作成してハッシュを比べて結合

    メモリ上にハッシュテーブルが収まれば高速に結合

    View full-size slide

  28. 非正規化

    30

    必要以上にJoinが発生してしまう場合などは、

    正規化しているテーブルの非正規化も検討を行います。


    データの追加・更新・削除が発生しないテーブルに関して、

    非正規化が有効で、Joinを減らすことでSQLのコストを下げることが出来ます。

    View full-size slide

  29. パラメータチューニング

    31

    ◎DBサーバのリソースが有向活用出来ていない場合

    CPUの使用率は高いが、メモリの使用率が低い場合などは、

    RDBにメモリを十分に与える事で解決する可能性があります。


    メモリを利用するソートやジョインが高速化され、

    不必要なディスクI/Oを発生させずに結果を返すことが出来ます。

    ディスクにデータを書き込むタイミングを調節することで、

    ディスクI/Oを減らす事が出来ます。


    RDBパラメータチューニングの内容はここでは話しきれないので、

    Appendixを是非ご覧ください


    View full-size slide

  30. Source、Replica

    32

    ◎DBサーバ複数台構成が組める場合

    データ更新のディスクのI/Oが原因でSELECTが遅くなってしまう可能性があります。


    そもそもの設計の話になりますが複数台構成を取り、データの更新はSourceに行い、

    データの参照はReplicaに行うことでデータの参照を遅延させない事が出来ます。

    View full-size slide

  31. PHPバージョンアップ

    34

    ◎問答無用でパフォーマンスアップの可能性

    PHPは日々進歩しています。

    PHPバージョンアップにより高速化する可能性は十分にあります。

    SwooleTwitterアカウント
    (@php_swoole)より引用


    View full-size slide

  32. オペコードキャッシュ

    35

    オペコードキャッシュの機構を使う事で更にPHPを高速化させる事が可能です。

    OPcacheを利用することで、コンパイルしたコードをメモリ上にキャッシュし、

    高速化することが出来ます。


    初期設定では定期的にPHPコードの変更をチェックして、

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

    View full-size slide

  33. JIT

    JIT(ジャストインタイム・コンパイラー)

    PHP8から導入された機能

    JITはOPCcacheの中間コードの重要な部分をネイティブコードまで変換します。

    コンパイルをバイパスするおかげで、

    パフォーマンスとメモリ使用状況を大幅に改善させる事が可能です。

    36

    php.netより引用


    View full-size slide

  34. ユーザーキャッシュ

    37

    ◎DBサーバ負荷が高い

    ユーザーキャッシュの機構を使いKey Valueのキャッシュを利用することで、

    ディスクアクセスを減らす事が可能です。

    但しディスクのデータが更新された際のキャッシュクリアが必要になります。

    - APCu

    - PHP単体で利用

    - Redis

    - キャッシュサーバーとして複数台で利用

    View full-size slide

  35. PHP

    38

    ◎Webサーバーからのリクエストを受けきれない

    フォークするプロセス数を増やす事で受け取るリクエスト数を増加させます。

    - pm.max_children:同時に処理するプロセスの最大数

    - pm.start_servers:起動時に生成されるプロセス数

    - pm.min_spare_servers:待ち状態の子プロセス最小数

    - pm.max_spare_servers:待ち状態の子プロセス最大数

    サーバーのリソースとmemory_limitの上限に相談して、

    プロセスを増やすことで処理性能を上げる事が出来ます。

    Apache + mod_phpの場合はMaxClientの設定に相当します。

    パラメータチューニングの詳細は
    Appendixをご覧ください。


    View full-size slide

  36. Webサーバー


    View full-size slide

  37. Webサーバーのパフォーマンス

    40

    ◎クライアントからのリクエストを受けきれない

    何らかの要因でスパイクアクセスが発生した際に、

    WebサーバはそのHTTPリクエストを受け切る必要があります。

    ApacheのMaxClientや、Nginxのworker_processesを調整します。

    OSパラメータであるulimit等の値を調節して、

    Too Many Open Fileが発生しない用にするなどの調整が必要になります。

    設定例はAppindexへ


    View full-size slide

  38. リソース圧縮

    41

    ◎ネットワークトラフィック

    HTMLやCSS等のリソースファイルを返還する際に、

    データを圧縮して転送してブラウザに解凍してもらう事で

    ネットワークトラフィックを少なくすることも出来ます。


    ブラウザで解凍する必要がありますので、

    クライアントに若干の負荷をかける事になりますが、速度の向上に繋がります。

    View full-size slide

  39. HTTP2

    42

    ◎HTTP/2を利用する

    利用するプロトコルをHTTP/2に変更します。

    1つのコネクション内で同時に並行して複数のリクエスト/レスポンスを処理出来る為

    複数のリソースファイルを1つのコネクションで返す事が出来ます。

    View full-size slide

  40. ファイルキャッシュ

    43

    ◎PHP以降の処理が重い

    HTMLやCSSをWebサーバーにキャッシュしてしまうのも手です。

    PHPでSSRをする場合、結果が変わらない場合はWebサーバー側で

    PHPのレンダリング結果をメモリ上にキャッシュしてしまい、

    PHPへのRequestを減らすことで、低負荷で高速に結果を返すことが可能です。


    但しキャッシュクリアのタイミングを失敗すると想定している結果を返せず、

    手動でキャッシュをクリアするなど障害が発生してしまう可能性があります。

    View full-size slide

  41. まとめ

    Point 3

    ボトルネックを解消する方法は様々な方法があります。知識を蓄え適切に対処しましょう。

    44

    推測するな、計測せよ。再現性のある対策を行いましょう。

    Point 1

    Point 4

    話せないことがたくさんあった、Appendix見ていってください


    リソースを正しく把握して、ボトルネックを特定しましょう。ディスクアクセスは遅い。

    Point 2


    View full-size slide

  42. ご清聴いただきありがとうございました

    Thank You
    We are Hiring !

    https://recruit.fusic.co.jp/


    View full-size slide

  43. Appendix

    05

    View full-size slide

  44. Nginxパラメータチューニング

    47

    Webサーバーはいかにリクエストを受け付けるのかが鍵 

    ※メモリの消費に直結するのでサーバーリソースと相談しながら 

    - worker_processes

    - 受け付けるNginxのプロセス数を決める。auto = CPU数がオススメ 

    - worker_connections 

    - ひとつのワーカーが開けるConnection数 

    - worker_rlimit_nofile 

    - 開けるファイルディスクリプタの上限値 


    View full-size slide

  45. 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などが対応します。 


    View full-size slide

  46. RDBパラメータチューニング

    49

    詳しい内容は下記から得た知識になります。

    ◎僕が心の拠り所にしているQiita
    MySQLパフォーマンスチューニング -my.cnfの見直し-

    ◎@mamy1326 さん作成の元ネタ

    初めてのMySQLサーバーチューニング -データベースは怖くない!-

    こちらを見て是非理解を深めてください。


    次のページからはPostgreSQLの設定をいくつかご紹介します

    View full-size slide

  47. RDSパフォーマンスチューニング

    50

    増やしたPHPのリクエストを受け切る為に接続数を上げます(max_connections)

    ただしその分だけメモリとディスクI/Oが増加する恐れがあるため、

    あまりにも多くし過ぎると接続を許しながら

    LoadAverageをひたすら増加させる恐れがあります。

    潔くtoo many clientsを出してLoadAverageの過剰な増加を防ぐのも一手です。

    ※PHP側で正しく判定する必要が出てきます。

    View full-size slide

  48. RDSパフォーマンスチューニング

    51

    利用するメモリを増やすことでメモリキャッシュを行ったり、

    ソートやジョインで利用する際のメモリを増やす事で高速化を行います。


    effective_cache_size … 単一の問い合わせで利用できるディスクキャッシュの実効容量

    shared_buffers … データベースサーバが使用する共有メモリバッファ

    work_mem … 一時ディスクファイルに書き込む前に、

            内部並べ替えとハッシュテーブル操作が使用するメモリ容量


    View full-size slide

  49. RDSパフォーマンスチューニング

    52

    ログ先行書き込み(WAL)はデータの一貫性を確実にするための標準的な手法です。

    ディスクに書き込む前にメモリ上に保管しておく事が出来、

    メモリに保持する量や頻度を調節することにより、ディスクへの負荷を低減出来る

    checkpoint_timeout、max_wal_size、wal_buffers(-1で大抵の場合妥当な結果となる)

    等が調整の対象となる。

    29.4. WALの設定


    View full-size slide

  50. 距離

    53

    データ転送も距離の問題があります。

    データやコンテンツとの通信の距離を短く設定する為にDNSやCDNで

    高速化の仕組みを提供します。

    - DNS:位置情報ルーティングポリシー(Route53など)

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

    View full-size slide

  51. CDN

    54

    CDNはコンテンツキャッシュの機能も有するので、

    コンテンツをオリジン(配信元)から受け取った後にキャッシュして、

    後続のサーバーへの負荷を減らすことが出来ます。(例のごとくTTLに注意)

    View full-size slide

  52. JAMstack

    55

    JavaScript/APIs/Markupの頭文字をとったフロントエンドスタック
    (PHPでは無いですが)

    FusicのTechBlogはJAMstackを利用したSSGで運用しています。

    パフォーマンス、スケーラビリティ、セキュアなブログ・サイトを運用出来てます。


    弊社の浦田の登壇資料が分かりやすいのでご参照ください。


    View full-size slide

  53. フロントエンド

    56

    本筋とはズレますが、フロントエンドのチューニングもあります。

    - Rendering

    - CSS(CSSOMツリー)

    - HTML(DOMツリー)

    - Scripting

    - JavaScript

    - Painting

    - 2Dグラフィックエンジン、ピクセル描画など

    私が解説出来るほど収めておらず次の本をオススメします。

    Webフロントエンド ハイパフォーマンス チューニング

    (著:久保田 光則)にて詳しく解説されています。

    ここでも開発者ツールのパフォーマンスタブを利用した

    「推測するな、計測せよ」という思想について触れられています。

    View full-size slide