【问题标题】:How do I optimize these scopes如何优化这些范围
【发布时间】:2019-05-15 17:49:52
【问题描述】:

我们在一个模型中有多个范围。 随着数据库的增长,范围变得不可用。

在这种类型的查询中使用范围是问题开始的地方: where("devices.id IN (?)", ETC

以下是我们的一些范围:

scope :reserved, -> { joins("INNER JOIN reservations ON (devices.ipaddress = reservations.ip)") }
scope :in_dhcp_range, -> {
    joins(
      "INNER JOIN dhcp_ranges ON (devices.ipaddress >= dhcp_ranges.start_ip AND devices.ipaddress <= dhcp_ranges.end_ip)"
    ).distinct
  }
scope :alternate_aws, -> {
    joins("INNER JOIN awssubnets ON (devices.ipaddress <<= awssubnets.cidr)").distinct
  }

scope :dhcp_full_calculation, -> {
    where("devices.id IN (?)",
      (Device.in_dhcp_range.select("devices.id") + Device.alternate_aws.select("devices.id")).uniq - Device.reserved)
  }

当数据库只有 50,000 条记录时,这些范围有效。但是,超过 25 万条记录,我们在这个范围内存在性能问题:dhcp_full_calculation

rails 5.2.3,ruby 2.6.3,使用 postgres 数据库

【问题讨论】:

  • 我相信这个想法很好,但我得到了这个错误:ArgumentError: Relation pass to #or must be structurely compatible。不兼容的值:[:select, :joins, :distinct]
  • 优秀。如果您可以设置为答案,我可以投票。

标签: ruby-on-rails postgresql optimization activerecord


【解决方案1】:

所以整体性能问题与发生在数据库之外的计算操作有关,例如

(Device.in_dhcp_range.select("devices.id") + 
 Device.alternate_aws.select("devices.id")).uniq 
   - Device.reserved)

这是执行 3 个查询,然后在 ActiveRecord::Relations 上使用 Array(或 Array like)方法返回要选择的 Devices 列表。这将导致大量计算以及查询中非常大的IN 子句(在某些时候会超过分配给 SQL 语句的字符数)

相反,您应该将所有这些工作放在数据库本身上,使用子查询来构建 where 子句,就像这样

scope :dhcp_full_calculation, -> {
  where.not(id: Device.reserved.select(:id) ).where(
    id: Device.select(:id).where(id: 
         Device.in_dhcp_range.select(:id) 
       ).or( 
         Device.select(:id).where( 
           id: Device.alternate_aws.select(:id) 
         ) 
       ) 
  )
}

这将生成类似于以下内容的查询:

 SELECT 
   devices.*
 FROM 
   devices
 WHERE 
   devices.id NOT IN (
     SELECT devices.id FROM [YOUR RESERVED QUERY])
   AND  devices.id IN ( 
     SELECT devices.id 
     FROM devices
     WHERE 
       devices.id IN ( SELECT devices.id FROM [YOUR IN DHCP RANGE QUERY])
       OR devices.id IN ( SELECT devices.id FROM [YOUR ALTERNATE AWS QUERY])
     )

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-01-25
    • 2011-07-30
    • 2018-09-16
    • 1970-01-01
    • 2014-07-24
    相关资源
    最近更新 更多