7

我正在尝试在一些大型(在最坏的情况下大约 1000 万不带搜索过滤器的情况下)记录集上使用 ZF2 Paginator。我的表采用 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 万条记录。

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

+----+-------------+----------+------+------------ ---+-------+---------+--------+---------+-------+
| 编号 | 选择类型 | 表| 类型 | 可能的键 | 关键 | 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`

除此之外:

  • 假设垃圾只是一个布尔值,使其成为布尔不可为空的列并搜索 int 或布尔值是真/假

    ALTER TABLE `contacts` CHANGE `trash` `trash` TINYINT( 1 ) NOT NULL 
    
  • 一定要索引垃圾列

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

此外:

  • 大型结果集的分页不一定需要精确计数:假设我们每页显示 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 回答