PHPでデータベースを利用する場合に注意すべき問題

GIGAZINEPHPとデータベースに関する5つの問題、とその解決法という記事にて、以下の記事が紹介されていました。

記事内では、

  • 問題1: MySQL関数を直接利用する
  • 問題2: オートインクリメントを利用しない
  • 問題3: データベースの多重化をする
  • 問題4: リレーションを使わない
  • 問題5: n+1パターン

の5つの問題についての解決法が書かれています。

記事で書かれている内容の要約と感じたことを自分なりに書きまとめてみました。記事内では、MySQLを例にして書かれていますが、どのDBでも同じことが言えると思います。

問題1: MySQL関数を直接利用する

PHPが標準で提供しているDB関数を利用するのではなく、PEAR::DB, PDOなどの抽象化したクラスライブラリを利用すべきだと書いてあります。

PEAR::DBは、PEAR::MDB2に取って代わられています。

※ PDOは、PHP5でのみ対応しています。PHP 5.0では、PECL拡張モジュールを組み込むことで利用できます。PHP 5.1以降ではバンドルされています。

上記に書かれた形にするべきなのは、以下の2点の理由からです。

  • DBアクセスが抽象化されるために、他のDBへの移行が容易
  • SQLインジェクション攻撃対策、およびメンテナンスの容易さ

問題2: オートインクリメントを利用しない

最近のDBでは、オートインクリメントをサポートしているのだから、プログラムロジック側でオートインクリメントをするのではなく、DB側にすべておまかせすべきだと書いてあります。

最悪な例としては、「DBから現在の値を求め、その値に1加えるというロジックによってオートインクリメントを行う」が挙げられます。この場合、無駄なDBへのアクセスが発生する上に、必ずオートインクリメントする値が一意になるように排他制御をきちんと行う必要があります。この無駄に必要となる排他制御のロジックを考えるだけでも面倒な上にバグを生みやすいので、やってはいけないですね。

最近流行っているSNSなどのようなコミュニティサービスなどを考えると、xxx_idといった連番カラムは増える可能性が高いので、気に留めておく必要がありますね。

問題3: データベースの多重化をする

※ 記事内では、多重化とは「データをあるくくりで分割すること」として表されています。

アプリケーションの規模にもよると思うんですが、一般的なアプリケーションでは、単純にデータベースの多重化を行えばよいというものではないと書かれています。もちろんデータベースの多重化が全く意味がないとは思いません。必要もないのにすると、逆にパフォーマンスが落ちる可能性があるのではないかと思います。

多重化を行う/行わないの境界線がどのレベルなのか、そして分割パターンも色々な形を検討および検証を行う必要があります。これらは簡単に答えが導き出せるものではなく、トライ&エラーを繰り返しつつ地道にサービスに適した答えを出すのしかないですね。

ちなみに記事内で書かれている例では、ユーザ情報のテーブルとそれ以外の情報の格納したテーブルを別々のDBへ持たせた例が挙げられています。

問題4: リレーションを使わない

リレーションを使わないと書いてありますが、実際には基本に返ってDBの正規化はきちんとやりましょうと書いてあります。あまりに当たり前な話なので、詳細は記事を読めば分かると思います。

問題5: n+1パターン

大きな機能を実装する際にでてくる問題で、エンティティ単位でそれぞれのデータを取得する際にパフォーマンス上に問題が出てくる(n+1パターン)ので、1つのクエリーにまとめましょうと書かれています。

エンティティ単位で取得するロジックを作ると、ロジック自体は明快で分かりやすいものになるのですが、同じSQL文のループが発生する可能性が出てくるために場合によっては、パフォーマンスに問題がでてきます。

実際の例として、記事内のSQL文とプログラムを見れば、分かりやすいです。

  1. nameからauthor_idを取得する
  2. author_idからbook_idのarrayを取得する
  3. book_idの数だけ、book情報を取得する

と仮に該当する書籍の数が1000冊あった場合、1002回もSQL文を実行する必要があります。一方でエンティティ単位でなく、機能単位で取得ロジックを作る、つまり1つのSQL文にまとめることで、たったの1回のSQLを実行するだけで済みます。