SQLクエリのキャッシュ:実験失敗
2008年12月25日
失敗した実験も記録しておかないと、あとでまた同じ事を試そうとするかもしれないので…。
Jeansのコードを書いているときに、Nucleusのコアのコードを眺めることがあるのだが、ふと思ったことは、同じSQLクエリーを繰り返し実行しているのではないかということ。これは正直、無駄である。なので、SQLクエリーをキャッシュして、再利用できないかを探ってみた。
Nucleusの場合、クエリーはglobalfunctions.phpにあるグローバル関数、sql_query()で行われている。次のようなコードだ。
これを、以下のように変更してみた。
リソースを、キャッシュとしてスタティック変数に残しておいて、同じクエリーが実行する場合は巻き戻して再利用しようというもの。もちろん、データベースに変更が加えられたときなどは同じクエリーでも結果が異なる。しかしながら、それについては後でコードを書き換えて対応することにして、とりあえず上記コードで実験してみた。
まず、Nucleusインストール直後のデフォルト状態で、クエリー実行回数が一回減ることを確認。気をよくして、このブログに試しに入れてみた結果……。クエリー実行回数は減らなかった。
Nucleusのクエリーの実行回数の多さは異常ともいえるほどなので、ゆくゆくは何か対処したい。Jeansでは、何らかのキャッシュ機構を是非導入したいと考えている。こういった仕組みについては、hsurさんのCACHEMANAGERがあるけれど、少し違った考え方で導入することを考えている。忘れないうちにアイデアだけここにメモしておくと、ようするにキャッシュ用のテーブルを一つ用意し、スキンパース前にそれらの内容を一気に取り込んでしまおうという物。テンポラリテーブルだとかの機能を利用することになりそう。fetchのさいにオブジェクトを利用しているJeansの方が、実装は容易かもしれない。
Jeansのコードを書いているときに、Nucleusのコアのコードを眺めることがあるのだが、ふと思ったことは、同じSQLクエリーを繰り返し実行しているのではないかということ。これは正直、無駄である。なので、SQLクエリーをキャッシュして、再利用できないかを探ってみた。
Nucleusの場合、クエリーはglobalfunctions.phpにあるグローバル関数、sql_query()で行われている。次のようなコードだ。
function sql_query($query) { global $SQLCount; $SQLCount++; $res = mysql_query($query) or print("mySQL error with query $query: " . mysql_error() . '<p />'); return $res; }
これを、以下のように変更してみた。
function sql_query($query) { static $cache=array(); if (isset($cache[$query])) { $res=$cache[$query]; if (@mysql_data_seek($res,0)) return $res; else unset($cache[$query]); } global $SQLCount; $SQLCount++; $res = mysql_query($query) or print("mySQL error with query $query: " . mysql_error() . '<p />'); if (mysql_num_rows($res)<5) $cache[$query]=$res; return $res; }
リソースを、キャッシュとしてスタティック変数に残しておいて、同じクエリーが実行する場合は巻き戻して再利用しようというもの。もちろん、データベースに変更が加えられたときなどは同じクエリーでも結果が異なる。しかしながら、それについては後でコードを書き換えて対応することにして、とりあえず上記コードで実験してみた。
まず、Nucleusインストール直後のデフォルト状態で、クエリー実行回数が一回減ることを確認。気をよくして、このブログに試しに入れてみた結果……。クエリー実行回数は減らなかった。
Nucleusのクエリーの実行回数の多さは異常ともいえるほどなので、ゆくゆくは何か対処したい。Jeansでは、何らかのキャッシュ機構を是非導入したいと考えている。こういった仕組みについては、hsurさんのCACHEMANAGERがあるけれど、少し違った考え方で導入することを考えている。忘れないうちにアイデアだけここにメモしておくと、ようするにキャッシュ用のテーブルを一つ用意し、スキンパース前にそれらの内容を一気に取り込んでしまおうという物。テンポラリテーブルだとかの機能を利用することになりそう。fetchのさいにオブジェクトを利用しているJeansの方が、実装は容易かもしれない。
コメント
Andy (2008年12月25日 17:18:03)
クエリーの回数を減らすにはロー・レベルのチューニングよりもハイ・レベルの方が効くと思っています。
例えば,アイテムを取得するときに,そのアイテムのプラグイン・オプションを同時に取得するとか,アイテム・オブジェクトは1回読み込んだら次回以降はDBアクセスに行かず,メモリー内のオブジェクトを利用するとか。テンプレート・パーツをパーツごとに取りに行くのでなく,一括して取得しておくとか。
例えば,アイテムを取得するときに,そのアイテムのプラグイン・オプションを同時に取得するとか,アイテム・オブジェクトは1回読み込んだら次回以降はDBアクセスに行かず,メモリー内のオブジェクトを利用するとか。テンプレート・パーツをパーツごとに取りに行くのでなく,一括して取得しておくとか。
Kat (2008年12月25日 18:23:36)
そうですね、そのあたりは考えていて、Jeansではいくらか実装しています。例えば、プラグインオプションのケースでは、一度のクエリーですべてのオプションのデータを読み込むようにしています。
これを参考に決めています。
http://www.rad51.net/blog/jeans/index.php?itemid=552
そういった工夫をした上で、さらにキャッシュできないかなと考えています。極端なケースですが、一回のアクセスに一度のクエリー発行だけですべてのデータを用意できるようなができればというところです。
もっとも、クエリーのキャッシュよりも、ページ全体のキャッシュの方が効率が良いようなケースもあると思いますが。あくまで、アイデアだけ暖めておこうという趣旨ですね。
これを参考に決めています。
http://www.rad51.net/blog/jeans/index.php?itemid=552
そういった工夫をした上で、さらにキャッシュできないかなと考えています。極端なケースですが、一回のアクセスに一度のクエリー発行だけですべてのデータを用意できるようなができればというところです。
もっとも、クエリーのキャッシュよりも、ページ全体のキャッシュの方が効率が良いようなケースもあると思いますが。あくまで、アイデアだけ暖めておこうという趣旨ですね。