Slide 1

Slide 1 text

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


Slide 2

Slide 2 text

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


Slide 3

Slide 3 text

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


Slide 4

Slide 4 text

Agenda
 4
 1. パフォーマンスチューニング概要 
 2. PHPにおけるチューニング要素
 3. 要素ごとの計測
 4. 計測、考察、対策のサイクルを回す 
 5. まとめ


Slide 5

Slide 5 text

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


Slide 6

Slide 6 text

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


Slide 7

Slide 7 text

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


Slide 8

Slide 8 text

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


Slide 9

Slide 9 text

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


Slide 10

Slide 10 text

長年の勘ではなく、理論に従う
 10
 信頼のおけるデータを正しく理解し、理論的な対策を行う事で 再現性を確保する。
 そのようなパフォーマンス改善には次のステップを踏む必要があります。 
 - 現在の状況を把握するために、計測を行う 
 - 計測で集まったデータを元に考察して、原因を特定する 
 - 特定した原因に対して、正しく改善する 
 愚直にこのサイクルを回す事が重要


Slide 11

Slide 11 text

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


Slide 12

Slide 12 text

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


Slide 13

Slide 13 text

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


Slide 14

Slide 14 text

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


Slide 15

Slide 15 text

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


Slide 16

Slide 16 text

ISUCON5 予選
 16
 ISUCON(iikanjini speed up contest)とはお題となるWebサービスを、 
 決められたレギュレーションの中で限界まで高速化を図るチューニングバトル。 
 
 今回はその第5回予選の問題を利用して、チューニングの様子をお話致します。 


Slide 17

Slide 17 text

03 要素ごとの計測


Slide 18

Slide 18 text

ベンチマーク計測
 18
 スループットを測定するためのツールとして 
 普段はLocustやApacheBench等のツールを利用します。 
 
 今回はISUCONが用意したベンチマークツールがありますので 
 そちらを利用して速度(スコア)の計測を行います。 
 
 ※今回はコードの変更に関しては基本的に触れません。 
  インフラミドルウェアをチューニングすることで対応します。 


Slide 19

Slide 19 text

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


Slide 20

Slide 20 text

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


Slide 21

Slide 21 text

処理待ち状況、CPUの負荷
 21
 サーバの処理待ちの状況を確認するにはLoad Averageを確認します。 
 初期アプローチとしては処理コストが少ない「uptime」を使うのが適切です。 
 
 CPU状況も合わせて確認するには「htop」を使うと便利です。 
 CPUのコア数以上の処理待ちがある場合、 
 そのサーバーがボトルネックになっている可能性が高いです。 


Slide 22

Slide 22 text

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


Slide 23

Slide 23 text

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


Slide 24

Slide 24 text

ボトルネック処理の特定


Slide 25

Slide 25 text

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


Slide 26

Slide 26 text

Xdebug x Webgrind
 26
 Xdebugにてプロファイルを取得して 
 プロファイル解析が可能なWebgrindを利用し、Graphvizを介して可視化します。 
 具体的な利用方法はFusic Tech Blogをご参照ください。 
 PHP8のプロファイリングにXdebug3とWebgrindを使う 
 
 右図の用に図が生成出来ます。 
 - どの関数から呼び出されているか 
 - 処理を専有している関数
 - 繰り返し実行されている回数
 N+1問題等を発見して解消していきます 


Slide 27

Slide 27 text

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


Slide 28

Slide 28 text

計測、考察、対策
 28
 このようにサーバのリソース計測したり、プロファイラを利用することで 
 WebサーバやPHP、RDBの状態を正しく認識します。 
 
 現状を正しく認識したら原因について考察し、対策を実施します。 
 
 対策後、新たなボトルネックが判明したら、再度計測を行い繰り返し改善します。 


Slide 29

Slide 29 text

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


Slide 30

Slide 30 text

ファーストベンチマーク


Slide 31

Slide 31 text

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


Slide 32

Slide 32 text

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


Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

スロークエリのチェック


Slide 35

Slide 35 text

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


Slide 36

Slide 36 text

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


Slide 37

Slide 37 text

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


Slide 38

Slide 38 text

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


Slide 39

Slide 39 text

Index改善後
 39
 スコアが「1240.0」→「2342.1」に上がりました。 
 
 またスロークエリが発生しなくなりました。 
 コードを変更することなく、 
 3倍のスコアを叩き出した事になります。 
 
 ここでボトルネックが移っていないかを確認します。 


Slide 40

Slide 40 text

スロークエリ改善後のベンチマーク
 40
 Nginx + PHP LA微増 CPU微増 メモリ微増 MySQL LA増加なし メモリ枯渇なし CPUに負荷低下

Slide 41

Slide 41 text

MySQLパラメータチューニング
 41
 パラメータチューニングに関しては、 
 次の記事とスライドが非常に参考になりますのでご参考にしてください。 
 ◎僕が心の拠り所にしているQiita MySQLパフォーマンスチューニング -my.cnfの見直し- 
 ◎@mamy1326 さん作成の元ネタ
 初めてのMySQLサーバーチューニング -データベースは怖くない!- 
 
 こちらの記事を参考に、次ページの様にMySQLのパラメーターを追加します。 


Slide 42

Slide 42 text

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


Slide 43

Slide 43 text

ベンチマーク結果
 43
 スコアが「2342.1」→「3697.6」に上がりました。 
 
 コードを変更することなく、 
 5倍のスコアを叩き出した事になります。 
 
 ここでボトルネックが移っていないかを確認します。 


Slide 44

Slide 44 text

パラメーターチューニング後のベンチマーク
 44
 Nginx + PHP LA微増 CPU増加 メモリ変化なし MySQL LA増加なし メモリ使用率増加 CPUに負荷低下

Slide 45

Slide 45 text

PHP-FPM
 45
 PHPからMySQLに対して
 負荷を掛けきれていない
 
 長年の勘が働き、
 PHPのプロセス数を
 増やせば良いはずと判断
 
 PHPのプロセス数を
 増やして負荷を掛ける


Slide 46

Slide 46 text

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


Slide 47

Slide 47 text

# 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


Slide 48

Slide 48 text

# 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

Slide 49

Slide 49 text

# 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

Slide 50

Slide 50 text

ベンチマーク結果
 50
 スコアが「3707.2」→「4564.7」 
 
 さすがPHP8、速い
 PHP8の目玉機能でもあるJITを有効化します。 


Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

ベンチマーク結果
 52
 スコアが「4564.7」→「5497.8」 
 
 ここまで効果が出る事は予想外でしたが、 
 JITによる効果は大きそうです。 
 
 ここでボトルネックが動いていないかを確認します。 
 


Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

# 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


Slide 55

Slide 55 text

プロファイリング用ベンチマーク
 55
 スコアが「5497.8」→「3322.2」 
 
 プロファイリング情報を収集しながら動作するのと、 
 100k 〜 1Mサイズのプロファイリングファイルの 
 書き出しを行うので動作としてはかなり影響が出ます 
 
 運用時はXdebugを切ることを忘れないように 
 気をつける必要があります。 


Slide 56

Slide 56 text

■描画用ツール # 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


Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

Webサーバー


Slide 59

Slide 59 text

Nginxパラメータチューニング
 59
 Webサーバーはいかにリクエストを受け付けるのかが鍵 
 ※メモリの消費に直結するのでサーバーリソースと相談しながら 
 - worker_processes
 - 受け付けるNginxのプロセス数を決める。auto = CPU数がオススメ 
 - worker_connections 
 - ひとつのワーカーが開けるConnection数 
 - worker_rlimit_nofile 
 - 開けるファイルディスクリプタの上限値 
 - Unix ドメインソケット 
 - Unixドメインソケットを利用する事で接続時のオーバーヘッドを減らす 


Slide 60

Slide 60 text

ファイルキャッシュ
 60
 ◎PHP以降の処理が重い
 HTMLやCSSをWebサーバーにキャッシュしてしまうのも手です。 
 PHPでSSRをする場合、結果が変わらない場合はWebサーバー側で 
 PHPのレンダリング結果をメモリ上にキャッシュしてしまい、 
 PHPへのRequestを減らすことで、低負荷で高速に結果を返すことが可能です。 
 
 但しキャッシュクリアのタイミングを失敗すると想定している結果を返せず、 
 手動でキャッシュをクリアするなど障害が発生してしまう可能性があります。 


Slide 61

Slide 61 text

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


Slide 62

Slide 62 text

kataribe用の設定
 62
 次の様に時間付きのLogFormatを用意して、 
 kataribeのデフォルト設定にて解析できるようにする 
 設定ファイルを生成して、設定ファイルが置いてあるディレクトリにて、 
 kataribeに対してアクセスログを投げる 
 # kataribe -generate
 # cat /var/log/nginx/access.log | kataribe
 その後、nginxを再起動すると共にnginxのアクセスログを初期化する 
 # cat /dev/null > /var/log/nginx/access.log


Slide 63

Slide 63 text

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


Slide 64

Slide 64 text

05 まとめ


Slide 65

Slide 65 text

まとめ
 Point 3
 ボトルネックを解消する方法は様々な方法があります。知識を蓄え適切に対処しましょう。 
 65
 推測するな、計測せよ。再現性のある対策を行いましょう。
 Point 1
 Point 4
 ISUCON11はそろそろ申し込みです!みなさん、高速化やっていきましょう! 
 
 リソースを正しく把握して、ボトルネックを特定しましょう。
 Point 2


Slide 66

Slide 66 text

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