【问题标题】:GAE Lookup Table Incompatible with Transactions?GAE 查找表与事务不兼容?
【发布时间】:2011-09-16 22:55:27
【问题描述】:

我的 Python 高复制数据存储应用程序需要一个包含 100,000 到 1,000,000 个条目的大型查找表。我需要能够为某些方法提供代码,该方法将返回与该代码关联的值(如果没有关联,则返回 None)。例如,如果我的表包含可接受的英文单词,那么我希望函数在找到该单词时返回 True,否则返回 False(或 None)。

我当前的实现是为每个表条目创建一个无父实体,并让该实体包含任何关联数据。我将该实体的数据存储键设置为与我的查找代码相同。 (我将所有实体放入它们自己的命名空间以防止任何键冲突,但这对于这个问题不是必需的。)然后我只需在代码上调用 get_by_key_name() 并获取相关数据。

问题是我无法在事务期间访问这些实体,因为我试图跨越实体组。回到我的例子,假设我想对聊天会话中使用的所有单词进行拼写检查。我可以访问聊天中的所有消息,因为我会给他们一个共同的祖先,但我无法访问我的词表,因为那里的条目是无父的。我必须能够在事务期间引用该表。

请注意,我的查找表是固定的,或者很少更改。这再次与拼写检查示例匹配。

一种解决方案可能是在一个事务期间加载聊天会话中的所有单词,然后对它们进行拼写检查(保存结果),然后启动第二个事务,对保存的结果进行拼写检查。但这不仅效率低下,而且聊天会话可能已添加到事务之间。这似乎是一个笨拙的解决方案。

理想情况下,我想告诉 GAE 查找表是不可变的,因此我应该能够查询它而不会抱怨事务中跨越实体组。但是,我看不出有任何方法可以做到这一点。

将表条目存储在内存缓存中很诱人,但这也有问题。数据量很大,但更麻烦的是,如果 GAE 启动了一个 memcache 条目,我将无法在事务期间重新加载它。

有人知道大型全局查找表的合适实现吗?

请理解,我不是在寻找拼写检查网络服务或类似的东西。我以单词查找为例只是为了说明这个问题,我希望为任何类型的大型查找表提供通用解决方案。

【问题讨论】:

    标签: python google-app-engine transactions google-cloud-datastore entity-groups


    【解决方案1】:

    如果可以,请尝试将数据放入实例内存中。如果它不适合实例内存,您可以使用一些选项。

    如果数据不经常更改,您可以将数据存储在随应用上传的资源文件中,然后从磁盘访问它。这假设您可以构建一个允许轻松查找磁盘的数据结构 - 实际上,您正在实现自己的基于磁盘的只读表。

    同样,如果它太大而不适合作为静态资源,您可以采用与上述相同的方法,但将数据存储在 blobstore 中。

    如果您的数据绝对必须在数据存储中,您可能需要模拟自己的读取-修改-写入事务。将“修订”属性添加到您的记录中。要修改它,获取记录(在事务之外),执行所需的更改,然后在事务内部,再次获取它以检查修订值。如果没有更改,请在您自己的记录中增加修订并将其存储到数据存储中。

    请注意,底层 RPC 层在理论上确实支持多个独立的事务(和非事务性操作),但 API 目前没有公开任何从事务中访问它的方法,除了可怕的(我的意思是真的很可怕) ) hack,不幸的是。

    最后一个选项:您可以运行配置有更多内存的后端,公开“SpellCheckService”,然后从前端对其进行 URLFetch 调用。请记住,内存中的速度总是比任何基于磁盘的选项快得多。

    【讨论】:

    • +1 建议将其放入内存中。对于固定数据,这可能是最佳选择。
    • 关于您的第一个建议,我是否正确理解我可以将表保留在数据存储中,然后在开始事务之前将整个表读入实例内存?这可行,但表占用​​大约 3MB,我只需要少量表查找。因此,当我只需要几百字节时,我会读取 3MB,这听起来效率低下。或者你有什么建议?实例内存无法在请求中生存(除了写入内存缓存)吗? (感谢您的回复——至少在周日晚上!)
    • 至于你的其他想法,我理解“修订属性”的想法,可以接受。我还没有了解 blobstore 或后端,所以接下来我会研究这些。我喜欢使用后端的声音;该解决方案有什么缺点吗?附:我仍然认为让 GAE 支持任何事务都可以访问的不可变数据存储实体是个好主意!
    • @DragonFly 实例持续存在多个请求,因此,所有全局变量和类变量(基本上任何非请求范围的变量)也是如此。您可以在启动时读取数据,并在实例持续存在时重复引用它。
    • 啊!我学到了一些新东西。这是个好消息。我会给你+1,但我对此太陌生了。不过,我要感谢你。
    【解决方案2】:

    首先,如果您认为命名空间有助于避免键冲突,那么是时候退后一步了。键由实体种类、命名空间、名称或 id 以及实体可能具有的任何父项组成。两种不同的实体类型具有相同的名称或 ID 是完全有效的。因此,如果您有一个要匹配的 LookupThingy,并且通过指定唯一名称创建了每个成员,则该键不会与其他任何内容发生冲突。

    至于在事务中对无父查找表进行等效的拼写检查的挑战,是否可以将查找表保留在代码中?

    或者你能想出一个更接近你需要的类比吗?是否激发了在事务中进行查找的需要?

    【讨论】:

    • 是的,你一定是对的,单独的命名空间没有增加任何保护。我原以为这可能会避免键冲突,但即使在默认命名空间内,这种方法也应该充分做到这一点。另一个例子:假设您想将数据与每个邮政编码相关联;例如,您可能有平均收入数据或其他人口统计数据。您将如何从事务中查阅此表?这似乎是一个非常普遍的问题。一百万个硬连接到 Python 代码中的条目?我想这会奏效,但看起来很笨拙。这是唯一的解决方案吗?
    • 为什么必须在事务中完成对查找表的匹配?您是否试图隔离对查找表的不频繁更改?
    • 这里没有足够的空间来完全解释事务的必要性,但这与查找表无关。这是出于通常的原因:我正在对实体组进行更改,并且必须以原子方式处理数据。我尝试用 Python 表示所有表数据,这超出了代码允许的空间量(“软进程大小限制”)。我想这是意料之中的。这个问题仍然没有答案。
    • 是什么阻止您在开始事务之前查询查找表?
    • 因为我当时没有数据。我相信使用事务的正确方法是:开始事务,进行读取,进行计算,进行写入,结束事务。如果我在事务开始之前阅读,那么我会为另一个进程在事务开始之前更改数据留出空间。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-07
    • 2018-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-13
    • 1970-01-01
    相关资源
    最近更新 更多