在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个ChildKindA
和ChildKindB
实体 - 可能有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