繁体   English   中英

Rails / Postgres查找性能

[英]Rails / Postgres Lookup Performance

我有一个状态仪表板,显示每分钟“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}

我的控制器/查找代码如下:

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

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

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

这样的问题通常如何解决?

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

  • 使用ActiveRecord预先加载来引入最新的日志
  • 为此目的,滚动您自己的迷你渴望加载系统(作为哈希)

基本的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.first 实际上只会在logs集中预取每个传感器的最新日志。

从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的评论 - 您实质上是在询问是否应该构建缓存。 答案是'这取决于'。 缓存有许多优点和许多缺点,因此它归结为“价值成本的好处”。 根据您所描述的内容,您似乎并不熟悉它们引入的困难(Google“缓存失效”),或者它们是否适用于您的情况。 我建议反对它,直到你可以证明a)它在非缓存解决方案上增加了真正的价值,并且b)它可以安全地应用于你的场景。

有3种选择:

  1. 急切的装载
  2. 加盟
  3. 缓存当前状态

-

  1. 由PinnyM解释

  2. 您可以从Sensor连接到每行的最新日志记录,以便在一个查询中获取所有内容。 不确定你的行数如何与你拥有的行数相比,可能它仍然比你想要的慢。

  3. 你提到的事情 - 缓存latest_log_id (甚至只缓存latest_status如果这是仪表板所需的全部内容)实际上是可以的。 它被称为非规范化 ,如果仔细使用它是一个有用的东西。 为了能够优化读取性能,您可能会遇到同样存在的“计数器缓存”插件 - 复制数据。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM