PHP

サイトの表示を高速化するために。

自分が管理しているWebサイトのレスポンスがめちゃくちゃ低下してきてたので、どうにかレスポンスを改善できないかと軽く考えたことをメモ。

今回はCakePHPにて構築しているWebサイトのレスポンス改善を考えた。

基本的にサイトレスポンスを改善するためにはキャッシュの有効化が効果絶大であるが、データベースを使用しているサイトの場合、テーブルに対するインデックスの張り方を見直すだけで劇的にレスポンスが改善される場合もある。
以前あるサイトのデータベーの設計を見直していた際にインデックスの張り忘れを見つけ、その場で貼り付けてあげただけでレスポンスが10倍以上改善されたことがあった。まぁ、だからといって闇雲にインデックスを張りまくればいいのかという問題でもないので注意。メリットデメリットをしっかりと理解した上で、ビッグトラフィックに耐えうる堅固なシステムの構築を心がけなければならない。

この度メスを入れるサイトは、以前DB設計見直しによるレスポンス改善をすでに施していたので、今回はキャッシュを用いた高速化を試みた。
これまでも部分的にキャッシュを導入してはいたが、PVが極端に少ないページであるとか、ほぼ利用されていない機能周りへの試験的な導入であり、期待するような大幅な改善にはあまり貢献していなかった。ではなぜいままで一番導入が必要であると分かりきっている部分への導入を躊躇していたかというと、自分のミスによってサイトが長時間停止することが怖かったからである。チキンである。しかし今回はそうもいっていられないのである。SEO対策的な観点からしてもサイトのレスポンスが遅いということはかなりのマイナス要素なのである。それもそのはず、検索エンジンの顧客満足度を向上させるためにはお客様を待たせるようなノロいサイトを排除したほうが確実にユーザーエクスペリエンスの質は高まるからである。

ということで今回、どうにもこうにも重たくなってしまったサイトのトップページをキャッシュ導入により改善してみようと考えたのである。

まず考えたアプローチは二つ

1, 一度誰かがアクセスしたページで発行されたクエリの結果を数分間保持し、有効期限内であればそのキャッシュを見に行く
2, 毎分欲しいクエリ結果のキャッシュを生成するバッチを作成し、フロント側ではそのキャッシュを見に行けば良いような設計に変える

以前試験的に施した対策は1, のアプローチであった。しかし今回はトップページという膨大なトラフィックがあるページに対しての施策なので、ユーザーのアクセスをトリガーにしたキャッシュ生成処理はUX的に非常によろしくない気がしたため、2, のバッチ作成というアプローチをとった。
なぜよろしくないと感じたか。それはユーザーがアクセスしてきた際にキャッシュを生成させると、ビューのレンダリングまでに少なからず待ち時間が発生してしまうからである。その待ち時間がブラウザのレンダリング処理に起因するものであれば仕方ないと言えそうであるが、サーバーサイドでの待機時間が発生しているというのはあまりにもよろしくない。SQLの発行にしてもCGIの処理にしても、サーバー側での処理は極力短時間で済ませ、ユーザーの通信待機時間を如何に減らせるかが、サイトコンテンツの質云々よりもとにかく大事なことなのは間違いない。せっかくキャッシュを導入したのに、1, のアプローチだったが故に途中で離脱してしまうユーザーが発生してしまっては元も子もないのである。

という上記の理由から今回2, のアプローチでレスポンス改善を試みたが、結論から言うとサイトレスポンスはかなり改善された。
もともとそのページで発行されていたクエリ自体はそこまで重くもなく複雑でもなかったが、毎秒何回も何回も発行されると話は別である。実際、アクセス過多でDBサーバーに対して相当な負荷がかかっており、サイトレスポンスの低下を招いていたのである。
それに対し、今回は今までの処理と比較すると、バッチが代表して毎分クエリを発行しその結果をキャッシュ、ユーザーはそれを読み取りに良くだけというシンプルな構造になったため、DBサーバーとの通信回数も大幅に減り、より高速なレンダリングが可能となった。

現時点においては現状のパフォーマンスで非常に満足しているが、今後もサイトのチューニングに関しては積極的に取り組んでいこうと考えている。
今回の取り組みに加え、レスポンス改善に繋がる有効なアプローチを更に追及し、日々の業務においても活かせるよう試行錯誤を重ねていこうと思う。

ダッシュボードをカスタマイズした時にやったことまとめ。

これ←をやった時の手順まとめ。

1, まずは/wp-admin/includes/以下にある、dashboard.phpを見つける

2, 次に同ファイル293行目付近の下記コードブロックを探す

// Tags
$num = number_format_i18n( $num_tags );
$text = _n( 'Tag', 'Tags', $num_tags );
if ( current_user_can( 'manage_categories' ) ) {
	$num = "<a href='edit-tags.php'>$num</a>";
	$text = "<a href='edit-tags.php'>$text</a>";
}
echo '<td class="first b b-tags">' . $num . '</td>';
echo '<td class="t tags">' . $text . '</td>';

echo "</tr>";

3, このブロックの最終行を下記のように変更

echo "</tr>";/* コノ行を */
↓
echo "</tr><tr>";/* こう書き換える */

4, 最後にその書き換えた行の直後に、下のコードを貼り付ける

// Number of days elapsed
// カウントし始める基準の日時を設定
$started = strtotime('2013-07-16 00:00:00');
$today = time();
$elapsedDays = number_format_i18n( floor( ($today - $started) / (60*60*24) ) );
$num = $elapsedDays;
$text = '経過日数';
if ( current_user_can( 'manage_categories' ) ) {
	$num = "<a href='javascript:void(0)'>$num</a>";
	$text = "<a href='javascript:void(0)'>$text</a>";
}
echo '<td class="first b b-tags">' . $num . '</td>';
echo '<td class="t tags">' . $text . '</td>';

echo "</tr><tr>";

// Achievement rate
// 記事数割る経過日数を計算
$percentage = round( ($num_posts->publish / $elapsedDays)*100, 2 );
$num = $percentage.'%';
$text = '達成率';
if ( current_user_can( 'manage_categories' ) ) {
	$num = "<a href='javascript:void(0)'>$num</a>";
	$text = "<a href='javascript:void(0)'>$text</a>";
}
echo '<td class="first b b-tags">' . $num . '</td>';
echo '<td class="t tags">' . $text . '</td>';
	
echo "</tr>";

以上。リンク先設定に関してはジャンプ先が思い浮かばなかったのでとりあえずJavascriptで無効化。
大したことはしてないけど、ちょっとしたことでも見える化してみると意外とモチベーションアップに繋がっていいね。

CakePHP2爆速インストール手順まとめ。

最近業務で使うのが独自のフレームワークばっかで、なかなかCakeちゃんといちゃつく暇が無い。
前まで新規案件でもバリバリ使ってたけど、最近ご無沙汰過ぎて使い方忘れるのが怖い。
ってことで復習もかねてインストール方法をまとめてみる。

以下、爆即インストール手順(デバッグキット込み)

1, まずはココから最新版のCakePHP2をダウンロード

2, 解凍して出てきたフォルダをサーバーのルートディレクトリに配置

3, app/tmp以下に書き込み権限を付与

chmod -R 777 tmp

4, app/Config/database.php.defaultを複製、database.phpに改名して保存

5, 同ファイルの中身を以下のように書き換えDBと接続

public $default = array(
	'datasource' => 'Database/Mysql',
	'persistent' => false,
	'host' => 'localhost',//ホスト
	'login' => 'USER',//ユーザー名
	'password' => 'PASSWORD',//パスワード
	'database' => 'DATABASE_NAME',//DB名
	'prefix' => '',
	'encoding' => 'utf8',
);

6, app/Config/core.php、197行目付近

Configure::write('Security.salt', '@@@');

上記@の部分をランダムな文字列に書き換える。(大文字小文字半角英数)

7, 同ファイル202行目付近

Configure::write('Security.cipherSeed', '@@@');

上記@の部分をランダムな文字列に書き換える。(半角数字)

8, 同ファイル242行目付近date_default_timezone_setのコメントアウトをはずし以下に修正

date_default_timezone_set('Asia/Tokyo');

9, 同ファイル末尾に以下を追加

Configure::write('Security.cookie', 'cakephpfdebackend');
Configure::write('Session.cookieTimeout', 0);
Configure::write('Session.checkAgent', false);

10, 最後にココの手順に従ってDebugKitをインストール

以上で完了!ね、簡単でしょ?(^o^)/

次回はLaravelのインストール手順でもまとめてみようかな。

PHPで文字列に含まれる全ての改行コードを取り除く。

メモ。

str_replace(array("\r\n","\r","\n"), '', $str);

PHPでAjax通信かどうか判定する。

jQuery、prototypeなどのライブラリを利用してAjax通信をした場合、
リクエストヘッダーに「X-Requested-With:XMLHttpRequest」がセットされるため
以下のコードにより判定が可能。

if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'){
    //処理を記述  
}

PHPで文字列中にURLが含まれるか判定する。

URL形式の入力をはじきたいとき用。

$str = 'http://example.com';
preg_match('/^(https?|ftp)(:\/\/[-_.!~*\'()a-zA-Z0-9;\/?:\@&=+\$,%#]+)$/', $str);

あとは返り値で判断すればOK。

PHPで配列の構造を破壊せずに、先頭と最後の要素を取得する方法。

必要に駆られてやった時のメモ。

$last = end($array);
$first = reset($array);

end()は配列の参照ポインタが一番後ろになるので先に記述。
スマートだね。

PHPで文字列中の空白(全/半角)を一機に取り除く。

全角スペースも半角スペースもまとめて一機に取り除く方法があったのでメモ。

$str = '文字列';
$str = trim(mb_convert_kana($str, 's'));

これは便利。