【问题标题】:Slow deserialization when fetching low level datastore Entity objects from Memcache从 Memcache 获取低级数据存储实体对象时反序列化缓慢
【发布时间】:2024-01-07 10:12:01
【问题描述】:

事实证明,检索存储在 memcache 中的低级数据存储实体非常缓慢。由于 objectify 将实体缓存为低级数据存储 Entity 类型,因此在使用 objectify 从 memcache 中获取许多实体时会导致性能不佳。

真正的问题是为什么从 memcache 中反序列化 Entity 类型很慢?我整理了一个示例项目来演示从 memcache 与纯字符串或简单 Map 中检索实体的差异。

代码如下:

https://github.com/aleemstreak/perftest或相关文件:https://github.com/aleemstreak/perftest/blob/master/src/com/rewardly/perftest/PerftestServlet.java

另外,我部署了它,以便您可以看到它在生产中的差异有多大:aleemsandbox.appspot.com/perftest。它是一个幼稚的分析器,但它确实表现出巨大的性能差异。刷新页面几次以查看差异。这是一些示例输出:

Storing String Data Test
-------------------------
generateData: 0ms
storeData: 10ms
fetchData: 9ms


Storing Map Data Test
-------------------------
generateData: 0ms
storeData: 21ms
fetchData: 92ms


Storing Entity Data Test
-------------------------
generateData: 69ms
storeData: 24ms
fetchData: 792ms

第一部分显示了在内存缓存中存储 1000 个字符串然后立即取回所需的时间。下一个示例对 1000 个 Map 对象执行相同的操作,最后一个示例存储和检索 1000 个低级实体类型。您可以看到检索实体类型的时间大幅增加。

知道为什么实体从 memcache 反序列化的速度会很慢吗?

更新 1

根据其中一个答案中的建议,我还记录了存储在 memcache 中的对象的累积大小,结果没有打印出来。我还添加了另一个测试用例——我没有直接存储实体,而是先自己将实体序列化为 byte[],然后将其存储在 memcache 中。结果如下:

StringBenchmark
----------------
Average Fetch Time: 40.16ms
Fetch Size: 24.41KB


MapBenchmark
----------------
Average Fetch Time: 27.36ms
Fetch Size: 102.54KB


EntityBenchmark
----------------
Average Fetch Time: 1029.88ms
Fetch Size: 463.87KB


EntityPreSerializedBenchmark
----------------
Average Fetch Time: 218.82ms
Fetch Size: 490.23KB

这里有趣的是最后两个结果。尽管它们的大小大致相同,但手动获取和反序列化 byte[] 大约需要 1/5 的时间。

github repo 中的代码已经更新,部署的示例应用程序也有最新的代码,所以请随时在那里运行此测试并查看结果。

【问题讨论】:

  • 我忘记提及的一件事是,这段代码也使用 appstats 进行了分析,所有从 memcache 中获取的数据都显示为
  • 您是否还可以为 EntityPreSerializedBenchmark 发布序列化需要多长时间以及获取时间?
  • 抱歉,获取时间是指 appstats 列出的实际内存缓存的获取时间吗?
  • 看appstats,每次RPC到memcache不到10ms

标签: java google-app-engine entity deserialization objectify


【解决方案1】:

也许我在挑剔你的措辞,但所有高级 API(JDO、JPA 和 Ofy)都使用低级 API,因此所有实体都是真正的 LL API 实体。因此,您注意到“Objectify 将实体缓存为低级数据存储实体类型”,但不是所有更高级别的数据存储 API 都会这样做(假设他们已被指示使用此类缓存)?所以我认为这与ofy没有任何关系。

继续前进,您的第 3 次测试比其他测试花费更长的时间似乎很自然 - 相对于简单的 String 甚至 Map 类型,Entity 类型增加了相当大的开销。我有点惊讶它需要更长的时间,但退后一步,您正在获取 1000 个实体,因此每个实体仍然

我认为您应该再添加 1 个测试。有一个 java API 可以获取实例的内存大小(不记得了)。确定您在测试中使用的实体的内存大小,然后更改字符串测试以使用相同大小的对象。这样我们就可以确定这是否与 Entity 类型本身有关,或者仅仅是在第三次测试中缓存的对象的大小相当大的结果。


更新以响应新的测试结果...有趣。这似乎证实了您的理论,即 memcache 代码完成的序列化导致速度变慢。不过,这很奇怪 - memcache 不会以与您正在执行的操作非常相似的方式简单地序列化对象吗?

也许这篇文章有帮助:
manual serialization / deserialization of AppEngine Datastore objects


您用于获取实体对象的内存缓存接口是:

java.lang.Object get(java.lang.Object key)

https://developers.google.com/appengine/docs/java/javadoc/com/google/appengine/api/memcache/MemcacheService#get(java.lang.Object)

所以它没有采用可序列化的方式,而不是按照你的方式序列化,它可能正在使用内省。这可以解释为什么“EntityPreSerializedBenchmark”比“EntityBenchmark”快得多。

【讨论】:

  • 其实我的意思是当它启用缓存时,Ofy 将低级实体类存储在 MEMCACHE 中。我不认为 JDO/JPA 可以自动缓存在 memcache 中,所以我认为这对那些框架来说不是问题。
  • 尽管如此 - 问题仍然存在。我更新了我的问题以考虑您的反馈,并尝试了一种将实体对象序列化为 byte[] 的方法,然后再将它们放入 memcache 中,同样,它们在获取后从 byte[] 反序列化为 Entity 对象(这包含在 fetch基准时间。(我更新了基准代码并部署它供 ppl 使用)
  • 更新 Q 以响应新信息。
  • 不幸的是,该帖子是针对 python 的。
【解决方案2】:

原来是 appengine sdk 中的一个错误。他们已经意识到了问题并可能正在修复。

【讨论】:

    【解决方案3】:

    App Engine 1.9.5 SDK 中修复了序列化性能的这种回归。

    【讨论】:

      最近更新 更多