Запрет Rails от кеширования результатов запроса ActiveRecord - PullRequest
4 голосов
/ 04 августа 2011

У меня есть задача граблей, которая должна проходить через большое количество записей (называемых торговцами), каждая из которых имеет большое количество связанных элементов.Моя проблема в том, что из-за того, что Rails автоматически кеширует результаты моих запросов к БД, я в конечном итоге очень быстро помещаю своих работников в пространство подкачки.

Короче, мне интересно, как выполнить такую ​​команду:

Merchant.all.each { |m| items = m.items }

без кэширования значения 'items' каждый раз через.

Я пробовал:

Merchant.all.each do |m|
  ActiveRecord::Base.connection.uncached do
   items = m.items
 end
end

и у меня естьтакже попытался добавить это к моей модели Merchant:

def items_uncached
  self.class.uncached { items }
end

и затем вместо этого вызвать items_uncached, но я все равно заканчиваю тем, что увеличивал использование памяти с каждым новым набором элементов, к которым у меня есть доступ.Я использую Rails 2.3.10, Ruby 1.9.2 и использую Mysql для хранения.

Заранее спасибо за ваши мысли!

*** edit:

Вот этоФактический фрагмент кода, над которым я работаю:

File.open(output, "w") do |f|
  Merchant.all.each do |m|
    items = m.items
    invalid_image_count = 0
    items.each do |i|
      invalid_image_count += 1 unless i.image_valid?
    end
    invalid_categories = items.select { |i| !i.categories_valid? }.count
    f.puts "#{m.name} (#{m.id}): #{invalid_image_count} invalid images, " +
            "#{invalid_categories} invalid categories"
  end
end

Попытка выполнить некоторую проверку ошибок и затем записать результаты.

Ответы [ 2 ]

4 голосов
/ 04 августа 2011

Кеш запросов здесь не главная проблема. В любом случае Rails "кеширует" ваши объекты.

Кеш запросов - это просто «поиск хешей», который предотвращает ненужное попадание Rails в БД, он не контролирует, как ruby ​​(или Rails) хранит объекты, внутренне возвращаемые ассоциациями.

Например попробуйте это (даже если не кэшировано):

m = Merhant.first # <- m is loaded from DB
m.items           # <- items are loaded from DB and STORED(!) in m
m.items           # <- items are returned from the association stored in m
m.items.reload    # <- hits the DB (or the query cache)
m.instance_variable_get("@items") # <- returns the actual stored items

Так что теперь, когда вы делаете m.items в цикле each, вы просто заполняете все экземпляры Merhcant всеми их элементами, и сборщик мусора не может ничего освободить, поскольку все объекты ссылаются из all массив, пока вы находитесь внутри цикла.

Таким образом, решение состоит в том, чтобы поступить так, как предлагает Виктор, что предотвращает срабатывание «хранилища ассоциаций».

3 голосов
/ 04 августа 2011

Если ваша ассоциация простая has_many, вы можете попробовать это:

Merchant.all.each do |m| 
  items = Item.find_all_by_merchant_id(m.id) 
  ...
end 

Или даже:

Merchant.find(:all, :select => "id, name").each do |m| 
  items = Item.find_all_by_merchant_id(m.id) 
  ... 
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...