连续聚合大型数据集

时间:2016-01-23 01:11:21

标签: python mysql cron aggregate

我试图想出一个算法来解决我遇到的这个问题。这不是硬件问题,而是我正在进行的一个副项目。

表格A 大约有10 ^ 5行的顺序,并且每天以10 ^ 2的顺序添加新的行。

表B 大约有10 ^ 6行,每天新增10 ^ 3。从A到B有一对多关系(A中某些行有很多B行)。

我想知道如何为这种数据进行连续聚合。我希望每隔约10分钟运行一次这样的工作并执行此操作:对于A中的每一行,查找在最后一天,一周和一个月中创建的B中与其相关的每一行(然后按计数排序)并保存他们在不同的数据库中或缓存它们。

如果这令人困惑,这里有一个实际的例子:Say表A有亚马逊产品,表B有产品评论。我们希望在过去4小时,每天,每周等显示最高评价的产品排序列表。新产品和评论会快速添加,我们希望上述列表能够达到最佳状态 - 尽可能的日期。

我当前的实现只是一个for循环(伪代码):

result = []

for product in db_products:
    reviews = db_reviews(product_id=product.id, create>=some_time)
    reviews_count = len(reviews)
    result[product]['reviews'] = reviews
    result[product]['reviews_count'] = reviews_count

sort(result, by=reviews_count)

return result

我每小时都这样做,并将结果保存在json文件中以供服务。问题是,这并不能很好地扩展,并且需要很长时间才能进行计算。

那么,我在哪里可以解决这个问题呢?

更新

感谢您的回答。但我最终学习并使用Apache Storm。

3 个答案:

答案 0 :(得分:1)

要求摘要

在数据库中有两个较大的表,您需要定期为过去的时间段(小时,天,周等)创建一些聚合,并将结果存储在另一个数据库中。

我将假设,一旦过去一段时间,相关记录就没有变化,换句话说,过去一段时间的汇总总是有相同的结果。

提议的解决方案:Luigi

Luigi是管道相关任务的框架,其中一个典型用途是计算过去时期的聚合。

概念如下:

  • 编写简单的Task实例,它定义了所需的输入数据,输出数据(称为Target)和创建目标输出的过程。
  • 任务可以参数化,典型参数是时间段(特定日,小时,星期等)
  • Luigi可以在中间停止任务并在稍后开始。它将考虑任何已完成的目标,并且不会重新运行它(您必须删除目标内容才能重新运行)。

简而言之:如果目标存在,任务就完成了。

这适用于多种类型的目标,例如本地文件系统,hadoop,AWS S3以及数据库中的文件。

为了防止半成品结果,目标实现会处理原子性,例如,文件首先在临时位置创建,并在完成后立即移动到最终目的地。

在数据库中有一些结构可以表示某些数据库导入已完成。

您可以自由创建自己的目标实现(它必须创建一些东西并提供方法exists来检查,结果存在。

使用Luigi完成任务

对于您描述的任务,您可能会找到您需要的所有内容。只有一些提示:

luigi.postgres.CopyToTable允许将记录存储到Postgres数据库中。目标将自动创建所谓的“标记表”,它将标记所有已完成的任务。

其他类型的数据库也有类似的类,其中一个使用SqlAlchemy,它可能涵盖您使用的数据库,请参阅类luigi.contrib.sqla.CopyToTable

在Luigi,doc是importing data into sqlite database

的例子

在StackOverflow答案中,完整的实现超出了扩展的可行性,但我相信,您将体验到以下内容:

  • 执行任务的代码非常清楚 - 没有样板编码,只写下必须完成的任务。
  • 很好地支持使用时间段 - 甚至可以从命令行查看,例如Efficiently triggering recurring tasks。它甚至可以在过去不会太过分,以防止生成太多可能使服务器过载的任务(默认值设置得非常合理且可以更改)。
  • 在多个服务器上运行任务的选项(使用随Luigi实现提供的中央调度程序)。

我已经使用Luigi处理了大量的XML文件,并且还完成了一些任务,将聚合数据导入数据库并且可以推荐它(我不是Luigi的作者,我只是很高兴的用户)。

加速数据库操作(查询)

如果您的任务执行数据库查询的执行时间过长,则几乎没有选项:

  • 如果您计算Python的每个产品的评论,请考虑尝试SQL查询 - 它通常要快得多。应该可以创建在正确记录上使用count的SQL查询,并直接返回所需的数字。使用group by,您甚至可以在一次运行中获得所有产品的摘要信息。
  • 设置正确的索引,可能在“产品”和“时间段”列的“评论”表中。这将加速查询,但请确保,它不会减慢过多插入新记录的速度(索引太多会导致这种情况)。

可能会发生这种情况,即使不使用Luigi,通过优化的SQL查询,您也可以获得有效的解决方案。

答案 1 :(得分:1)

数据仓库?汇总表是正确的方法。

数据是否发生变化(一旦写入)?如果是这样,那么逐步更新摘要表就成了一个挑战。大多数DW应用程序都没有这个问题

在插入原始数据表时更新汇总表(日期+维度+次数+次数+总和)。由于每分钟只获得一次插入,INSERT INTO SummaryTable ... ON DUPLICATE KEY UPDATE ...将足够,并且比每10分钟运行一次脚本更简单。

从汇总表中进行任何报告,而不是原始数据(Fact表)。它会更快很多

My Blog on Summary Tables讨论了详细信息。 (它针对更大的DW应用程序,但应该是有用的阅读。)

答案 2 :(得分:0)

我同意Rick,汇总表对你来说最有意义。每10分钟更新一次汇总表,只需从中提取数据,作为用户的请求摘要。

此外,请确保您的数据库已正确编制索引以提高性能。我确定db_products.id设置为唯一索引。但是,还要确保db_products.create被定义为DATE或DATETIME,并且因为您在WHERE语句中使用它而编制索引。