2

我试图使用AppEngine实现一个大型(ish)数据集的汇总视图。如何汇总Google AppEngine中的数据

我的模型看起来像:

def TxRecord(db.Model): 
    expense_type = db.StringProperty() 
    amount = db.IntegerProperty() 

def ExpenseType(db.Model): 
    name = db.StringProperty() 
    total = db.IntegerProperty() 

我的数据存储包含的TxRecord 100K情况下,我想通过expense_type总结这些。

在SQL它会是这样的:

select expense_type as name, sum(amount) as total 
    from TxRecord 
    group by expense_type 

什么我目前做的是使用Python MapReduce framework遍历所有TxRecords的使用下面的映射:

def generate_expense_type(rec): 
    expense_type = type.get_or_insert(name, name = rec.expense_type) 
    expense_type.total += rec.amount 

    yield op.db.Put(expense_type) 

这似乎工作,但我觉得我必须使用1的shard_count来运行它,以确保总数不会被写入并发写入。

有没有一种策略可以用来使用AppEngine来解决这个问题或者它是什么?

回答

3

使用mapreduce是正确的方法。正如David所言,计数器是一种选择,但它们不可靠(它们使用memcache),而且它们并不是为大量计数器并行设计的。

您当前的mapreduce有几个问题:首先,get_or_insert每次调用时都会执行数据存储事务。其次,您然后更新事务之外的数量并第二次异步存储它,生成您所关注的并发问题。

至少要等到减少是完全支持,最好的选择是做全更新的映射器在一个事务中,像这样:

def generate_expense_type(rec): 
    def _tx(): 
     expense_type = type.get(name) 
     if not expense_type: 
     expense_type = type(key_name=name) 
     expense_type.total += rec.amount 
     expense_type.put() 
    db.run_in_transaction(expense_type) 
3

使用MapReduce框架是一个好主意。如果利用MapReduce框架提供的计数器,则可以使用多个分片。因此,而不是每次修改数据存储,你可以做这样的事情:

yield op.counters.Increment("total_<expense_type_name>", rec.amount) 

MapReduce的结束(希望更快地当你只用一个碎片比)后,你就可以将最终确定计数器复制到你的数据存储实体。

+0

我目前正在尝试使用内存缓存条目类似的东西。我无法与op.counters一起工作的是如何获得回调处理程序中的计数器......是否有时间处理另一个问题? – 2011-03-27 11:29:22