Я не знаю, если это правильное место, чтобы задать вопрос, как это, но вот оно:
У меня есть приложение на Rails 3, похожее на интрасеть, управляющее примерно 20 тысячами пользователей, которые находятся во вложенном наборе (предварительно упорядоченное дерево - http://en.wikipedia.org/wiki/Nested_set_model).
Эти пользователи вводят статистику (данные, просто числовые значения). Введенная статистика присваивается категории (мы называем ее указателем) и номеру недели.
Эти данные дополнительно обрабатываются и рассчитываются для результатов.
Некоторые из них вычисляются по активности пользователей + результат из другой категории ... и т. Д
То, что вводит пользователь, не всегда совпадает с тем, что он видит в отчетах.
Эти вычисления могут быть очень сложными, некоторые категории имеют очень специфические формулы.
Но остальное просто «дайте мне сумму всех введенных значений для этой категории для этого пользователя за эту неделю / месяц / год».
Проблема заключается в том, что эти статистические данные также необходимо суммировать для подмножества пользователей для выбранного пользователя (поэтому в основном он будет возвращать сумму всех значений для всех пользователей в рамках пользователя, включая самого себя).
Это приложение работает в течение 2 лет, и оно выполняет свою работу довольно хорошо ... но с все большим количеством пользователей это также довольно медленно, когда дело доходит до серверных отчетов, типа "дать мне список всех пользователей в разделе" я и их статистика. Одна строка для их подгруппы и одна строка для их личной статистики "). Конечно, пользователи хотят (и нуждаются) в том, чтобы их отчеты были как можно более актуальными, 5 минут для отражения вновь введенных данных - это слишком много для них. И этот конкретный отчет является их любимым: /
Чтобы оставаться в реальном времени, мы не можем напрямую выполнять высокоинтенсивные sqls ... Это убило бы сервер. Поэтому я вычисляю их только один раз с помощью фонового процесса, а веб-интерфейс просто читает результаты.
Эти sqls трудно оптимизировать, и я рад, что я перешел от этого подхода ... (кэширование не вариант. См. Ниже.)
Текущее приложение выглядит так:
внешний интерфейс: когда пользователь вводит новые данные, они сохраняются в простую таблицу mysql, например [user_id, pointer_id, date, value]
, и также вставляются в очередь.
backend: затем идет процесс calc_daemon, который каждые 5 секунд проверяет очередь на наличие новых «повторных вычислений». Мы выводим запросы, определяем, что еще нужно пересчитать вместе с ним (у указателей есть зависимости ... простейший случай: когда вы меняете статистику за неделю, мы должны пересчитать статистику за месяц и год ...). Это делает этот пересчет простым способом. Мы выбираем данные с помощью настраиваемых SQL-запросов для каждого указателя, сгенерированных их классами.
- эти вычисленные результаты затем записываются обратно в mysql, но в секционированные таблицы (одна таблица в год). Одна строка в этой таблице похожа на
[user_id, pointer_id, month_value, w1_value, w2_value, w3_value, w4_value]
. Таким образом, таблицы имеют ~ 500 тыс. Записей (я в основном сократил количество записей в 5 раз).
- когда веб-интерфейсу нужны эти результаты, он делает простые суммы для этих секционированных данных с двумя объединениями (из-за вложенного набора conds).
Проблема в том, что эти простые sqls с суммами, group by и join-on-the-subtree могут занимать около 200 мс каждая ... только для нескольких записей ... и нам нужно запустить много этих sqls .. Я думаю, что они оптимизированы как можно лучше, согласно explain
... но они слишком сложны для этого.
Итак ... ВОПРОС:
Могу ли я переписать это, чтобы использовать Redis (или другое быстрое хранилище значений ключей) и увидеть какую-то выгоду от этого, когда я использую Ruby и Rails? Насколько я понимаю, если я перепишу его для использования redis, мне придется выполнить к нему гораздо больше запросов, чем к mysql, а затем выполнить сумму в ruby вручную ... так что производительность может быть подорвана значительно ... я не совсем уверен, смогу ли я написать все возможные запросы, которые у меня сейчас есть с Redis ... Загрузка пользователей в рельсы, а затем сделать что-то вроде "Redis, дать мне сумму для пользователей 1,2,3, 4,5 ... "не похоже на правильную идею ... Но, может быть, есть какая-то особенность в redis, которая могла бы сделать это проще?) ...Также древовидная структура должна быть похожа на вложенный набор, то есть она не может иметь одну запись в redis со списком всех дочерних идентификаторов для некоторого пользователя (что-то вроде children_for_user_10: [1,2,3]
), потому что древовидная структура часто меняется ... Это также причина, почему У меня не может быть этих сумм в этих секционированных таблицах, потому что, когда дерево меняется, мне придется все пересчитать ... Вот почему я выполняю эти суммы в реальном времени.)
Или вы бы предложили мне переписать это приложение на другой язык (java?) И вместо этого вычислить результаты в памяти? :) (Я пытался сделать это SOA-способом, но не получилось, что в итоге я так или иначе получаю XXX мегабайт данных в ruby ... особенно при генерации отчетов ... и gc просто убивает это .. .) (и побочным эффектом является то, что один генерирующий отчет блокирует все приложение rails: /)
Предложения приветствуются.