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

我们在Google App Engine上有一个尺寸合理的数据库 - 只有超过50,000个实体 - 我们希望从中清除陈旧的数据。 计划是编写一个延迟任务来迭代我们不再需要的实体,并批量删除它们。

一个复杂因素是我们的实体也有我们也想清除的子实体 - 我们认为没有问题; 我们只需查询这些实体的数据存储,然后将它们放在父代的同一时间:

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秒的时间运行 - 并花费了1933秒的时间访问数据存储。

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

更新

我们将查询改为了keys_only ,虽然这减少了将一批运行时间缩短到4.5秒的时间,但CPU时间仍需keys_only 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呼叫是操作中最昂贵的部分!

有没有解决的办法? Nick Johnson提到使用批量删除处理程序是目前删除的最快方法,但理想情况下,我们不希望删除所有类型的实体,只是与我们的初始bar = foo查询匹配并且是子级的实体。


如果你想分散CPU烧录,你可以创建一个地图缩小作业。 它仍然会迭代每个实体(这是mapper API的当前局限性)。 但是,您可以检查每个实体是否符合条件并在当时删除或不删除。

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


我们最近添加了一个批量删除处理程序,记录在这里。 尽管它仍然占用CPU配额,但它采用了最有效的批量删除方法。

链接地址: http://www.djcxy.com/p/3979.html

上一篇: Do a mass db.delete on App Engine, without eating CPU

下一篇: How do I discard unstaged changes in Git?