Google Datastore中的原子序列计数器

时间:2017-11-13 12:00:09

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

我们在Google App Engine中运行了一个应用程序,作为其操作的一部分,它会生成序列号。这些数字必须符合以下标准:

  • 它们必须介于指定的开始和结束范围之间
  • 它们必须是唯一的(在某些情况下达到范围的末尾,在这种情况下我们可以从序列的开头再次开始)
  • 它们必须是连续的(随机数不好,即使它们符合其他两个标准)

我们编写的代码试图确保生成的数字是全球唯一的,但我不能共享该代码,因为a)它相当复杂,b)它的属性是我的雇主和c)它似乎没有在重负荷下工作。

我已经完成了一些阅读,因为未能确保我们符合独特标准,并找到了有关sharded counters的一些信息,但我认为这种方法可能有助于我仍然没有&# 39;认为它可以保证我们100%肯定会产生独特的序列。我怀疑在数据存储区中存在一些延迟,当涉及到upserts时,更新的计数器与后续读取中反映的更新之间的延迟是罪魁祸首。除了没有处理分片的PHP示例(虽然我们可能从其他示例中找出它,如果有一个有效的PHP示例,那将是很好的)。

我建议的解决方案如下:

  • 在Memcache(支持整数的原子递增)和数据存储(用于持久性)中维护计数器中的当前值。我们也可能尝试在数据存储区中对计数器进行分片。
  • 当来自给定序列的新号码的请求进入时:
    • 检查Memcache以获取当前值。如果数据不在Memcache中,请从Datastore
    • 填充
    • 在Memcache中进行原子增量
    • 使用Memcache返回的值作为我们的流程
    • 将新计数器值写回数据存储以确保持久性

从表面上看,这似乎是一个合理的解决方案,但我担心仍然存在边缘情况,我们最终得到不一致的计数器值,特别是如果同时发生大量更新。虽然Memcache将确保返回值的原子性,但我不确定对数据存储的写入是否会按请求的顺序发生,并且在数据存储结束时数据存储可能无法反映Memcache中可能导致问题的值如果应用程序关闭,并且在恢复服务时从数据存储区加载了错误的值。

数据存储区写入是否按照收到的顺序应用?我可以保证在执行完所有写操作后,Datastore中的值与Memcache中的值匹配吗?有没有更好的解决方案来解决这个问题(除了切换到具有自动增量/序列支持的SQL数据库)?

1 个答案:

答案 0 :(得分:0)

AppEngine数据存储区中的顺序ID是一个难以解决的问题。

如果您尝试使用持久性数据存储区逐个执行此操作,那么您将达到事务吞吐量限制。

我能想到的最佳方法:

  1. 使用分片来保留最后N个计数器值。
  2. 开始交易
  3. 在更新数据存储区碎片之前锁定memcache条目(您可以在Go中使用nds包或以类似方式执行)并通过键从数据存储区获取所有碎片值。基本上在Go中只需为所有分片调用nds.GetMulti()。锁应具有合理的到期值。对于nds包来说,这是30秒。
  4. 获取具有最大值的分片的分片ID和值。
  5. 首先在数据存储中更新相应的分片值 - 将数据存储到db只是单个实体。例如,您可以有10个数字0-9的分片,其中分片编号对应于计数器值中的最后一个数字。所以23将被映射到碎片3。
  6. 提交事务(退出事务功能/上下文
  7. (使用memcache CAS(比较和交换)操作来更新相应的memcache值或从memcache中删除它。虽然做正确的事并不是一件容易的事。你可以阅读我在nds package上开始的讨论 - {{ 3}}
  8. 基本上你可以在没有memcache的情况下完成,但是memcache可以节省你的钱并且可以减少延迟(或不减少)。

    重要的是先锁定内存缓存并更新数据存储,然后从内存缓存中删除或使用CAS进行更新。你的计划相反。