PostgreSQLが起動しない

以前の記事「EC2上にCentOS5.3をインストールする」で作成したCentOSにPostgreSQLサーバーをインストールしたのだが、なぜかエラーが出た。

[root@domU-12-31-39-00-89-01 ~]# /etc/init.d/postgresql start
データベースを初期化中:                                    [  OK  ]
postgresql サービスを開始中:                               [失敗]

むむむ?と思って、/var/lib/pgsql/pgstartup.logを確認すると以下の記述があった。

LOG:  could not translate host name "localhost", service "5432" to address: 名前またはサービスが不明です
WARNING:  could not create listen socket for "localhost"
FATAL:  could not create any TCP/IP sockets

いろいろと調べた結果、/etc/hostsがないことによるエラーであることが判明。
/etc/hostsファイルを作成して、以下の内容を入力した。

127.0.0.1 localhost localhost.localdomain

そのあとで、ネットワークを再起動し、postgresqlを起動したらちゃんと起動した。

いままで、CentOSをいれてたら/etc/hostsを手作業で作る必要はなかったのだが、このファイルはいつできていたのだろう?

というわけで、PostgreSQLは/etc/hostsがないと起動しません。

PostgreSQLのSQLチューニング(初心者向け)

PostgreSQLのSQLチューニングに関しては多くの情報がありますが、多くの場合sshなどでベンチマークを行ったり、PostgreSQLサーバーの設定ファイルを変更するなど、レンタルサーバーではなかなか検証が難しい内容が多いようです。

そこで、ちょっと乱暴な部分も多いのですが、初心者プログラマ向けにパフォーマンスに大きな悪影響を及ぼす事例のみをご紹介します。

以下で説明する項目は改善すればかなり効果があります。

order byにもインデックスを設定するべし

初心者の多くは、where句で指定するフィールドにはインデックスを指定しても、order byに指定するフィールドに指定し忘れていることが多いようです。

実は、これを忘れるとwhereにかけ忘れるよりも、大きなパフォーマンス低下を招きます。

count()やmax()は禁止

count()やmax()関数はとても便利ですが、これを使用するとPostgreSQLは全てのデータをスキャンします。

数百万件のデータになると致命的に痛いことになりますので、別のテーブルに総数や最大値を保存しておく等、なるべくcount()やmax()を使わないように工夫しましょう。

limit及びoffsetは絶対に指定するべし

どんなWEBアプリケーションでも、1ページに表示する件数に限りがあります。

手を抜かないでlimitやoffsetは必ず指定するようにしましょう。

日付や時刻に注意

日付や時刻型のフィールドに対してwhere条件を使用する場合、関数を使っていたりbetweenを使用していなかったりすると、インデックスが適切に使用されません。

めんどくさいのですが、「関数インデックス」やbetweenをがんばって活用しましょう。

where句に指定する順番をチェック

SQLのwhere句にANDで複数の条件が指定されている場合、PostgreSQLは左から順に絞り込んでいきます。

したがって、検索結果が少なくなるであろう条件ほど左に持っていくなどの工夫をすることで、劇的に早くなったりします。

PHP+PostgreSQLでのSQLインジェクション対策

某大手企業向けのCMSシステムでセキュリティ監査があって、弊社納入のシステムで、ちょっと指摘をいただきました。

実際には、SQLインジェクションが実行可能だったわけではなく、お客様に土下座なんて事態にも至らなかったのですが、知らないことがあったのは事実なので、素直に今後は気を付けようと思う今日この頃。

内容は、以下の通り。全て必須対策だそうです。

コンパイル済みSQLを使用する

$db = pg_connect('dbname=…');
$result = pg_prepare($db, 'query1',
'SELECT * FROM TABLE1 WHERE CITY = $1 AND AGE >= $2');
$result = pg_execute($db, 'query1', array($city, $age));

これは正直言って関数自体を知りませんでした。
最近の入門書には書いてあるのかもしれませんね。
「ベテランであること」と「スキル」は全く関係ないことを痛感しました。

header関数で文字コードを必ず指定する

header("Content-Type: text/html; charset=UTF-8");

こちらに解説がありました。
結果オーライでやっていましたが、XSS対策の一つになるとは知りませんでした。

文字列のエスケープ

pg_escape_string()

これは知ってましたが以下の内容は勉強不足でした。

「’」(シングルクオート)と「\」(バックスラッシュ)のエスケープを実
施している場合でも、文字コードの問題によってSQLインジェクションが発生するケースがあります。
処理系の日本語対応が不十分な場合、シフトJISで2バイト目が「\」(バックスラッシュ)のアスキーコードである「0x5C」になっている文字が入力されると「’」(シングルクオート)が「\(0x5C)」にエスケープされます。
これにより、SQLインジェクションを成立させることが可能となりますので、
対策の際には注意が必要です。
具体的には以下の方法があります。

  • 最新のデータベース接続ライブラリを使用した上で、データベースエンジンにて提供されたエスケープ機能を利用する
  • バインド機構を利用する
  • PostgreSQLの場合は、以下の設定を利用する。この場合円記号「\」のエスケープはしないことに注意。
    backslash_quote = off
    standard_conforming_strings = on

とても勉強になりました。

2009/06/25 追記
徳丸様のご指摘により、standard_conforming_strings = offの部分をonに修正しました。