2013-06-21 25 views
6

私はZF2パージネーターをいくつかの大規模な(最悪の場合にはサーチフィルターなしで1000万回)レコードセットで使用しようとしています。私のテーブルはInnoDBフォーマットであり、私はメタデータの一部として明確なカウントを保持していないと理解しています。InnoDBテーブルのZF2ページネイターを使ったCount()の異常なパフォーマンス

私はZend \ Paginator \ Adapter \ DbSelectクラスを拡張し、別のテーブルに手動で格納するカウントデータを使用する独自のcount()メソッドを実装できることを認識していますが、カウントを格納する方法は不明です検索の可能性のあるすべての順列。

デフォルトZF2 DbSelect adapterは、このメソッドを使用しています。ここでは

<?php 
public function count() 
{ 
    if ($this->rowCount !== null) { 
     return $this->rowCount; 
    } 

    $select = clone $this->select; 
    $select->reset(Select::LIMIT); 
    $select->reset(Select::OFFSET); 
    $select->reset(Select::ORDER); 

    $countSelect = new Select; 
    $countSelect->columns(array('c' => new Expression('COUNT(1)'))); 
    $countSelect->from(array('original_select' => $select)); 

    $statement = $this->sql->prepareStatementForSqlObject($countSelect); 
    $result = $statement->execute(); 
    $row  = $result->current(); 

    $this->rowCount = $row['c']; 

    return $this->rowCount; 
} 
?> 

は方法が私のために生産する非常に単純なクエリの例です:

SELECT 
    COUNT(1) AS `c` 
FROM 
    (
     SELECT 
      `contacts`.`id` AS `id`, 
      `contacts`.`firstname` AS `firstname`, 
      `contacts`.`middlename` AS `middlename`, 
      `contacts`.`lastname` AS `lastname`, 
      `contacts`.`gender` AS `gender` 
     FROM 
      `contacts` 
     WHERE 
      `contacts`.`trash` = '0' 
    ) AS `original_select` 

私はパフォーマンスはMyISAMテーブルにどうなるかわからないんだけどこれは、それが動作しているAmazon RDS(25GB、db.m1.small)インスタンス上の空き領域をすべて食いつぶすために失敗します。比較として、内部(元の)クエリだけを実行すると、100秒で完了し(確かにうまくいきません)、730万レコードが返されます。ここで

は、内側のクエリからEXPLAIN(カウント1にEXPLAINまた、RDSサーバー上のためのディスク・スペースのダイ)されています

 
+----+-------------+----------+------+---------------+-------+---------+-------+---------+-------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | 
+----+-------------+----------+------+---------------+-------+---------+-------+---------+-------+ 
| 1 | SIMPLE  | contacts | ref | trash   | trash | 1  | const | 3441317 |  | 
+----+-------------+----------+------+---------------+-------+---------+-------+---------+-------+ 
1 rows in set (0.04 sec) 

は、この優れたチューニングを行うことができることはありますか? ZF2 Paginatorが、InnoDBが何らかの方法で、何らかの形で互換性のないカウントを処理する方法ですか?データベース内のほとんどのフィールドの検索を許可している場合、他の人が可能なすべてのクエリのキャッシュカウントをどのように処理しますか?

事前のおかげで...

+0

この問題は、InnoDBに固有の問題です。この問題は、メタデータ内のテーブル内の行数を追跡するため、この問題はありません。クエリをデータベースのUIで微調整し、それがどのような影響を及ぼすかを確認するのが最善の方法です。 COUNT(1)の代わりに、COUNT(ID)を試すか、mysql> select count(1)from ... index(your_index)を使用して特定のインデックスを使用するようにMySQLに指示してください。 – Ruben

+0

** COUNT 15分36秒; ** COUNT(ID)**:18分8.83秒 – jcq

答えて

0

あなたの代わりにこのクエリを使用する場合:

SELECT c from 
( 
    SELECT COUNT(1) AS c 
    from contacts 
    where trash = '0' 
) AS original_select 
2

あなたは、元のクエリからの選択を必要としない - これはあなたのメモリ/ディスク容量を消費します!その横に

SELECT count(1) AS `c` 
FROM (
    SELECT 1 
    FROM `contacts` 
    WHERE `trash` = 0 
) AS `original_select` 

  • ゴミは単なるブール値であると仮定すると、それブールないNULL可能列にし、int型の検索やブール値は/偽真である

    ALTER TABLE `contacts` CHANGE `trash` `trash` TINYINT(1) NOT NULL 
    
  • ごみ箱のインデックスを作成してください。

    ALTER TABLE `contacts` ADD INDEX `TRASH` (`trash`) 
    
  • さらに

より:大きな結果セットのページ付けは必ずしも正確な数を必要としない

  • :私たちは、1ページにつき100個のエントリを表示していると言う、我々は100000個の単一のページNボタンは必要ありません。代わりに、オフセットとリミットを使用してページを計算し、たとえば前/次の10ページとそれをいくつかの "次の/前の10ページの表示"ボタンと組み合わせてください。

  • 「最後のページに行く」可能性が必要なときは、同様のことを達成するためにDESC命令のようなものを使用しないでください。

  • 誰かがあなたの10mの行を改ページするという状況は本当ですか?ユーザーが必要とするものを見つけるのに役立つ高度なフィルタを提供する場合があります。

関連する問題