PHP Tipsの最近のブログ記事
ずいぶん昔に書いたphpのtipsに関して、間違いを指摘してくれた親切な方がいるんですけど、どこが間違っているのか分からないので、もしお暇だったらこのエントリーにコメントしてくださいね。
と、トップに表示されるエントリーが無くなっちゃうのはいかがなものかと。
phpでiptablesを設定するスクリプトを書いて、だいぶ安定してきたようなので。
基本的にはJPNICが逆引き権限を持っているブロックと、APNICがJPにアサインしているブロックについてはFROMJPというChainに追加して許可して、FROMJPをJPFILTERというChainに追加して基本的にドロップするというiptablesコマンドを生成します。ただし、上記ブロックに登録されていないJPなブロックやgooglebotのブロックもあるので、それは適宜調整するという運用になります。要するに日本以外からのアクセスを禁止するということです。
とりあえず/var/log/messagesからドロップされたパケットを抜きだすスクリプトで見る限り、mxやらthやらcnからのお行儀の悪いパケットがドロップされているようなので、よしとするか。
英語でブログを書き始めたら、この対策は出来ないねぇ...。
【事例】キャッシュでサイト障害を克服,DBサーバーの負荷を1/9にってのは、「システム作っちゃったけど設計しょぼいからアブストラクションレイヤなんて入れてないよ(/_;)」っていう状況なんだろうなと推察。
すでにphpでコンテンツを作るときにサーバの負荷を軽くする手法ってのはほぼ出尽くしていて、phpアクセラレータを使うとか、PEARのCacheクラスを使うとか、Smartyのコンテンツキャッシュを使うとか。例えばPEARのCacheクラスを使うだけで、多分、ロードアベレージを1/9ぐらいにするのは楽勝なはず。それが出来ない状態というのは、connectとかqueryとかの関数がコードのあちこちに散らばっちゃっていて、短期間でCacheクラスに置き換えるのが難しかったんだろうね。ADOdbとか使わないとダメダメ。
phpのテンプレートエンジン・smarty 2.6.1もしくは2.6.2と、php accelerator 1.3.3r2を一緒に使うと、まれにsmartyのincludeがおかしくなることを先日発見。すごいはまったんだけど、テンプレートに書いてあるのと全然違うファイルをincludeしちゃうのよねぇ。多分、acceleratorが悪いのでは無いかということで、一応メールしておこう。
phpでスライスは出来ないの?
【Tips】1文字だけならスライスが可能、しかも高速!
【Description】スライスというのはpython、ruby、D言語などにある概念(関数)で、部分文字列を取得する方法です。phpでは通常substr関数を用いて実現しますが、部分文字列が1文字であればスライスすることが出来、しかもsubstr関数よりも高速です。substr($str, 5, 1)と$str[5]は等価です。ただし、substr($str, 5, 2)と同じ結果を得るために$str[5].$str[6]と連結してしまうとsubstr関数よりも低速になってしまいます。なお、$str[5:6]といった方法は使えません。
リテラルの展開はどのタイミングでするべき?
【Tips】もちろん展開しておいた方が高速!
【Description】例えば1日は86400秒ですが、これを86400と書くか60*60*24と書くかで実行速度は大きく変わってきます。">count関数の使い方でも説明したように、事前に出来る処理はしておくのが鉄則です。
ob_start関数はクッキーやtext/html以外のMIMEタイプを出力するためだけのもの?
【Tips】ob_startを使うと数倍高速!
【Description】ob_start関数はクッキーを用いる場合などによく使われますが、通常のhtmlの出力の際にob_start関数とob_end_flush関数を使うと、echo関数やprint関数などの出力をバッファリングしてからapacheのAPIに出力を引き渡すと考えられるため(phpのソースを見たんですが確認できず(^^ゞ)、高速になります。
バッファリングしない場合、ob_implicit_flush関数を明示する方法もあります。
文字列を比較するstrcmp関数は何のためにあるの?
【Tips】strcmpを使う方が僅かに高速!
【Description】単純に同じかどうかを比較するのであれば、$hoge == "fuga"をテストする方法と、!strcmp($hoge, "fuga")とする方法がありますが、後者の方が非常に僅かながら高速です。
switch文に並べる条件の順番は?
【Tips】出現頻度順に並べる方が高速!
【Description】caseに並べる条件は出現頻度の多いものを先に書くべきです。以下のソースでは頻度0.7、0.2、0.1の順に並べて比較しましたが、もし数万件に1件しか含まれないような条件を先に書いてしまうと、かなり無駄なロジックになってしまいます。実際に運用してみないと分からないケースももちろんあるので、データベースで件数をカウントするなどして最適化をしましょう。
ifとswitch、どちらを使うべき?
【Tips】ifの方が高速!
【Description】同じ変数の複数の値をチェックする場合、if~else if~elseと、swith~case~defaultの2通りが考えられます。以下のソースではifの方が高速です。しかし実際には、見通しという意味でswitchを使った方が良いと思われます。また、
if($hoge == 1){
} else if($fuga == 1){
というように違うレベル(変数)のものを1つのif構造で記述すると見通しが悪くなるので、
if($hoge == 1){
} else {
if($fuga == 1){
とするべきでしょう。
if文などの条件節でtrue/falseと比較する?
【Tips】true/falseと比較しない方が高速!
【Description】if($flag == true)とif($flag)は等価です。同様にif($res = ex_func($param))という書き方も可能です。ただしDBへの接続などでは、is_resource()関数などで適切に判断する必要があります。
配列のサイズを決めるのにcount関数を使うタイミングは?
【Tips】forループの前にcount関数を呼ぶ!
【Description】forループの終了判定にcount関数を入れてしまうと、ループの度にcount関数を実行してしまいます。動的にサイズの変わる配列は仕方がないですが、そうでない場合はforループの前にサイズを変数に格納しておきましょう。また、count関数に限らず終了判定では演算(例:$i < $cnt + 1)を含まないようにしましょう。
文字列の連結にはいくつかの方法があるけど、どれが速いの?
【Tips】"(クォート)部分と変数を結合演算子(.)で結合!
【Description】結合演算子で連結する方法、"(クォート)内に{}で変数を記述する方法、sprintfで連結する方法を比較してみました。結合演算子で丁寧に書くのが一番高速です。
PEARのCacheを利用してPostgreSQLのクエリを高速化するには?
【Tips】PEARのCacheクラスを継承!
【Description】データベースの件数やSQLの複雑さに依存するので一概に言えないが、1万件のレコードをそのまま引っ張ってくるような場合で3倍程度高速です(シャア専用ってことです)。
Expireを上手に変えれば結構使える箇所は多いはず。日次でマスターが変更されるような場合3600秒のままでも問題なし。クエリの結果が巨大な場合はphpのメモリ上限を変更しないとダメなことも。以下のように使います。
$cache = new PGSQL_Query_Cache();
$cache->connect("DB_NAME", "DB_USER");
if($res = $cache->query("SELECT * FROM TABLE")){
while($row = $cache->fetch_row()){
echo($row[FLD_NAME]);
}
}
