Утечка памяти в Ruby on Rails при просмотре большого количества записей; find_each не помогает - PullRequest
32 голосов
/ 12 июля 2011

У меня есть приложение Rails, которое обрабатывает большое количество записей в базе данных mysql. Как только он начинает работать, его использование памяти быстро растет со скоростью 50 МБ в секунду. С помощью таких инструментов, как oink, я смог сузить основную причину до одного цикла, который проходит через все записи в большой таблице в базе данных.

Я понимаю, что если я использую что-то вроде Person.all.each , все записи будут загружены в память. Однако, если я переключаюсь на find_each , я все равно вижу ту же проблему с памятью. Для дальнейшей изоляции проблемы я создал следующий тестовый контроллер, который делает только циклический просмотр записей. Я полагаю, find_each каждый раз удерживает в памяти только небольшое количество объектов, но использование памяти растет линейно по мере выполнения.

class TestController < ApplicationController
  def memory_test
    Person.find_each do |person|
    end
end

Я подозреваю, что это связано с ActiveRecord, кэширующим результаты запроса. Но я проверил настройки своей среды, и у меня все параметры, связанные с кэшированием, установлены на false в разработке (я использую настройки по умолчанию, созданные rails). Я провел поиск в Интернете, но не смог найти решение.

Я использую рельсы 3.1.0 rc1 и ruby ​​1.9.2

Спасибо!

Ответы [ 3 ]

40 голосов
/ 13 июля 2011

Я смог понять это сам. Есть два места для изменения.

Сначала отключите IdentityMap. В config / application.rb

config.active_record.identity_map = false

Во-вторых, используйте без кэширования , чтобы завершить цикл

class MemoryTestController < ApplicationController
  def go
    ActiveRecord::Base.uncached do
      Person.find_each do |person|
        # whatever operation
      end
    end
  end
end

Теперь использование моей памяти под контролем. Надеюсь, что это помогает другим людям.

3 голосов
/ 13 июля 2011

Как бы ни был хорош ActiveRecord, он не лучший инструмент для решения всех проблем. Я рекомендую перейти на свой родной адаптер базы данных и выполнить работу на этом уровне.

2 голосов
/ 11 августа 2014

find_each звонки find_in_batches с размером партии 1000 под капотом.

Все записи в пакете будут создаваться и сохраняться в памяти до тех пор, пока пакет обрабатывается.

Если ваши записи велики или если они занимают много памяти через коллекции прокси (например, has_many кэширует все свои элементы каждый раз, когда вы их используете), вы также можете попробовать меньший размер пакета:

  Person.find_each batch_size: 100 do |person|
    # whatever operation
  end

Вы также можете попытаться вручную периодически вызывать GC.start (например, каждые 300 наименований)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...