1

我一直在阅读关于“COUNT(*) without where 子句”在 PostgreSQL 中速度非常慢的文章。并且来自 MySQL,我不知道我是否能够在不再使用它的情况下生活。我还读到即使您添加了“where”子句,它也必须从结果中扫描每一行,除非您的结果很少,否则这会很慢。我看到有使用触发器和额外表的疯狂黑客,但我不喜欢它的外观。我的意思是我是这个 RDBMS 的新手,刚开始使用基本功能,并且已经不得不使用变通方法?

我需要做的一个例子是创建一个防洪机制。类似的东西if "count(id) where ip = 1.2.3.4" > 100, fail instead of insert

所以我的问题是:

  • 这仍然是一个未解决的问题吗?
  • 如果我改用 COUNT(id) 或添加一些像 DISTINCT 这样的子句,会有什么不同吗?
  • PostgreSQL 用户如何在不计数的情况下生存?
4

3 回答 3

3

我认为“非常慢”是一个巨大的夸张:) 在您的示例中,结果中最多有 100 行,计算它们会非常非常快。我很确定在这种情况下您不需要任何技巧或解决方法。

据我了解,“COUNT(*)WHERE子句在 Postgres 中较慢”是指 MySQL 的 MyISAM 表存储表头中的总行数,所以这样做SELECT COUNT(*) FROM my_table是 O(1) 操作 - 只需从表头中读取值即可它。在 Postgres 中,由于 MVCC 和事务,这无法完成,它必须扫描每一行以确定它是否在当前事务中可见。

但是,如果您WHERE在查询中使用子句,MySQL 不再可以从标题中读取行数,实际上必须计算行数。在这种情况下,我认为 Postgres 的性能没有太大差异。

于 2013-08-08T23:22:53.060 回答
2

这主要在 PostgreSQL 9.2 中通过仅索引扫描进行了整理。你应该没问题,只要确保你的 autovacuum 设置为经常运行。

即使在以前的版本中,我也会说“非常慢”可能已经过分了,除非您有巨大的表或非常慢的顺序 I/O。它确实需要 seqscan,所以它并不便宜,但是同步扫描之类的东西有很大帮助。

COUNT(id)是更好的风格,所以你通常应该更喜欢使用它。我没有详细研究性能影响。我认为Pg 无论如何都会使用主键,但需要做更多的挖掘而不是我有时间验证。

您提议的使用容易出现竞争条件,这将使其在任何数据库中都无效。如果您不介意插入(例如)120 条记录而不是计划的 100 条记录,这可能无关紧要,但如果您需要它,您必须先锁定表,否则许多并发插入都会检查计数是否正常,都看到了,然后都插入一行。

您会发现一般来说,Pg 的计数与 MySQL 中事务安全的 InnoDB 存储引擎 MVCC 的计数并没有太大区别。

如果您使用的是 MyISAM,那么您会以速度换取比我能解决的更多问题;我更喜欢较慢的计数而不是无法回滚,数据修改以避免错误作为无法回滚,缺乏崩溃安全等的解决方法。

于 2013-08-09T01:30:14.167 回答
1

在这种情况下,您可以limit计算:

select count(id) > 100
where id = 1.2.3.4
limit 101

无论符合条件的行数如何,它都会在 101 处停止计数,例如 100,000。如果count(id) > 100它将返回true,否则返回false。

您可以将该测试与插入命令结合起来,这样您就不需要往返服务器:

with c as (
    select count(id) > 100 as c
    from t
    where id = 1.2.3.4
    limit 101
)
insert into t (x, y)
select 1, 2
where (select c from c)
于 2013-08-08T23:46:27.107 回答