13 KiB
パフォーマンスチューニング
Note|注意: この節はまだ執筆中です。
あなたのウェブアプリケーションのパフォーマンスは二つの部分に基づいています。 一つはフレームワークのパフォーマンスであり、もう一つはアプリケーションそのものです。 Yii は、そのままの状態でも、パフォーマンスを劣化させる影響がかなり小さいものですが、本番環境のためには、さらに微調整することが可能です。 アプリケーションに関しては、ベストプラクティスのいくつかを提供すると共に、それを Yii に適用する方法を例示します。
PHP 環境を最適化する
PHP 環境を正しく構成することは非常に重要です。 最大のパフォーマンスを得るためには、
- 最新の安定した PHP バージョンを使うこと。 使用する PHP のメジャーリリースを上げると、顕著なパフォーマンスの改善がもたらされることがあります。
- Opcache (PHP 5.5 以降) または APC (PHP 5.4 以前) を使って、バイトコードキャッシュを有効にすること。 バイトコードキャッシュによって、リクエストが入ってくるたびに PHP スクリプトを解析してインクルードする時間の浪費を避けることが出来ます。
デバッグモードを無効にする
本番環境でアプリケーションを実行するときには、デバッグモードを無効にしなければなりません。
Yii は、YII_DEBUG
という名前の定数の値を使って、デバッグモードを有効にすべきか否かを示します。
デバッグモードが有効になっているときは、Yii はデバッグ情報の生成と記録のために時間を余計に費やします。
エントリスクリプト の冒頭に次のコード行を置くことによってデバッグモードを無効にすることが出来ます。
defined('YII_DEBUG') or define('YII_DEBUG', false);
Info|情報:
YII_DEBUG
のデフォルト値は false です。 従って、アプリケーションコードの他のどこかでこのデフォルト値を変更していないと確信できるなら、単に上記の行を削除してデバッグモードを無効にしても構いません。
キャッシュのテクニックを使う
さまざまなキャッシュのテクニックを使うと、あなたのアプリケーションのパフォーマンスを目に見えて改善することが出来ます。 たとえば、あなたのアプリケーションが Markdown 形式のテキスト入力をユーザに許可している場合、解析済みの Markdown のコンテントをキャッシュすることを考慮してください。 そうすれば、リクエストごとに毎回同じ Markdown テキストの解析を繰り返すことを回避できるでしょう。 Yii によって提供されているキャッシュのサポートについて学ぶためには キャッシュ の節を参照してください。
スキーマキャッシュを有効にする
スキーマキャッシュは、アクティブレコード を使おうとする場合には、いつでも有効にすべき特別なキャッシュ機能です。 ご存じのように、アクティブレコードは、賢いことに、あなたがわざわざ記述しなくても、DB テーブルに関するスキーマ情報 (カラムの名前、カラムのタイプ、外部キー制約など) を自動的に検出します。 アクティブレコードはこの情報を取得するために追加の SQL クエリを実行しています。 スキーマキャッシュを有効にすると、取得されたスキーマ情報はキャッシュに保存されて将来のクエリで再利用されるようになります。
スキーマキャッシュを有効にするためには、アプリケーションの構成情報 の中で、cache
アプリケーションコンポーネント にスキーマ情報を保存するように構成し、yii\db\Connection::enableSchemaCache を true
に設定します。
return [
// ...
'components' => [
// ...
'cache' => [
'class' => 'yii\caching\FileCache',
],
'db' => [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=mydatabase',
'username' => 'root',
'password' => '',
'enableSchemaCache' => true,
// スキーマキャッシュの持続時間
'schemaCacheDuration' => 3600,
// スキーマ情報を保存するのし使用されるキャッシュコンポーネントの名前
'schemaCache' => 'cache',
],
],
];
アセットを結合して最小化する
複雑なウェブページでは、多数の CSS や JavaScript のアセットファイルをインクルードすることがよくあります。 HTTP リクエストの回数、および、これらのアセットの全体としてのダウンロードサイズを削減するために、アセットを単一のファイルに結合して、それを圧縮することを考慮すべきです。 これによって、ページのロードにかかる時間とサーバの負荷を大きく削減することが出来ます。 詳細については、アセット の節を参照してください。
セッションのためにより良いストレージを使用する
デフォルトでは、PHP はセッションを処理するためにファイルを使います。
開発と小さなプロジェクトではそれでも構いませんが、リクエストを並列処理するとなると、データベースのような別のストレージに変更する方が良いでしょう。
そうするためには、config/web.php
によってアプリケーションを構成します。
return [
// ...
'components' => [
'session' => [
'class' => 'yii\web\DbSession',
// デフォルトの 'db' 以外の DB コンポーネントを使用したい場合は
// 以下を設定する
// 'db' => 'mydb',
// デフォルトの session テーブルをオーバーライドするためには
// 以下を設定する
// 'sessionTable' => 'my_session',
],
],
];
CacheSession
を使って、セッションをキャッシュに保存することが出来ます。
キャッシュストレージの中には、memcached のように、セッションデータが失われないことを保証しないものもあり、予期せぬログアウトを引き起こす場合があることに注意してください。
サーバに Redis がある場合は、それをセッションのストレージに使用することを強く推奨します。
データベースを最適化する
DB クエリの実行とデータベースからのデータ取得がウェブアプリケーションのパフォーマンスの主たるボトルネックになることがよくあります。 データキャッシュ の使用によってパフォーマンスの劣化を緩和することは出来ますが、問題を完全に解決することは出来ません。 データベースが膨大なデータを抱えている場合、キャッシュされたデータが無効化されたときに最新のデータを取得するためのコストは、データベースとクエリが適切に設計されていないと、法外なものになり得ます。
DB クエリのパフォーマンスを向上させるための一般的なテクニックは、フィルタの対象になるテーブルカラムにインデックスを作成することです。
例えば、username
によってユーザのレコードを検索する必要があるなら、username
に対してインデックスを作成するべきです。
ただし、インデックスを付けると SELECT クエリを非常に速くすることが出来る代りに、INSERT、UPDATE、または DELTE のクエリが遅くなることに注意してください。
最後にもう一つ大事なことですが、SELECT クエリで LIMIT を使ってください。 こうすることで、大量のデータが返されて、PHP のために確保されたメモリを使い尽くすということがなくなります。
プレーンな配列を使う
アクティブレコード は非常に使い勝手のよいものですが、データベースから大量のデータを取得する必要がある場合は、プレーンな配列を使うほどには効率的ではありません。
そういう場合は、アクティブレコードを使ってデータを取得する際に asArray()
を呼んで、取得したデータがかさばるアクティブレコードのオブジェクトではなく配列として表現されるようにすることを考慮するのが良いでしょう。
例えば、
class PostController extends Controller
{
public function actionIndex()
{
$posts = Post::find()->limit(100)->asArray()->all();
return $this->render('index', ['posts' => $posts]);
}
}
上記において、$posts
は、テーブル行の配列としてデータを代入されることになります。
各行はプレーンな配列になります。
$i
番目の行の title
カラムにアクセスするためには、$posts[$i]['title']
という式を使うことが出来ます。
クエリを構築するのに DAO を使って、データをプレーンな配列に取得することも出来ます。
Composer オートローダを最適化する
全体としてのパフォーマンスを改善するために、composer dumpautoload -o
を実行して、Composer のオートローダを最適化することが出来ます。
バックグラウンドでデータを処理する
ユーザのリクエストに素早く応答したい場合、リクエストの重い部分は、それについて即座にレスポンスを返す必要がなければ、後から処理することが出来ます。
これを達成する一般的な方法が二つあります。クロンジョブによる処理と、専用のキューです。
最初のケースでは、後から処理したいデータを、データベースなどの持続的ストレージに保存する必要があります。 そして、クロンジョブによって定期的に実行される コンソールコマンド がデータベースを検索して、データがあれば処理します。
このソリューションでたいていの場合は OK ですが、一つ大きな欠点があります。 データベースを検索するまでは処理すべきデータの有無を知ることが出来ません。 そのため、データベースをかなり頻繁に検索するか、または、データの作成と処理の間に若干の遅延を生じさせるかのどちらかになります。
この問題は、キューやジョブサーバ (RabbitMQ、ActiveMQ、Amazon SQS、その他いろいろ) によって解決することが出来ます。 この場合は、持続的ストレージにデータを書き込む代りに、キューやジョブサーバによって提供される API を通じてデータをキューに入れます。 処理はたいていはジョブハンドラのクラスに渡されます。 キューに入れられたジョブは、先行するジョブが全て完了した直後に実行されます。
何をしても効果がない場合
何をしても効果がない場合は、何がパフォーマンスの問題を解決するかについての思い込みを排することです。 代りに、いつでも、何かを変更する前にはコードをプロファイルしてください。 次のツールが役に立つでしょう。