【发布时间】: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 之类的方式缓慢、离线地完成。
-
我们计算用户在执行特定操作上所花费的时间。我们有一个小的启发式方法来填补时间序列数据中的空白,并且我们无法保存服务器对此计算的请求。即使触发任务也不是一种选择,因为任务队列的最大任务数非常有限。这就是为什么我们每隔几分钟就将其作为后期处理进行一次。
-
我们可能会以实时处理结束,但这需要
user和timestamp上的多索引,因为有数十万条日志,这并不好。
标签: java performance google-app-engine objectify