为什么postgresql(v9.6)中COUNT
的时间复杂度似乎和它一样糟糕?
以下内容表明,即使我的机器在资源压力下也不会有3倍以上的记录导致它需要大约9倍的时间
即。的 O(n2)的
(397.0ms) SELECT COUNT(*) FROM "cases"
=> 850219
(3289.1ms) SELECT COUNT(*) FROM "cases"
=> 2577661
我原本预计它可以利用桌子上的一些索引,但我想这只会有助于估计而不是确定计数。
我想这很难改进,但我有兴趣找出原因,以及它是否可调。特别是对于很少写的表我很惊讶,计数无法完全缓存。 DB内部结构中的内容是否很难在表的元数据中包含last_edited
时间戳?
答案 0 :(得分:2)
它是非线性的原因可能是因为hint-bit writing on the first touch of the data,其中 read 查询可以写入。或者它可能归结为缓存效果。
通常,没有合适索引的数据计数在成本上是线性的。通过预热尝试适当重复的基准。
PostgreSQL并不试图保持对表的准确计数,因为它并不关心那么多。它保存了足够好的近似统计数据和数字,如果你想要一个精确计数,你当时就可以执行一次。有时快速而有时慢的计数可能会更令人困惑。
会话1看到一个包含100行的表,但会话2已插入一些,因此它有205,而会话3删除了一些,所以它有50.会话6在会话5提交删除后启动,因此它看到的行少于会话1所做的同一时刻。
它无法保持“已提交的计数器”+每会话计数器,因为回滚可能发生在不可预测的订单中,而“已提交”的内容会因会话的快照而异。它必须在表行计数的一个副表上执行MVCC,其中为每个提交添加了一个具有适当xmin的行,并将它们作为事务快照老化而修剪。这会花费相当多的性能,对大多数工作负载来说几乎没有什么好处。
最重要的是,没有人希望它足以增加对它的支持。通过资助工作或直接编码。这对于一点点好处都是一项重要的工作。
已经做了什么是添加对使用仅索引扫描进行计数的支持,极大地加快PRIMARY KEY
或其他索引列的计数。他们可以使用VACUUM
和可见性映射来跳过堆重新检查以查看未新写入的页面的可见性。