рельсы высокое использование памяти - PullRequest
9 голосов
/ 18 августа 2010

Я планирую использовать отложенное задание для запуска некоторой фоновой аналитики.В моем первоначальном тесте я увидел огромное количество использования памяти, поэтому я в основном создал очень простую задачу, которая выполняется каждые 2 минуты, просто чтобы посмотреть, сколько памяти используется.

Задача очень проста и analytics_eligbile?метод всегда возвращает false, учитывая, где данные находятся сейчас, поэтому в основном не вызывается ни один из сложных кодов.У меня есть около 200 сообщений в моей выборке данных в разработке.Post has_one analytics_facet.

Независимо от внутренней логики / бизнеса здесь, единственное, что делает эта задача, - это вызвать analytics_elptable?метод 200 раз каждые 2 минуты.В течение 4 часов мое использование физической памяти составляет 110 МБ, а виртуальной памяти - 200 МБ.Просто для того, чтобы сделать что-то такое простое!Я даже не могу представить, сколько памяти это будет съесть, если он будет выполнять аналитику real на 10000 сообщений с реальными производственными данными !!Конечно, он может не работать каждые 2 минуты, больше как каждые 30, но я не думаю, что он будет летать.

Это работает ruby ​​1.9.7, rails 2.3.5 на Ubuntu 10.x 64 бит.Мой ноутбук имеет 4 ГБ памяти, двухъядерный процессор.

Действительно ли рельсы такие плохие или я что-то не так делаю?

 Delayed::Worker.logger.info('RAM USAGE Job Start: ' + `pmap #{Process.pid} | tail -1`[10,40].strip)
Post.not_expired.each do |p|
    if p.analytics_eligible?
        #this method is never called
        Post.find_for_analytics_update(p.id).update_analytics
    end
end
Delayed::Worker.logger.info('RAM USAGE Job End: ' + `pmap #{Process.pid} | tail -1`[10,40].strip)

Delayed::Job.enqueue PeriodicAnalyticsJob.new(), 0, 2.minutes.from_now

Пост-модель

def analytics_eligible?
        vf = self.analytics_facet
        if self.total_ratings > 0 && vf.nil?
            return true
        elsif !vf.nil? && vf.last_update_tv > 0
            ratio = self.total_ratings / vf.last_update_tv
            if (ratio - 1) >= Constants::FACET_UPDATE_ELIGIBILITY_DELTA
                return true
            end
        end
        return false
    end

Ответы [ 4 ]

19 голосов
/ 27 августа 2010

ActiveRecord довольно требователен к памяти - будьте очень осторожны при выполнении выборок и помните, что Ruby автоматически возвращает последний оператор в блоке в качестве возвращаемого значения, что может означать, что вы возвращаете массив записей, которые были сохранены в результате чего-то и, следовательно, не имеют права на GC.

Кроме того, когда вы вызываете «Post.not_expired.each», вы загружаете все ваши сообщения not_expired в RAM. Лучшее решение - find_in_batches, которое загружает только X записей в RAM одновременно.

Исправить это можно так просто:

def do_analytics
  Post.not_expired.find_in_batches(:batch_size => 100) do |batch|
    batch.each do |post|
      if post.analytics_eligible?
        #this method is never called
        Post.find_for_analytics_update(post.id).update_analytics
      end
    end
  end
  GC.start
end

do_analytics

Здесь происходит несколько вещей. Во-первых, все это ограничено в функции, чтобы не допустить столкновения переменных для удержания ссылок из итераторов блока. Затем find_in_batches извлекает объекты batch_size из БД за раз, и, пока вы не создаете ссылки на них, вы получаете право на сборку мусора после каждой итерации, что снижает общее использование памяти. Наконец, мы вызываем GC.start в конце метода; это заставляет GC запустить развертку (что вы не хотели бы делать в приложении реального времени, но, поскольку это фоновая работа, все в порядке, если для ее запуска требуются дополнительные 300 мс). Это также имеет очень явное преимущество при возврате nil, что означает, что результатом метода является nil, что означает, что мы не можем случайно зависеть от экземпляров AR, возвращенных из искателя.

Использование чего-то подобного должно гарантировать, что вы не получите утечки AR-объектов, и значительно улучшит как производительность, так и использование памяти. Вы хотите убедиться, что не пропускаете нигде в своем приложении (переменные класса, глобальные переменные и ссылки на классы являются худшими нарушителями), но я подозреваю, что это решит вашу проблему.

Все это говорит о том, что это проблема cron (периодическая периодическая работа), а не проблема DJ, на мой взгляд. У вас может быть одноразовый анализатор аналитики, который запускает вашу аналитику каждые X минут с помощью script/runner, вызываемой cron, которая очень аккуратно устраняет любые потенциальные утечки памяти или неправильное использование при выполнении (так как весь процесс завершается в конце)

6 голосов
/ 28 августа 2010

Загрузка данных в пакетном режиме и агрессивное использование сборщика мусора, как предположил Крис Хилд, принесет вам действительно большие выгоды, но люди часто упускают из виду то, в какие платформы они загружают.

Загрузка стека Rails по умолчанию предоставит вам ActionController, ActionMailer, ActiveRecord и ActiveResource вместе. Если вы создаете веб-приложение, возможно, вы не используете их все, но, вероятно, вы используете большинство из них.

Когда вы создаете фоновое задание, вы можете избежать загрузки ненужных вещей, создав для этого специальную среду:

# config/environments/production_bg.rb

config.frameworks -= [ :action_controller, :active_resource, :action_mailer ]

# (Also include config directives from production.rb that apply)

Каждая из этих платформ будет просто сидеть в ожидании электронной почты, которая никогда не будет отправлена, или контроллера, который никогда не будет вызываться. Там просто нет смысла загружать их. Настройте файл database.yml, настройте фоновое задание для запуска в среде production_bg, и у вас будет гораздо более чистый лист для начала.

Еще одна вещь, которую вы можете сделать, это использовать ActiveRecord напрямую, не загружая Rails вообще. Это может быть все, что вам нужно для этой конкретной операции. Я также обнаружил, что использование облегченного ORM, такого как Sequel , делает вашу фоновую работу очень легкой, если вы в основном выполняете вызовы SQL для реорганизации записей или удаления старых данных. Если вам нужен доступ к вашим моделям и их методам, вам нужно будет использовать ActiveRecord. Однако иногда стоит по-новому реализовать простую логику в чистом SQL из соображений производительности и эффективности.

При измерении использования памяти единственное число, которое следует учитывать, - это «реальная» память. Виртуальное количество содержит разделяемые библиотеки, и их стоимость распределяется среди каждого процесса, использующего их, даже если он учитывается полностью для каждого из них.

В конце концов, если для выполнения чего-то важного требуется 100 МБ памяти, но вы можете уменьшить его до 10 МБ за три недели работы, я не понимаю, зачем вам это надо. На управляемом провайдере 90 МБ памяти стоит не более $ 60 в год, что обычно намного дешевле, чем ваше время.

Ruby on Rails охватывает философию большей заботы о вашей производительности и времени, чем об использовании памяти. Если вы хотите урезать его, посадить на диету, вы можете это сделать, но это потребует немного усилий.

1 голос
/ 23 августа 2010

Если у вас возникают проблемы с памятью, одним из решений является использование другой технологии фоновой обработки, например resque .Это обработка BG, используемая github .

Благодаря родительской / дочерней архитектуре Resque, задания, которые используют слишком много памяти, освобождают эту память после завершения.Нет нежелательного роста

Как?

На некоторых платформах, когда работник Resque резервирует задание, он сразу же разветвляется на дочерний процесс.Ребенок обрабатывает работу, затем выходит.Когда ребенок успешно вышел, работник резервирует другую работу и повторяет процесс.

Более подробную техническую информацию вы можете найти в README.

0 голосов
/ 18 августа 2010

Факт, что Ruby потребляет (и пропускает) память. Я не знаю, сможете ли вы что-то с этим сделать, но, по крайней мере, рекомендую вам взглянуть на Ruby Enterprise Edition .

REE - это порт с открытым исходным кодом, который обещает «на 33% меньше памяти» среди всех других хороших вещей. Я использую REE с Passenger в производстве почти два года, и я очень доволен.

...