【问题标题】:NDB delete & consistencyNDB 删除和一致性
【发布时间】:2017-03-20 21:17:19
【问题描述】:

我了解 NDB 查询(读取)最终是一致的。但是删除和写入呢?

例如,将运行以下代码总是(最终)会导致我们的数据存储区中有一个有缺陷的小部件:

# get rid of defective widgets
defective_widgets = Widget.query(Widget.defective == True).fetch(keys_only=True)
ndb.delete_multi(defective_widgets)

# uh oh, we have a new one
Widget(defective=True).put()

...或者删除操作是否有可能删除新的有缺陷的小部件?

【问题讨论】:

    标签: google-app-engine google-cloud-datastore


    【解决方案1】:

    “...删除操作是否有可能删除新的有缺陷的小部件?”

    没有。

    “将始终(最终)运行以下代码导致我们的数据存储区中有一个有缺陷的小部件”

    也没有。以前有缺陷的小部件可能没有被删除。

    Cloud Datastore 在查找(按键获取)和祖先查询方面高度一致。它最终在非祖先查询上是一致的。思考 Cloud Datastore 如何处理一致性的一种方式是:

    • 实体写入被同步写入/删除
    • 索引是异步更新的(大部分时间是立即更新,但并非总是如此)
    • 实体组定义数据局部性,因此祖先查询强制实现强一致性

    让我们看看你的例子

    为了参数,让我们先设置一个示例数据集。一些代码添加了新的有缺陷的小部件:

    WidgetA(defective=True).put()
    WidgetB(defective=True).put()
    WidgetC(defective=True).put()
    

    现在我们有了:

    • WidgetA(defective: True)
    • WidgetB(defective: True)
    • WidgetC(defective: True)

    进行查找(按键获取)将始终返回这 3 个实体,因为查找是高度一致的。

    在这 3 个小部件被添加后,一些代码想要获取所有有缺陷的小部件的列表。

    defective_widgets = Widget.query(Widget.defective == True).fetch(keys_only=True)
    

    然后发出调用将它们全部删除:

    ndb.delete_multi(defective_widgets)
    

    我们现在在数据库中有哪些数据?答案是 WidgetA、B 和 C 的任意组合,或者它们都没有,甚至全部都没有。为什么?因为发出的查询最终是一致的,并且取决于是否应用了索引更新来确定defective_widgets 中的实体键列表是否。

    因此,此时,在您最终执行 Widget(defective=True).put() 后,您的数据库中可能会有 1 到 4 个有缺陷的小部件。

    如何避免这种情况?

    选项 1(实体组):如果有缺陷的小部件总是以每秒 1 个事务或更低的速度写入/更新,那么您可以将它们全部放在一个实体组中,修改您的查询成为祖先查询 = 然后一切都将最终保持一致。注意:如果您可以在一个事务中一起批处理小部件写入/更新,则所有这些写入仅计为 1 个事务,例如,将 500 个小部件一起批处理允许您每秒执行 500 个小部件。

    选项2(软删除):如果你的写入率很高,你可以实现一个软删除系统。单个实体(缓存,我们称之为“版本实体”)存储一个单调递增的版本号(整数)。任何新编写的有缺陷的小部件也会将此编号存储在名为“版本”的属性中。当您想要删除一组小部件时,将版本实体中的数字加一,并启动对版本低于新版本号的所有小部件的查询。对于处理小部件的任何查询,检查版本是否小于当前版本号,如果是,则从进程中丢弃(它被软删除)。您可以通过包含 version = current 过滤器对其进行优化,以便查询不会返回大多数软删除的项目。

    【讨论】:

      【解决方案2】:

      如果您在 删除操作完成后创建一个新的有缺陷的小部件,为什么您希望它被已执行的早期代码删除?

      第 2 行的 .fetch(keys_only=True) 已经从数据存储中提取了现有密钥的列表(此时新的缺陷实体不存在),因此下一行的 ndb.delete_multi() 无法知道您还没有.put() 的新缺陷小部件因此不会被删除。

      【讨论】:

      • 这就是我所期望的。我在我们的代码中看到了一些令人费解的东西,这可能暗示了其他情况,所以我正在仔细检查我的假设。如果您反转操作,我相信您可以创建一个小部件,然后 not 删除它,因为由于最终的一致性,查询不会返回新的小部件(尚未)。我的问题是,使用上面的代码,查询是否会意外返回(立即)创建的小部件 查询操作,并删除该小部件?
      猜你喜欢
      • 1970-01-01
      • 2012-09-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-10
      相关资源
      最近更新 更多