ruby-on-rails - Rails/Postgres 查找性能

标签 ruby-on-rails ruby database performance postgresql

我有一个状态仪表板,显示每分钟“ping”应用程序并记录其状态的远程硬件设备的状态。

class Sensor < ActiveRecord::Base
  has_many :logs

  def most_recent_log
    logs.order("id DESC").first
  end
end

class Log < ActiveRecord::Base
  belongs_to :sensor
end 

鉴于我只对显示当前状态感兴趣,仪表板只显示所有传感器的最新日志。该应用程序已经运行了很长时间,并且有数千万条 Log 记录。

我遇到的问题是仪表板需要大约 8 秒才能加载。据我所知,这主要是因为有一个 N+1 查询在获取这些日志。

Completed 200 OK in 4729.5ms (Views: 4246.3ms | ActiveRecord: 480.5ms)

我确实有以下索引:

add_index "logs", ["sensor_id", "id"], :name => "index_logs_on_sensor_id_and_id", :order => {"id"=>:desc}

我的 Controller /查找代码如下:

class SensorsController < ApplicationController
  def index
    @sensors = Sensor.all
  end
end

  1. 如何使加载时间合理?
  2. 有没有办法避免 N+1 并重新加载它?

我曾想过将 latest_log_id 引用放在 Sensor 上,然后在每次发布该传感器的新日志时更新它 - 但我脑子里的东西告诉我认为其他开发人员会说这是一件坏事。是这样吗?

通常如何解决此类问题?

最佳答案

有两种相对简单的方法可以做到这一点:

  • 使用 ActiveRecord eager loading 只提取最新的日志
  • 为此目的推出您自己的迷你预加载系统(作为哈希)

基本的 ActiveRecord 方法:

subquery = Log.group(:sensor_id).select("MAX('id')")
@sensors = Sensor.eager_load(:logs).where(logs: {id: subquery}).all

请注意,您不应为每个传感器使用most_recent_log 方法(这将触发 N+1),而应使用 logs.firstlogs 集合中实际上只会预取每个传感器的最新日志。

从 SQL 的角度来看,自己滚动可能更有效,但阅读和使用起来更复杂:

@sensors = Sensor.all
logs = Log.where(id: Log.group(:sensor_id).select("MAX('id')"))
@sensor_logs = logs.each_with_object({}){|log, hash|
  hash[log.sensor_id] = log
}

@sensor_logs 是一个 Hash,允许通过 sensor.id 快速查找最新日志。

关于您关于存储最新日志 ID 的评论 - 您实质上是在询问是否应该构建缓存。答案是“视情况而定”。缓存有很多优点也有很多缺点,因此归结为“ yield 是否值得付出代价”。根据您的描述,您似乎并不熟悉它们引入的困难(谷歌“缓存失效”)或者它们是否适用于您的情况。在您可以证明 a) 它比非缓存解决方案增加了真正的值(value),并且 b) 它可以安全地应用于您的场景之前,我建议不要使用它。

关于ruby-on-rails - Rails/Postgres 查找性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36672481/

相关文章:

sql-server - 为什么使用 Entity Framework 创建 MSSQL(Express) 数据库这么慢?

ruby-on-rails - 如何用 ruby​​ 中的每个干代码?

ruby-on-rails - dig 方法不能与 ActionController::Parameters 一起正常工作

ruby-on-rails - ActiveRecord::未知属性错误

ruby - 在 Ruby 中获取调用类的名称

ruby - 在 Unicorn 启动的 Thin 实例中重置缓存

索引上的 Cassandra IN 子句

mysql - 更好的数据库设计可以在 mysql 中保存大量员工详细信息

ruby-on-rails - 如何在 Elastic Beanstalk 容器中提供 Rails 应用程序的 webpack Assets ?

ruby-on-rails - StatementInvalid SQLite3::SQLException:没有这样的列。将标签系统添加到博客时