タグ別アーカイブ: PostgreSQL

CodeIgniter 3.x で作ったWebアプリを更に高速化させていく

この記事は CodeIgniter Advent Calendar 2017 の 12月16日 分の投稿です。

CodeIgniter は元々軽量・軽快で高速なフレームワークですが、今時の高速化トレンドに対応して更に速くしていきましょう。

PHP 7に対応する

PHP 7.0 でも 7.1 でも 7.2 でも動きます!
もちろん PHP OPcache も有効にしておきましょう。更に速くなります。

gzip圧縮にする

Googleのアドバイスツールでも指摘してくるやつですね。CodeIgniter、というよりはPHPの標準的な機能になるのですが、以下のコード(冒頭4行)をビューのアウトプットをする前に入れておくだけでPHPの透過的なgzip圧縮が有効になります。簡単。

// 透過的な圧縮を有効にする
if (ini_get('zlib.output_compression') != 'On') {
	ini_set('zlib.output_compression', 'On');
}

$this->load->library('Twig');
$data = [
	'title' => 'XXXXXXX',
	'hoge' => $hoge,
	'foo' => $foo,
	'bar' => $bar
];
$this->output->set_output($this->twig->render('dir1/example', $data));

※このコードではビューの組み立てにTwigを使っています。

注意点として、画像であったりZIPファイルをバイナリで返したりであるような際に透過的な圧縮が有効になっているとファイルが壊れてしまったりします。そういう出力をするURLが有る場合は、全てのURLで常に適用されるような書き方は避けるのが無難です。

http/2対応にする

これはWebサーバ側の話になるのでもちろん CodeIgniter でも対応可能です。http/2 に対応したバージョンの Apache 2.4 or Nginx (h2oとかOpenLightSpeedとかでも)と php-fpm を組み合わせてあげましょう。Apache を http/2 に対応する場合、prefork MPMではなくevent MPM(やworker MPM)にする事が必要になりますので、その辺りも適宜チューニングしていきましょう。

参考: Amazon Linux AMI 2017.09で今こそApacheをhttp/2対応にする手順

APIのバックエンドとして同時に叩かれる

CodeIgniter 3は同じクライアントから同時に複数URLを叩かれるような場合に、同時に処理せず逐次処理のような挙動をします。これはPHPのセッションが有効になっている際の制限なのですが、セッションの機能を使い終わったタイミングでセッションを書き込み・クローズさせてあげる事で並列度が上がります。

// 同ユーザー・セッションからの連続アクセスの為にセッションのロックを解除する。
// これを実行した後はセッション関連の機能が使えない事に注意。
session_write_close();

そもそもAPIでセッションを利用しているのがよくないという話も有りますが、必要な場合にはこの対策をどうぞ。

Redisでキャッシュとかも使っていきたい

Memcachedとか、AWSのElastiCacheとか。CodeIgniterにはそれらを利用するための機能が標準で用意されています。……といっても私自身は php-pecl-redis で 直接 Redis を扱ってしまっているのでCodeIgniter側の機能は使った事が無いのですが。ちょっと重めの外部APIから値を取得しているような場合など、適用できそうな場所はどんどん Redis でキャッシュしましょう。

最新のMySQLやPostgreSQL使いたいよね

MySQLにしてもPostgreSQLにしてもバージョンが上がるたびに様々な改善が有り、高速化が有り、新機能が有ります。MySQL 8.0にも更なる期待が持てるこの頃。

CodeIgniterには重厚なORマッパーが有りません。QueryBuilderは本当にクエリのビルダですし、生のSQLを直接実行する事も容易です。その為、PostgreSQLやMySQL 8.0のウィンドウ関数、CTEであったり、JSON型、配列型など、RDBの強力な機能を積極的に使っていけます。実際、PostgreSQLの配列型をタグの実装として使っていてなかなか便利だったりします。

参考(外部): タグ検索するならPostgreSQLで決まり! – yohgaki’s blog

配列型はCodeIgniterからは「配列っぽい文字列」として扱う事になるので、適当な変換関数を作ってオブジェクトだったり連想配列だったりでやり取り出来るようにすると便利です。

RDBの冗長化はAmazon RDSが便利だよね

といっても一応フェイルオーバーには数十秒かかります。以下の過去記事で「数秒間のDB接続不可に耐えるための方法」をまとめてあります。

参考: CodeigniterのDB接続不可をハンドリングして再接続する

また、CodeIgniterのデータベース接続には「1つ目の接続先が繋がらなかった場合に2つ目の接続先に繋ぐ」という機能が有ります。これはまだ活用した事が無いのですが、MariaDB の Galera Cluster であったり、PostgreSQL の BDR (Bi-Directional Replication) など、これからのマルチマスター時代のフェイルオーバーに容易に対応出来ちゃったりするのでしょうか。出来るのかは分かりませんが、試してみると面白いかもしれません。

終わり

他にもいろいろあるかもしれませんがこの辺で。フロントエンドだと AMP 、 ServiceWorker など色々な話題が有ります(その辺も勉強しないとなぁ)が、CodeIgniterも高速化していきましょう。


開発環境だけPostgreSQL + PHPの処理がめちゃくちゃ遅い理由がxdebugだった話

タイトルでもう内容が完結してしまっているのですが。アプリケーションにPHPを、DBにPostgreSQLを使っているシステムで、開発環境でだけやたら動きが遅い、具体的には5秒以上ページの表示に時間がかかるという現象を感じていました。特定の機能でだけ発生していました。

  • 言語: PHP 7.1.11 (Windows 64bit ThreadSafe版)
  • DB接続: DoctrineDBAL(pdo_pgsql)
  • DB: PostgreSQL 9.6.5

 

PHPやPostgreSQLのチューニング回り確認

PHPのメモリ設定は128MB、OPCacheも有効化済み、メモリ不足が出るような事も無く特に問題無し。PostgreSQLもshared_buffersは比較的大きなサイズが取られています。実行している処理もKabyLake世代のintel Core i7 + RAM16GB + 240GB SSDのマシンが悲鳴を上げるような処理とは思えません。

SQLの調査

特に重いページでPostgreSQLのクエリログ出力を有効化。

logging_collector = on
log_statement = 'all'

確認したクエリをPgAdmin 4からPostgreSQLに投げてみるも、1秒もかからず実行が完了します。となるとDB側は問題なさそうです。

コードの調査

PHPのコードを追いかけてみるも、特にネックになって居そうな処理はなく。

xdebugのプロファイリングが原因だった

そういえばComposerって「xdebugが有効だと遅いよ」というメッセージが出るよね、と思いだし
xdebug.profiler_enable=1 に設定していたのを
xdebug.profiler_enable=0 にしてみたところ、
あっさり解消。今まで5秒以上かかっていた画面が1秒未満で表示されるように。

本当の原因は何なのか

特定の機能だけやたら遅い状態だったので、PostgreSQL周りが常にxdebug要因で遅くなるというよりは、更に条件が有りそうです。同様に妙に(開発環境でだけ)重かった他のシステムではCodeIgniter 3のQueryBuilderとphp_pgsqlを使っており、DoctrineDBALやPDOが遅いという訳でもなさそうです。流石にxdebugのソースコードを読むのは辛いのでここから先は置いておきます。

開発環境のPostgreSQLがなぜだか遅いんだけど! という方はxdebugも確認してみるといいかもしれません。