在InnoDB表上使用ZF2 Paginator计算Count()的糟糕表现

时间:2013-06-21 18:09:28

标签: mysql zend-framework2 innodb

我试图在某些大型(大约1000万,最差情况下没有搜索过滤器)记录集上使用ZF2 Paginator。我的表格是InnoDB格式,据我所知,这些格式并没有作为元数据的一部分。

我意识到我可以扩展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秒内完成(当然不好)并返回739万条记录。

这是来自内部查询的EXPLAIN(由于RDS服务器上的磁盘空间,计数器上的EXPLAIN也会死掉):

+----+-------------+----------+------+---------------+-------+---------+-------+---------+-------+
| 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的工作方式在某种程度上不兼容?如果我们允许搜索数据库中的大多数字段,其他人如何处理所有可能查询的缓存计数?

提前致谢...

2 个答案:

答案 0 :(得分:2)

您不需要从原始查询中选择 - 这会消耗您的内存/磁盘空间!

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

除此之外:

  • 假设垃圾桶只是一个布尔值,请将其设为布尔值不可为空的列,并搜索int或布尔值为true / false

    ALTER TABLE `contacts` CHANGE `trash` `trash` TINYINT( 1 ) NOT NULL 
    
  • 请务必为垃圾栏编制索引

    ALTER TABLE `contacts` ADD INDEX `TRASH` ( `trash` )
    

更多:

  • 大型结果集的分页不一定需要精确计数:假设我们每页显示100个条目,我们不需要100000个单页N个按钮。而是使用偏移量和限制来计算页面,只显示单个按钮,例如上一页/后十页,并将其与一些“show next / prev 10 pages”按钮结合起来。

  • 当您需要“转到最后一页”的可能性时,为什么不使用DESC命令之类的东西来实现类似的目标。

  • 是否有人会通过你的10米行分页?也许提供高级过滤器来帮助用户找到他需要的东西。

答案 1 :(得分:0)

如果您改用此查询:

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