7

客观的:

当计数的数量级为 100,000 - 10,000,000 时,获取两次之间发生某事的次数。

当前实施:

  • 使用 PostgreSQL
  • 每个“事件”都记录为表格中的单独行

列:

  • 事件类型
  • 它发生的日期时间

获取计数的查询(伪代码):

COUNT rows WHERE time_occurred > <begin_time> AND time_occurred < <end_time>

问题:

这可行,但查询效率非常低,需要大约 40 秒才能响应。据我了解,PostgreSQL 不是用于此类查询的好数据库。

我坐下来想了几种方法可以在 O(log n) 时间内索引和执行这种类型的查询,所以我知道 t 是可能的。

我应该使用什么工具来做到这一点?我们应该使用不同的数据库来存储计数行吗?有没有我们可以在 PostgreSQL 上安装的软件包来轻松完成这项工作?我们有哪些选择?

笔记:

不确定我是否清楚这一点。的结果COUNT应该在 100,000 - 10,000,000 的数量级上。这意味着与查询匹配的行数大约为 100,000 - 10,000,000。表中的实际行数要多一个数量级。

非常感谢!

4

4 回答 4

5

在 PostgreSQL 9.2 之前,MVCC 的实现需要任何查询来访问表的每一行,以检查该行的版本是否对当前事务可见。即使查询只涉及索引列,也会发生这种情况。这表现为对大型表的缓慢计数,即使对于简单的情况也是如此。

PostgreSQL 9.2 实现了仅索引扫描,这可能有助于缓解某些工作负载的这个问题。

如果您被困在 v9.2 以下,如果您只需要简单查询的大致行数,则有一些已知的解决方法。请参阅http://wiki.postgresql.org/wiki/Count_estimate

于 2013-03-10T07:31:29.213 回答
1

另一种方法可能是对表进行分区。这家伙似乎已经解决了一个非常相似的分区问题:

http://www.if-not-true-then-false.com/2009/performance-testing-between-partitioned-and-non-partitioned-postgresql-tables-part-3/

我对使用他的方法的担忧是可维护性。在他的示例中(您必须单击本教程的第 1 部分才能查看他是如何创建分区的),他手动创建每个子表,并在触发器中硬编码路由到子表。如果您的表不断增长,那么您将做大量的 DBA 工作。

然而,他的表现似乎确实得到了很大的提升。所以,如果你能弄清楚如何使它更易于维护,这可能是一个很好的方法。

于 2013-03-11T22:12:56.773 回答
1

这正是维度建模和数据仓库旨在解决的问题。

我之前的一个项目在几周内用 Ruby 构建了一个数据仓库,以便处理这样的查询,并通过一个简单的 REST API 将其公开给主应用程序。基本上,您提取数据并将其转换为“星型模式”,该模式针对您描述的查询进行了高度优化。

Postgresql 非常适合作为数据仓库数据库。

这是一个非常详细的主题,一个很好的入门资源是: http ://www.amazon.com/Data-Warehouse-Toolkit-Complete-Dimensional/dp/0471200247

于 2013-03-11T22:54:26.743 回答
1

保留按天汇总的事件表。

create table incidents_agreggated_by_day (
    "day" date primary key, total integer
);

日常跑:

insert into events_agreggated_by_day ("day", total) values
select date_trunc('day', time_occurred), count(*) total
from incidents
where 
    time_occurred < current_date
    and date_trunc('day', time_occurred) not in (
        select "day" from incidents_agreggated_by_day
    )
group by 1

假设您想要 '2013-01-01 10:37' 和 '2013-03-02 11:20' 之间的总数:

select
(
    select sum(total)
    from incidents_aggregated_by_day
    where "day" >= '2013-01-02'::date and "day" < '2013-03-02'::date
) +
(
    select count(*)
    from incidents
    where 
        time_ocurred >= '2013-01-01 10:37':timestamp
        and time_ocurred < '2013-01-02'
        or
        time_ocurred <= '2013-03-02 11:20':timestamp
        and time_ocurred >= '2013-01-02'
) total

您将阅读数百或数千行,而不是阅读 1 亿行。如果正确索引它会很快。

于 2013-03-11T00:42:25.023 回答