gpt4 book ai didi

python - App Engine memcache/ndb.get_multi 的性能问题

转载 作者:太空狗 更新时间:2023-10-29 20:20:10 30 4
gpt4 key购买 nike

在 App Engine (Python) 中使用 ndb.get_multi() 从 Memcache 获取多个键时,我发现性能非常差。

我正在获取约 500 个小对象,所有这些对象都在内存缓存中。如果我使用 ndb.get_multi(keys) 执行此操作,则需要 1500 毫秒或更多时间。以下是 App Stats 的典型输出:

App StatsRPC Stats

如您所见,所有数据均由内存缓存提供。大多数时间被报告为在 RPC 调用之外。但是,我的代码尽可能少,所以如果时间花在 CPU 上,它必须在 ndb 中的某个地方:

# Get set of keys for items. This runs very quickly.
item_keys = memcache.get(items_memcache_key)
# Get ~500 small items from memcache. This is very slow (~1500ms).
items = ndb.get_multi(item_keys)

您在 App Stats 中看到的第一个 memcache.get 是获取一组键的单次提取。第二个 memcache.get 是 ndb.get_multi 调用。

我正在获取的项目非常简单:

class Item(ndb.Model):
name = ndb.StringProperty(indexed=False)
image_url = ndb.StringProperty(indexed=False)
image_width = ndb.IntegerProperty(indexed=False)
image_height = ndb.IntegerProperty(indexed=False)

这是某种已知的 ndb 性能问题吗?与反序列化成本有关吗?还是内存缓存问题?

我发现,如果我不是获取 500 个对象,而是将所有数据聚合到一个 blob 中,我的函数运行时间为 20 毫秒而不是 >1500 毫秒:

# Get set of keys for items. This runs very quickly.
item_keys = memcache.get(items_memcache_key)
# Get individual item data.
# If we get all the data from memcache as a single blob it is very fast (~20ms).
item_data = memcache.get(items_data_key)
if not item_data:
items = ndb.get_multi(item_keys)
flat_data = json.dumps([{'name': item.name} for item in items])
memcache.add(items_data_key, flat_data)

这很有趣,但对我来说并不是真正的解决方案,因为我需要获取的项目集不是静态的。

我看到的性能是典型的/预期的吗?所有这些测量都基于默认的 App Engine 生产配置(F1 实例、共享内存缓存)。是反序列化成本吗?还是由于从内存缓存中获取了多个 key ?我认为问题不是实例启动时间。我使用 time.clock() 调用逐行分析代码,我看到大致相似的数字(比我在 AppStats 中看到的快 3 倍,但仍然很慢)。这是一个典型的配置文件:

# Fetch keys: 20 ms
# ndb.get_multi: 500 ms
# Number of keys is 521, fetch time per key is 0.96 ms

更新:出于兴趣,我还对所有应用引擎性能设置增加到最大值(F4 实例、2400Mhz、专用内存缓存)进行了分析。表现并没有好多少。在更快的实例上,App Stats 计时现在与我的 time.clock() 配置文件匹配(因此 500 毫秒来获取 500 个小对象而不是 1500 毫秒)。但是,它似乎非常慢。

最佳答案

我对此进行了详细调查,问题出在 ndb 和 Python,而不是 memcache。事情如此缓慢的部分原因是反序列化(解释了大约 30% 的时间),其余似乎是 ndb 任务队列实现的开销。

这意味着,如果你真的想要,你可以避免使用 ndb,而是直接从 memcache 中获取和反序列化。在我有 500 个小实体的测试用例中,这提供了 2.5 倍的巨大加速(在生产中的 F1 实例上为 650 毫秒对 1600 毫秒,或者在 F4 实例上为 200 毫秒对 500 毫秒)。这个要点展示了如何做到这一点: https://gist.github.com/mcummins/600fa8852b4741fb2bb1

这是手动内存缓存提取和反序列化的 appstats 输出: app stats for manual memcache fetch and deserialization

现在将其与使用 ndb.get_multi(keys) 获取完全相同的实体进行比较: ndb fetch of same items

将近 3 倍的差异!!

分析每个步骤如下所示。请注意,时间与 appstats 不匹配,因为它们在 F1 实例上运行,所以实时是 3 倍时钟时间。

手动版本:

# memcache.get_multi: 50.0 ms
# Deserialization: 140.0 ms
# Number of keys is 521, fetch time per key is 0.364683301344 ms

对比 ndb 版本:

# ndb.get_multi: 500 ms
# Number of keys is 521, fetch time per key is 0.96 ms

因此,即使实体只有一个属性并且位于内存缓存中,ndb 也需要 1 毫秒来获取每个实体。那是在 F4 实例上。在 F1 实例上需要 3 毫秒。这是一个严重的实际限制:如果您想保持合理的延迟,则在 F1 实例上处理用户请求时,您不能获取超过 100 个以上的任何类型的实体。

很明显,ndb 正在做一些非常昂贵且(至少在这种情况下)不必要的事情。我认为这与它的任务队列以及它设置的所有 future 有关。是否值得绕过 ndb 并手动执行操作取决于您的应用程序。如果您有一些 memcache 未命中,那么您将不得不去执行数据存储提取。所以你基本上最终部分地重新实现了 ndb。然而,由于 ndb 似乎有如此巨大的开销,这可能是值得做的。至少根据我对小对象的大量 get_multi 调用的用例,它看起来是这样,预期内存缓存命中率很高。

这似乎还表明,如果 Google 将 ndb 的一些关键位和/或反序列化实现为 C 模块,Python App Engine 可能会快得多。

关于python - App Engine memcache/ndb.get_multi 的性能问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24030855/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com