【问题标题】:GAE Datastore read performanceGAE 数据存储读取性能
【发布时间】:2015-10-21 07:33:07
【问题描述】:

我们需要定期处理数千个时间序列实体,我们在从数据存储区读取大量数据时遇到性能问题,这些处理计算量很少,不会导致问题。我们创建了一个模拟真实服务器流量的综合测试,我们在其中使用 25k 实体进行测试。

我们使用 Java 运行时和 Objectify(5.1.1 和 5.1.8)来访问 Datastore。

实体

@Entity(name="logs")
@Cache
public class Log {
    @Id
    public Long id;

    @Index
    public Ref<User> user;

    public String deviceId;
    public String nonce;
    public String version;

    public String data;

    @Index
    public Date timestamp;

    @OnSave
    private void prePersist() {
        if (timestamp == null) {
            timestamp = new Date();
        }
    }
}

查询

query = ofy().load().type(Log.class).
        filter("timestamp >", startDate).
        order("timestamp").
        limit(25000);

我们尝试了不同的实体加载方式。首先query.list() 然后ofy().load().keys(query.keys()) 这样查找将通过GAE 的memcache,但结果是一样的。检索 25k 个实体大约需要 8 秒(通过System.nanoTime() 测量)。在query.list() 的情况下,该调用本身很快,但对实体的迭代很慢。看起来实体是在那一刻从数据存储中检索到的,而不是在query.list() 中。所有这些都是 F4 前端实例上的一个简单 servlet,带有专用的内存缓存,没有任务。

读取 25k 个实体只是为了获取有关我们的服务器实现性能的一些数字的测试。在现实世界中,我们希望一次读取多达 50 万个实体,使用 GAE 的数据存储和专用内存缓存是否可以在 30-60 秒内完成?在 2 年内它可能是数百万个实体。

另一个问题是 RAM 有限,但这可以通过 GAE 的托管 VM 或 GCE 解决。

问题是使用 Objectify 从 Datastore + 专用 memcache 中检索时间序列实体的最快方法是什么。看起来 memcache 在我们的例子中对 Objectify 没有帮助。 memcache 内部有数万个 Objectify 项目,但加载时间与空 memcache 相同。 Objectify/Datastore 的最佳实践是进行批量获取操作,如何实现呢? Objectify 是通过我们的实体和查询在后台执行此操作,还是我们必须更改某些内容?底层 Datastore API 能否帮助我们提高读取性能?谢谢。

编辑 我们已经在努力合并日志,因此每个日志实体都将保存多个当前日志。这将给我们带来大约 10 倍的 reed 改进,这对于数十万条记录来说仍然不够。

【问题讨论】:

  • 我正在尝试设想一个场景,您需要一次读取所有密钥。你到底在做什么?
  • 我们需要读取最近几分钟的日志,然后根据这些日志加载活动用户,然后遍历每个用户的日志,并根据日志中的数据更新用户的实体。处理速度很快(任务 + 线程),但加载日志和用户很慢。我们已经在努力合并日志,因此每个日志实体都将保存多个当前日志。
  • 好吧,我不太了解您的用例,但是您有什么理由不能直接在操作上更新实体,而不是存储日志然后解析它?关键是,正如您所发现的,GAE 并不最适合这种用途。批量操作最好通过 map-reduce 之类的方式缓慢、离线地完成。
  • 我们计算用户在执行特定操作上所花费的时间。我们有一个小的启发式方法来填补时间序列数据中的空白,并且我们无法保存服务器对此计算的请求。即使触发任务也不是一种选择,因为任务队列的最大任务数非常有限。这就是为什么我们每隔几分钟就将其作为后期处理进行一次。
  • 我们可能会以实时处理结束,但这需要usertimestamp 上的多索引,因为有数十万条日志,这并不好。

标签: java performance google-app-engine objectify


【解决方案1】:

此解决方案不太可能按您想要的方式扩展。

查询@Cache 实体默认为“混合”仅键查询(速度极快),然后是批量获取(相对较慢)。如果缓存是温暖的,这可以很好地执行,但可能达不到您所说的规模。最终,即使使用专用的内存缓存,缓存也会被重置 - 然后您的操作可能会超时并失败几次,直到缓存再次预热。

您可以禁用此混合功能:ofy().load().hybrid(false) 或仅删除 @Cache 注释。使用冷缓存,常规查询的性能会显着提高。您也可以尝试将chunk() 大小更改为更大的值。默认值为 20 之类的小值。

通过标准 API 对数据存储的托管 VM 访问(当前)明显慢于从经典 GAE 中访问。这可能会导致这种规模的问题。

数据存储区通常不太适合涉及大量实体的批量读写操作。为此目的,它也往往非常昂贵。您可能会考虑将数据存储用作可靠的“主”副本,并为使用聚集索引的其他从属数据库中的数据编制索引。或者,根据您的持久性要求,只需将辅助数据存储用作主副本。

【讨论】:

  • 我们将尝试chunk(),但看起来这不会像你在开始时所说的那样扩展。在创建日志时,我们可能需要实时处理。
  • 您尚未展示如何迭代结果。如果你只是在做一个 foreach,你会发现更大的块大小会产生巨大的差异。潜在地,您还可以使用投影查询来查找用户,然后将任务排队以在您的时间窗口内独立处理每个用户。
  • 设置更大的块,即使是最大可能值也没有改变任何东西。最后,我们以额外的多索引和更长的请求为代价进行实时计算。正如您所说,数据存储区不适合批量读取。
  • 这不是我想听到的答案。但它是最好的。 Datastore simple 不能扩展到批量读取,如果必须在很短的时间内准备好结果,最好实时进行所有计算。
猜你喜欢
  • 1970-01-01
  • 2011-06-11
  • 1970-01-01
  • 2018-11-03
  • 2017-11-19
  • 1970-01-01
  • 2013-09-22
  • 2012-09-24
  • 2012-11-22
相关资源
最近更新 更多