【问题标题】:Performance issues with EmberJS and Rails 4 APIEmberJS 和 Rails 4 API 的性能问题
【发布时间】:2014-04-09 21:27:45
【问题描述】:

我有一个由 Rails 4 REST API 提供支持的 EmberJS 应用程序。该应用程序运行良好,但根据正在执行的查询类型,它变得非常缓慢。

目前API输出如下:

"projects": [{
    "id": 1,
    "builds": [1, 2, 3, 4]
}]

当用户有很多项目并且在它们之间拆分了很多构建时,就会出现问题。 EmberJS 当前查看builds 键,然后向/builds?ids[]=1&ids[]=2 发出请求,这是我想要的那种行为。

这个问题可能有两种解决方案之一。

  1. 更新 Rails 以更有效地加载 build_ids
  2. 更新 EmberJS 以支持构建的不同查询

选项 1:更新 Rails

我已经尝试了各种关于急切加载和使用序列化程序上的自定义方法手动获取 ID 的解决方案。这两个解决方案都添加了很多我不想做的额外代码,并且仍然对每个项目进行单独的查询。

默认情况下,rails 在执行 has_many 时也会执行SELECT * 样式查询,我不知道如何在序列化程序层覆盖它。我还写了一个可怕的解决方案,它把整个事情都交给一个快速查询,但它涉及编写原始 SQL,我知道这不是 Rails 的做事方式,我宁愿没有如此庞大的复杂的不可测试的查询作为默认范围.

选项 2:让 Ember 使用不同的查询

而不是请求/builds?ids[]=1&ids[]=2,我宁愿在项目中根本不包含构建密钥,而是在我在 Ember 中访问该变量时向/builds?project_id=1 发出请求。我想我可以通过使用类似的东西在每个字段的基础上手动执行此操作:

builds: function () {
    return this.store.find('builds', { project_id: this.get('id') });
}.property()

而不是当前的:

builds: DS.hasMany('build', { async: true })

还值得一提的是,这不仅适用于“构建”。项目对象上还有 4 个其他键执行相同的操作,因此每个项目有 4 个查询。

【问题讨论】:

  • 我认为尽可能使用 ORM。但在必要时用原始 SQL 覆盖。主动记录如此方便,但也如此缓慢。您的数据库上的一些信息可能会有所帮助,是 MySQL 吗?您的数据透视表/联接表上的索引是什么样的?
  • 这个问题是否得到了亚历克斯足够好的马克?你能选择答案吗?你是怎么做到的?很想知道:)

标签: javascript sql ruby-on-rails performance ember.js


【解决方案1】:

您是否确定已正确地向数据库中添加了索引?在project_id 上的构建表上添加和索引将使其工作更快。

或者,您应该使用 links 属性来加载您的记录。

{"projects": [{
    "id": 1,
    "links": {
      "builds": "/projects/1/builds"
    }
}]}

这意味着只有在访问关系时才会查询构建表。

【讨论】:

  • 是的,我有索引,但目前正在通过 WAN 访问数据库。 EmberJS 是否理解“链接”属性?我很想使用这种语法:jsonapi.org/format/#document-url-template-shorthands。我目前正在使用上面提到的选项 2 解决这个问题,这不是理想的 synatx,但与您的建议相同。如果 ember 可以在内部做到这一点,那就完美了。
  • Ember 原生支持这个
【解决方案2】:

你可以尝试的事情:

  • 确保您的 rails 控制器仅选择 JSON 序列化所需的列。

  • 确保在 where 和 join 子句中存在的列上有索引,除非该列是布尔值或不同值的数量很少。始终确保您在外键列上有索引。

  • 在使用 ActiveRecord joins vs includes vs preload vs eagerreferences 时要非常小心。这个领域充满了将范围组合在一起的问题,微妙的事情可能会改变生成的 SQL 和发出的查询数量,甚至是返回的实际结果。我注意到 AR 4 的次要版本的差异会产生不同的查询结果,因为 AR 会选择连接策略。

  • 通常您希望减少向数据库发出的 SQL 数量,但连接表并不总是最好的解决方案。您将需要进行基准测试并使用 EXPLAIN 来查看更适合您的查询的方法。有时子查询/子选择可能更有效。

  • 如果您可以让 Ember Data 以这种方式执行请求,因为数据库的查询更简单,那么通过 parent_id 查询是一个不错的选择。

  • 您可以考虑使用 Ember-Model 而不是 Ember-Data,我目前正在使用它,因为它更简单、更容易适应我的需求,并且支持多提取以避免 1+N 请求问题。

  • 您可以使用嵌入式模型或侧载模型,以便您的服务器可以减少 Web 请求的数量和 SQL 的数量,并在一个请求/一个 SQL 中返回客户端所需的内容。 Ember-Model 支持嵌入式和侧载模型,因此 Ember-Data 也可能更具野心。

  • 虽然从您的问题中可以看出 Ember-Data 正在执行多重提取,但请确保您正在为这些 ID 执行 SQL IN 子句而不是单独的查询。

  • 确保 Rails 端的 SQL 没有以 1+N 模式散开。使用 includes 选项对 AR 关系进行预加载可能有助于避免 1+N 查询,或者根据您的响应所需的结果,它可能会不必要地加载模型。

  • 我还发现 Ruby JSON 序列化程序库不是最佳的。我创建了一个 gem ToJson,它可以比现有解决方案加速 JSON 序列化很多倍。您可以自己尝试并进行基准测试。

我发现 ActiveRecord(包括 AR 4)对我来说效果不佳,最后我转向了 Sequel,因为它让我可以更好地控制连接类型、连接条件、查询组合和战术渴望加载速度更快,对标准 SQL 功能的支持更广泛,对 postgres 功能和扩展的支持也很好。这些事情会对您设计数据库架构的方式以及您可以实现的查询的性能和类型产生巨大的影响。

使用 SequelToJson 对于我的大多数查询,我可以处理的请求比使用 ActiveRecord + JBuilder 多 30-50 倍,在某些情况下,它比我使用 AR 实现的要好数百倍(特别是创建/更新)。除了 Sequel 在从数据库中实例化模型方面速度更快之外,它还有一个 Postgres 流适配器,这使得它再次更快地获得大型结果。

更改数据访问/ORM 层和 JSON 序列化可以将性能提高 30-50 倍,或者在相同负载下需要管理更少的 30-50 个服务器。这没什么好打喷嚏的。

【讨论】:

    猜你喜欢
    • 2013-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-22
    • 1970-01-01
    • 2013-09-23
    • 1970-01-01
    相关资源
    最近更新 更多