我们在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个子ChildKindA
和ChildKindB
个实体 - 可能有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
查询的子项。
答案 0 :(得分:2)
我们最近添加了一个批量删除处理程序,记录为here。虽然它仍然消耗CPU配额,但它采用最有效的批量删除方法。
答案 1 :(得分:1)
如果要分散CPU刻录,可以创建map reduce作业。它仍将遍历每个实体(这是映射器API的当前限制)。但是,您可以检查每个实体是否符合条件并在此时删除。
要减慢CPU使用率,请将映射器分配给您配置为比正常运行速度慢的任务队列。您可以将运行时间分摊几天,而不会耗尽所有CPU配额。