我正在尝试使用AppEngine实现对大型(ish)数据集的摘要视图。
我的模型看起来像:
def TxRecord(db.Model):
expense_type = db.StringProperty()
amount = db.IntegerProperty()
def ExpenseType(db.Model):
name = db.StringProperty()
total = db.IntegerProperty()
我的数据存储区包含100个TxRecord
个实例,我想通过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)
这似乎有效,但我觉得我必须使用shard_count
为1来运行它,以确保总数不会超过并发写入。
我是否可以使用AppEngine来解决此问题的策略,还是它?
答案 0 :(得分:3)
使用MapReduce框架是个好主意。如果使用MapReduce框架提供的计数器,则可以使用多个分片。因此,不是每次都修改数据存储区,而是可以执行以下操作:
yield op.counters.Increment("total_<expense_type_name>", rec.amount)
MapReduce完成后(希望比仅使用一个分片时快得多),然后您可以将最终的计数器复制到数据存储区实体中。
答案 1 :(得分:3)
MapReduce非常适合离线处理数据,我喜欢David处理计数器的解决方案(+1 upvote)。
我只是想提一下另一种选择:处理数据。请查看Brett Slatkin在IO 2010上发表的High Throughput Data Pipelines on App Engine演讲。
我在一个简单的框架(slagg)中实现了这项技术,您可能会找到我的grouping with date rollup useful示例。
答案 2 :(得分:3)
使用mapreduce是正确的方法。正如大卫建议的那样,计数器是一种选择,但它们不可靠(它们使用内存缓存),并且它们不是为大量计数器并行设计的。
您当前的mapreduce有几个问题:首先,get_or_insert
每次调用时都会执行数据存储区事务。其次,然后更新事务外部的数量并再次异步存储它,生成您关注的并发问题。
至少在完全支持reduce之前,最好的选择是在事务中执行mapper中的整个更新,如下所示:
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)