我有一个包含数百万行的postgres数据库,它可以驱动一个Web应用程序。数据是静态的:用户不会写入数据。
我希望能够为用户提供可查询的聚合(例如,具有特定外键值的所有行的总和),但现在数据库的大小意味着计算此类聚合需要10-15分钟。
我应该:
1.唯一的问题是我不一定知道用户想要哪些聚合,而且显然会进一步增加数据库的大小。
如果对于这些问题有比postgres更好的解决方案,那么我会非常感谢任何建议。
答案 0 :(得分:3)
您正在尝试使用OLTP(联机事务处理)数据库结构解决OLAP(联机分析进程)数据库结构问题。
您应该构建另一组表,这些表只存储聚合并在半夜更新这些表。这样,您的客户就可以查询汇总表集,并且根本不会干扰在线转换程序系统。
唯一的问题是汇总数据总是落后一天。
答案 1 :(得分:1)
答案 2 :(得分:0)
如果您有一组常见查询聚合,最好创建一个由触发器维护的聚合表(或与您的OR / M绑定的观察者模式)。
示例:假设您正在编写会计系统。您将所有借方和贷方保留在总帐表(GL)中。这样的表可以在繁忙的组织中快速累积数千万行。要在特定日期找到资产负债表上特定账户的余额,您通常必须计算截至该日期该账户的所有借方和贷方的总和,即使正确计算也可能需要几秒钟的计算索引表。计算资产负债表的所有数字可能需要几分钟。
相反,您可以定义account_balance表。对于每个帐户和感兴趣的日期或日期范围(通常是每个月的结束),您可以通过使用GL表上的触发器来维持余额数字,以通过将每个增量单独添加到所有适用的余额来更新余额。这将在每个持久性上聚合这些数字的成本分摊到数据库,这可能会在保存时将其降低到可忽略的性能损失,并且会降低将数据从大规模线性操作变为接近常数的成本。
答案 3 :(得分:0)
如果将聚合存储在中间对象(类似MyAggragatedResult)中,则可以考虑使用缓存代理:
类ResultsProxy { calculateResult(param1,param2){ ..从缓存中检索 ..如果没有找到,计算并存储在缓存中 }
}
java有很多缓存框架,大多数情况下也适用于其他语言/环境,例如.Net。这些解决方案可以处理失效(结果应该存储在内存中多长时间)和内存管理(在达到内存限制时删除旧的缓存项等)。
答案 4 :(得分:0)
对于该数据量,您不必离开Postgres。
我希望首先调整 - 对于“几百万行”,10-15分钟看起来相当过分。这应该只是几秒钟。请注意,Postgres的开箱即用配置设置不会(或至少没有)分配大量磁盘缓冲区内存。你也可以看一下。
更复杂的解决方案涉及在数据库上实现某种数据集市或OLAP前端,如Mondrian。后者会预先计算聚合并缓存它们。
答案 5 :(得分:0)
如果您有一组常用聚合,您可以在一个单独的表和/或列中预先计算它(例如,每周一次),并且用户可以快速计算它。
但我也在寻求调整方式 - 修改你的索引策略。由于您的数据库是只读的,因此您无需担心索引更新开销。
修改你的数据库配置,也许你可以挤出它的一些性能 - 通常默认配置的目标是让初次使用者的生活更轻松,并且在大型数据库中快速变得短视。
在修改索引和数据库配置之后,甚至一些非规范化可能会加快速度 - 而且还会出现需要更高性能的情况,但尝试将其作为最后的手段。
答案 6 :(得分:0)
Oracle支持一种名为Query Rewrite的概念。这个想法是这样的:
如果希望查找(WHERE ID = val)更快,则添加索引。您不必告诉优化器使用索引 - 它只是这样做。您不必将查询更改为从索引中读取...您按照常规方式执行相同的表但现在不是读取表中的每个块,而是读取一些索引块并知道将在何处执行表
想象一下,如果你可以为聚合添加类似的东西。优化器只是“使用”而不被告知改变的东西。假设您在过去十年中有一个名为DAILY_SALES的表。一些销售经理希望每月销售,一些希望每季度销售,一些希望每年销售。
你可以维护一堆额外的表来保存这些聚合,然后你告诉用户改变他们的查询以使用不同的表。在Oracle中,您将构建它们作为物化视图。除了在源表上定义MV和MV Log之外,您没有任何工作。然后,如果用户按月查询DAILY_SALES总和,ORACLE将更改您的查询以使用适当的聚合级别。关键是没有改变查询。
也许其他DB的支持......但这显然是你在寻找的。