代码之家  ›  专栏  ›  技术社区  ›  Chris Dutrow

计算高数量级计数(100000以上)的匹配行数

  •  7
  • Chris Dutrow  · 技术社区  · 13 年前

    目标:

    当计数的数量级是100000到10000000时,得到两次之间发生的事情的次数。

    当前实施:

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

    列:

    • 事故类型
    • 日期发生的时间

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

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

    问题是:

    这是可行的,但查询效率非常低,大约需要40秒才能做出响应。据我所知,PostgreSQL不是一个用于这种类型查询的好数据库。

    我坐下来思考了几种方法,可以在O(logn)时间内对这种类型的查询进行索引和执行,这样我就知道这是可能的。

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

    注:

    不确定我是否清楚这件事。的结果 COUNT 应该在100000到10000000之间。这意味着与查询匹配的行数将在100000到10000000之间。表中的实际行数要多出一个数量级。

    非常感谢!

    4 回复  |  直到 13 年前
        1
  •  5
  •   Craig Ringer    13 年前

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

    PostgreSQL 9.2实现 index only scans ,这可能有助于缓解某些工作负载的此问题。

    如果您被困在v9.2以下,如果您只需要一个简单查询的近似行数,那么有一些已知的解决方法。看见 http://wiki.postgresql.org/wiki/Count_estimate .

        2
  •  1
  •   Clodoaldo Neto    13 年前

    保留一个按天汇总的事件表。

    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亿行。如果索引正确,它会很快。

        3
  •  1
  •   Borys    13 年前

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

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

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

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

        4
  •  1
  •   Chris Aitchison    13 年前

    这正是维度建模和数据仓库设计要解决的问题。

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

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

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