7

ZF2 Paginator をいくつかの大規模な (最悪の場合、検索フィルターなしで約 1,000 万) レコード セットで使用しようとしています。私のテーブルは InnoDB 形式ですが、メタデータの一部として明確な数を保持していないことを理解しています。

Zend\Paginator\Adapter\DbSelect クラスを拡張し、別のテーブルに手動で格納したカウント データを使用する独自の count() メソッドを実装できることはわかっていますが、考えられるすべての順列のカウントを格納する方法がわかりません。行われる可能性のある検索の。

デフォルトのZF2 DbSelect アダプターは、次の方法を使用します。

<?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 秒で完了し (確かに良くありません)、739 万件のレコードが返されます。

内部クエリからの EXPLAIN を次に示します (カウント 1 の EXPLAIN も、RDS サーバーのディスク容量が原因で停止します)。

+----+-------------+----------+------+------------ ---+-------+---------+-------+---------+-------+
| | ID | select_type | テーブル | タイプ | 可能な_キー | キー | key_len | 参照 | 行 | 行 エクストラ |
+----+-------------+----------+------+------------ ---+-------+---------+-------+---------+-------+
| | 1 | シンプル | 連絡先 | 参照 | ゴミ箱 | ゴミ箱 | 1 | 定数 | 3441317 | | |
+----+-------------+----------+------+------------ ---+-------+---------+-------+---------+-------+
セットで 1 行 (0.04 秒)

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

前もって感謝します...

4

2 に答える 2

3

元のクエリから選択する必要はありません - これはメモリ/ディスクスペースを消費します!

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

その横に:

  • ゴミ箱が単なるブール値であると仮定し、それをブール値ではないnull許容列にし、intまたはブール値を検索してtrue / false

    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 行を改ページするような状況は本当にありますか? ユーザーが必要なものを見つけるのに役立つ高度なフィルターを提供することもできます。

于 2013-06-22T04:16:51.930 に答える
1

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

SELECT c from
(    
  SELECT COUNT(1) AS c
  from contacts
  where trash = '0'
) AS original_select
于 2013-06-21T19:17:57.227 に答える