Как мне оптимизировать эти области - PullRequest
2 голосов
/ 15 мая 2019

У нас есть несколько областей в одной модели.По мере роста базы данных области становятся непригодными для использования.

При использовании области с запросом такого типа начинается проблема: где ("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 записей. Однако, при записи более 250 000 записей у нас есть проблемы с производительностью в этой области: dhcp_full_calculation

rails 5.2.3, ruby ​​2.6.3, используя базу данных postgres

1 Ответ

0 голосов
/ 15 мая 2019

Таким образом, общая проблема производительности связана с операциями вычисления, которые происходят вне базы данных, например:

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

Это выполняет 3 запроса, а затем использует Array (или Array как) методына ActiveRecord::Relation s, чтобы вернуть список Device s для выбора.Это приведет к большому количеству вычислений, а также к очень большому предложению 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])
     )
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...