关于postgresql:Postgres计数(*)优化的想法

Postgres count(*) optimization idea

我目前正在研究一个项目,该项目涉及跟踪用户及其在我的数据库中的操作(PostgreSQL作为RDM),并且我在尝试对每个用户的出现次数执行count(*)时遇到了一个问题。我想要的是能够高效地计算每个用户在每个记录中出现的次数,并且能够在特定日期范围内查看计数。

所以,问题是我们如何计算用户从表内容中出现的总次数,以及如何计算日期范围内的总次数。

我试过什么

正如您可能知道的,Postgres不支持使用索引的count(*),因此我们必须考虑其他方法来减少它查看的记录的数量,以便加快查询速度。因此,我的第一个方法是创建一个表,以跟踪用户有一条与之相关联的日志消息的次数,以及在哪一天(类似于物化视图背后的想法,但我不希望用我的计数查询不断刷新物化视图)。以下是我的想法:

1
2
3
4
5
6
CREATE TABLE users_counts(USER VARCHAR(65536), counter INT DEFAULT 0, DAY DATE);

CREATE RULE inc_user_date_count
AS ON INSERT TO main_table
DO ALSO UPDATE users_counts SET counter = counter + 1
WHERE USER = NEW.user AND DAY = DATE(NEW.date_);

每次将新记录插入"主记录表"时,我们都会更新当前用户计数表以增加日期等于新记录日期的记录,并且用户名相同。

注意:"主表"中的日期列是时间戳,因此我必须将新记录日期转换为日期类型。

问题是,如果用户列值在我的新表"用户数"中不存在,那么什么都不会更新。

我的问题是:

如何编写规则,以便检查当前日期是否存在用户,如果存在,则递增该计数器,否则插入用户、日期和计数器为1的新行;

我还想知道我的方法是否有意义,或者有什么想法我错过了,我只是没有想过。随着数据库的增长,执行计数的效率越来越低,因此我希望避免任何性能瓶颈。

编辑1:我可以通过创建单独的规则来实际解决这个问题,但我不确定这是否正确:

1
2
3
4
CREATE RULE test_insert AS ON INSERT TO main_table
DO ALSO INSERT INTO users_counts(USER, counter, DAY)
SELECT NEW.user, 1, DATE(NEW.date)
WHERE NOT EXISTS (SELECT USER FROM users.log_messages WHERE USER = NEW.user_);

基本上,如果用户不在我的缓存表中,即用户计数中,则会发生插入,并且上面的第一个规则会更新计数。

我不确定的是如何知道何时首先调用哪个规则,更新规则或插入。必须有更好的方法,我如何结合这两个规则?这可以用函数来完成吗?


事实上,PostgreSQL在计算(*)查询时速度非常慢。但是,如果您确实有一个限制条目数的WHERE子句,那么查询将更快。如果您使用的是PostgreSQL 9.2或更高版本,那么这个查询将和MySQL中的查询一样快,因为9.2中添加了仅索引扫描,但最好解释一下分析您的查询以确保。

我的解决方案有意义吗?

如果你的解释分析显示没有使用只包含索引的扫描,那就非常好了。基于触发器的解决方案,如您已经适应的解决方案,可广泛使用。但是,当您意识到初始状态的问题出现时(无论是进行更新还是插入)。

先叫哪个规则

Multiple rules on the same table and same event type are applied in
alphabetical name order.

来自http://www.postgresql.org/docs/9.1/static/sql-createrrule.html同样适用于触发器。如果你想执行一个特定的规则,首先改变它的名称,使它按字母顺序排列得更高。

我如何结合这两个规则?

一种解决方案是修改规则以执行upsert(在该页面的底部查看示例upsert)。另一种方法是用初始值填充计数器表。诀窍是同时创建触发器以避免错误。这篇博文解释得很好。

虽然最初的设置会很慢,但是每个单独的插入可能会更快。两个相反的因素是Where-Not-exists查询的速度慢与捕获异常的开销。

Tip: A block containing an EXCEPTION clause is significantly more
expensive to enter and exit than a block without one. Therefore, don't
use EXCEPTION without need.

来源于上面链接的PostgreSQL文档页面。