在App Engine上做一个大规模的db.delete,不用吃CPU

时间:2010-12-15 08:42:44

标签: python google-app-engine cpu-usage bigtable

我们在Google App Engine上有一个相当大的数据库 - 只有超过50,000个实体 - 我们希望清除过时的数据。计划是编写一个deferred task来迭代我们不再需要的实体,并批量删除它们。

一个复杂因素是我们的实体也有我们想要清除的子实体 - 没问题,我们想;我们只是查询这些实体的数据存储区,并将其与父项同时删除:

query = ParentKind.all()
query.count(100)
query.filter('bar =', 'foo')
to_delete = []
for entity in enumerate(query):
    to_delete.append(entity)
    to_delete.extend(ChildKindA.all().ancestor(entity).fetch(100))
    to_delete.extend(ChildKindB.all().ancestor(entity).fetch(100))
db.delete(to_delete)

我们限制自己一次删除100个ParentKind个实体;每个ParentKind共有大约40个子ChildKindAChildKindB个实体 - 可能有4000个实体。

当时这似乎是合理的,但我们运行了一个批处理作为测试,结果查询运行了9秒 - 并且在可计算的CPU时间内花费了1933 来访问数据存储区。 / p>

这看起来相当苛刻 - 每个实体0.5个可计费秒! - 但我们并不完全确定我们做错了什么。它只是批量的大小吗?祖先的查询特别慢吗?或者,删除(实际上,所有数据存储访问)是否只是作为糖蜜缓慢?

更新

我们将查询更改为keys_only,虽然这样可以将运行一个批次的时间缩短到4.5秒,但CPU时间仍然需要大约1900秒。

接下来,我们将Appstats安装到我们的应用程序(感谢,kevpie)并运行一个较小的批次 - 10个父实体,总计约450个实体。这是更新后的代码:

query = ParentKind.all(keys_only=True)
query.count(10)
query.filter('bar =', 'foo')
to_delete = []
for entity in enumerate(query):
    to_delete.append(entity)
    to_delete.extend(ChildKindA.all(keys_only=True).ancestor(entity).fetch(100))
    to_delete.extend(ChildKindB.all(keys_only=True).ancestor(entity).fetch(100))
db.delete(to_delete)

Appstats的结果:

service.call           #RPCs  real time  api time
datastore_v3.RunQuery  22     352ms      555ms
datastore_v3.Delete    1      366ms      132825ms
taskqueue.BulkAdd      1      7ms        0ms

Delete电话是操作中最昂贵的部分!

有解决方法吗?尼克约翰逊提到使用bulk delete处理程序是目前删除的最快方法,但理想情况下我们不想删除某种类型的所有实体,只是匹配的实体,以及是我们最初的bar = foo查询的子项。

2 个答案:

答案 0 :(得分:2)

我们最近添加了一个批量删除处理程序,记录为here。虽然它仍然消耗CPU配额,但它采用最有效的批量删除方法。

答案 1 :(得分:1)

如果要分散CPU刻录,可以创建map reduce作业。它仍将遍历每个实体(这是映射器API的当前限制)。但是,您可以检查每个实体是否符合条件并在此时删除。

要减慢CPU使用率,请将映射器分配给您配置为比正常运行速度慢的任务队列。您可以将运行时间分摊几天,而不会耗尽所有CPU配额。