我们在一个模型中有多个作用域。 随着数据库的增长,作用域变得无法使用。
使用范围与这种类型的查询是问题开始的地方: 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
使用Postgres数据库的Rails 5.2.3,ruby 2.6.3
答案 0 :(得分:0)
因此,总体性能问题与数据库外部发生的计算操作有关,例如
(Device.in_dhcp_range.select("devices.id") +
Device.alternate_aws.select("devices.id")).uniq
- Device.reserved)
这将执行3个查询,然后在Array
上使用Array
(或类似ActiveRecord::Relation
)方法以返回要选择的Device
的列表。这将导致大量计算以及查询中非常大的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])
)