9.3. Zend_Db_Profiler

9.3.1. 導入

Zend_Db_Profiler を使用すると、クエリの情報を取得することができます。 アダプタが実際に実行したクエリの内容や実行所要時間などが取得でき、 余計なデバッグコードをクラスに追加しなくてもクエリを調べられるようになります。 さらに、条件を指定して特定のクエリだけの情報を取得することもできます。

プロファイラを有効にするには、アダプタのコンストラクタで指定するか、 あるいは後からアダプタに指示します。

<?php
require_once 'Zend/Db.php';

$params = array (
    'host'     => '127.0.0.1',
    'username' => 'malory',
    'password' => '******',
    'dbname'   => 'camelot',
    'profiler' => true  // プロファイラを使用します。false (デフォルト) にすると無効になります。
);

$db = Zend_Db::factory('PDO_MYSQL', $params);

// プロファイラを無効にします
$db->getProfiler()->setEnabled(false);

// プロファイラを有効にします
$db->getProfiler()->setEnabled(true);
?>

9.3.2. プロファイラの使用

好きなところでアダプタの getProfiler() メソッドを使用すれば、 プロファイラを取得できます。

<?php
$profiler = $db->getProfiler();
?>

これは、Zend_Db_Profiler オブジェクトのインスタンスを返します。 このインスタンスのさまざまなメソッドを使用することで、 クエリの内容を調べることができます。

  • getTotalNumQueries() は、 情報を取得したクエリの総数を返します。

  • getTotalElapsedSecs() は、 情報を取得したクエリの実行所要時間の合計を返します。

  • getQueryProfiles() は、 すべてのクエリ情報を配列で返します。

  • getLastQueryProfile() は、最後に (直近に) 実行されたクエリの情報を (そのクエリが 完了したか否かにかかわらず) 返します (クエリが完了していない場合は、終了時刻が null となります)。

  • clear() は、スタック上に残っている 過去のクエリ情報をすべて消去します。

getLastQueryProfile() の返り値、および getQueryProfiles() の個々の要素は Zend_Db_Profiler_Query オブジェクトで、 これを使用すると個々のクエリ自体の情報を調べられます。

  • getQuery() は、クエリの SQL テキストを返します。

  • getElapsedSecs() は、 クエリの実行所要時間を返します。

Zend_Db_Profiler の提供する情報は、 アプリケーションのボトルネックを調査したり クエリをデバッグしたりする場合に有用です。 例えば、直近に実行されたクエリが実際のところどんなものだったのかを知るには次のようにします。

<?php
$query = $profiler->getLastQueryProfile();

echo $query->getQuery();
?>

ページの生成に時間がかかっているとしましょう。この場合、 プロファイラを使用してまず全クエリの実行所要秒数を取得します。 それから、個々のクエリを調べ、一番時間がかかっているのはどれかを見つけます。

<?php
$totalTime    = $profiler->getTotalElapsedSecs();
$queryCount   = $profiler->getTotalNumQueries();
$longestTime  = 0;
$longestQuery = null;

foreach ($profiler->getQueryProfiles() as $query) {
    if ($query->getElapsedSecs() > $longestTime) {
        $longestTime  = $query->getElapsedSecs();
        $longestQuery = $query->getQuery();
    }
}

echo '全部で ' . $queryCount . ' 件のクエリが ' . $totalTime . ' 秒で実行されました' . "\n";
echo '平均の所要時間: ' . $totalTime / $queryCount . ' 秒' . "\n";
echo '1 秒あたりのクエリ実行数: ' . $queryCount / $totalTime . "\n";
echo '所要時間の最大値: ' . $longestTime . "\n";
echo "一番時間のかかっているクエリ: \n" . $longestQuery . "\n";
?>

9.3.3. プロファイラの高度な使用法

単にクエリを調べるだけでなく、どのクエリを調べるのかという 条件を指定することも可能です。以下で説明するメソッドは、 Zend_Db_Profiler インスタンスのメソッドです。

9.3.3.1. クエリの実行所要時間による絞り込み

setFilterElapsedSecs() は、クエリの情報を取得する条件として 実行所要時間の最小値を指定します。このフィルタを削除するには、 メソッドに null 値を渡します。

<?php
// 所要時間が 5 秒以上のクエリのみ調べます
$profiler->setFilterElapsedSecs(5);

// 所要時間にかかわらず、すべてのクエリを調べます
$profiler->setFilterElapsedSecs(null);
?>

9.3.3.2. クエリの形式による絞り込み

setFilterQueryType() は、クエリの情報を取得する条件として クエリの形式を指定します。複数の形式を扱うには、それらの論理 OR を指定します。 クエリの形式は、Zend_Db_Profiler のクラス定数として以下のように定義されています。

  • Zend_Db_Profiler::CONNECT: 接続操作、あるいはデータベースの選択。

  • Zend_Db_Profiler::QUERY: 他の形式にあてはまらないクエリ。

  • Zend_Db_Profiler::INSERT: 新しいデータをデータベースに追加するクエリ。 一般的には SQL の INSERT。

  • Zend_Db_Profiler::UPDATE: 既存のデータを更新するクエリ。通常は SQL の UPDATE。

  • Zend_Db_Profiler::DELETE: 既存のデータを削除するクエリ。通常は SQL の DELETE。

  • Zend_Db_Profiler::SELECT: 既存のデータを取得するクエリ。通常は SQL の SELECT。

  • Zend_Db_Profiler::TRANSACTION: トランザクションに関する操作。例えばトランザクションの開始や コミット、ロールバックなど。

既存のフィルタを削除するには、 setFilterElapsedSecs() の引数に null だけを渡します。

<?php
// SELECT クエリのみを調べます
$profiler->setFilterQueryType(Zend_Db_Profiler::SELECT);

// SELECT、INSERT そして UPDATE クエリを調べます
$profiler->setFilterQueryType(Zend_Db_Profiler::SELECT | Zend_Db_Profiler::INSERT | Zend_Db_Profiler::UPDATE);

// DELETE クエリを調べます (削除されたデータを見つけることができます)
$profiler->setFilterQueryType(Zend_Db_Profiler::DELETE);

// すべてのフィルタを削除します
$profiler->setFilterQueryType(null);
?>

9.3.3.3. クエリの型を指定することによる情報の取得

setFilterQueryType() を使用すると、生成される情報を減らすことができます。 すべての情報を保持しておくほうがよい場合もありますが、 普通はそのときに必要な情報のみが見られればよいでしょう。 getQueryProfiles() のもうひとつの機能として、 最初の引数にクエリの型 (あるいは複数の型の論理和) を指定することによるその場での絞り込みが可能です。 クエリの型を表す定数については 項9.3.3.2. 「クエリの形式による絞り込み」 を参照ください。

<?php
// SELECT クエリの情報のみを取得します
$profiles = $profiler->getQueryProfiles(Zend_Db_Profiler::SELECT);

// SELECT、INSERT そして UPDATE クエリの情報のみを取得します
$profiles = $profiler->getQueryProfiles(Zend_Db_Profiler::SELECT | Zend_Db_Profiler::INSERT | Zend_Db_Profiler::UPDATE);

// DELETE クエリの情報を取得します (データがなぜ削除されたのかを
// 知ることができます)
$profiles = $profiler->getQueryProfiles(Zend_Db_Profiler::DELETE);
?>