Когда пользователь заходит в профиль, у него справа есть панели транзакций и событий. Формируются они не простым путём, т.е. парсятся некоторые данные, потом формируются и всё такое. Так вот эта панель на всё проекте всегда видна пользователю. Куда бы ни пошёл - везде она будет. Но получается, что каждый переход по страницам - отбирает по 1.5 секунды на загрузку из-за этого блока. Транзакции, как и события - могут меняться с неопределённой периодичностью. Т.е. например сегодня могут добавиться транзакции, а завтра нет, а после завтра они могут появляться каждые полчаса.
Я не очень знаком с кэшированием (кроме того факта, что некие данные кладутся в память или ещё куда-то и просто потом дёргаются оттуда, а не делается новая уйма запросов), но если правильно понимаю, в данном случае кеширование не прокатит. Т.е. не прокатит из-за того, что данные могут меняться и может получиться так, что новая транзакция или событие появилось, а пользователь его не увидит.
Или я ошибаюсь? Поясните пожалуйста, как быть в подобной ситуации. Учитывая то, что на загрузку какого-то там блока тратится 1.5 секунды - это прям растрата "бюджета". :)
P.S. Ах да, забыл добавить. Транзакции и события зависят от идентификатора пользователя. Возможно это важно.
Кешировать стоит как раз те данные, которые постоянно нужны.
Постойте, у вас пользователь ждёт полторы секунды на загрузку каждой страницы - и не за счёт огромного количества данных, а потому, что эти данные заново собираются? Но это же RoR-приложение, самописное, я правильно понимаю? То есть у вас есть доступ к коду, который всё это показывает и к коду, который вызывается, когда транзакции добавляются? Тогда при обновлении транзакций делайте кеш для этого пользователя "протухшим", а в коде, который всё это хозяйство формирует, заново добавляйте кешированный результат в memcached и показывайте его. Тогда в следующий раз актуальный кеш будет выбран из системы кеширования, но на это потребуется в полторы тысячи раз меньше времени, а данные будут всё время актуальными.
Да, понимаете вы правильно. Это самописное приложение, есть доступ до всего. Сейчас 1.5 секунды и количество данных считается низким (примерно 1-1.5% от того, что может быть) и это уже 1.5 секунды. Будет больше - будет дольше. Основные затраты времени сейчас делятся примерно так: около половины секунды на выборку и около секунды на обработку. Считаем это очень долгим, особенно при расчете, что данных будет значительно больше.
На тему транзакций. Работа с ними ведётся отдельным worker'ом на той же БД. Возможно это важно.
Немного не знаком с кэшированием (особенно в RoR), поэтому хотел бы переспросить вас, правильно ли я понимаю, что логика примерно такая... При первом показе блока данных, сами данные грузятся как обычно и попадают таблицу с кэша с пометкой актуальности (флажок или дата). Затем, когда транзакции изменились, worker смотрит кэш пользователей и обновляет статус (помечая флажок или дату как неактуальные). Затем, когда пользователь перешёл на новую страницу, срабатывает новая выборка, так как кэш неактуален, судя по флажку или дате. Верно понимаю?
И ещё вопрос. В какой статье не погляжу, везде когда кладут данные в кэш - выставляют ключ. Но ключ, совершенно не привязан к пользователю. Т.е. например $redis.set("categories", categories) Но по хорошему ведь нужно в кэш класть с ключом не categories, а что-то вроде этого: categories+current_user.id.to_s Логично ведь?
Лучше, имхо, использовать именно специализированные средства для кеша. Memcached, redis, но не таблицы в БД. Тогда не нужно будет выставлять никаких флагов.
Логика работы: а) смотрим, есть ли кеш; б) если есть, возвращаем его (лучше прям сразу уже обработанный шаблонизатором, чтобы прямо сразу вставить на страницу); в) если кеша нет, то запрашиваем функцию, которая собирает данные, рендерит их и сохраняет в кеше (это важно!); г) если произошло обновление транзакций, нужно просто пометить удалить кеш, что-то типа region.delete(ключ) - в этом случае при первой же загрузке страницы пойдёт по пути а-б-в и кешированный вариант будет сохранён.
Что до вопроса, почему в примерах не привязывают к пользователю - похоже, пример с "общими" для всех данными, типа списка категорий или там меню. Их-то нет никакого смысла привязывать к какому-то пользователю, они показываются всем одинаковыми.
...а для кеширования вещей для каждого пользователя ключ нужно выбирать иначе, вы правы. Будет очень неприятно, если все пользователи увидят один и тот же кусок.
Про таблицу писал, потому что не работал ранее с кэшированием и тут скорее просто выразился так.
В целом, суть ясна. Чуть увеличится нагрузка на обработчик транзакций (ввиду предварительной подготовки для пользователей), но это не смертельно. В любом случае, всё стало яснее. Спасибо за помощь!
На самом деле нагрузка на обработчик транзакций не увеличится, в нём же нужно просто удалить кеш (для пользователя, чью транзакцию этот обработчик обрабатывает). А вот в функции, собирающей данные, добавятся всего несколько строк - проверка на существование кеша и его создания, если кеша нет. Нагрузка увеличится на величину, близкую к погрешности, зато выдача кешированного результата ускорит работу приложения просто невероятно. Ну и да, ключ выбирайте всё-таки привязанный к пользователю, ведь показывать пользователю нужно именно его данные. Что-то типа 'transactions_user_' + current_user.id.to_s будет роскошным ключом. Удачи!
Та не, там транзакции не имеющие отношения к пользователям, но пользователи имеют возможность видеть те транзакции, которые подходят под их условия. Как бы так пояснить... Транзакции - это транзакции некоторых Штук. И вот Вася может смотреть только транзакции по Штуке1 и Штуке2, а Петя по Штуке2, Штуке3 и Штуке4. Но перед тем, как эти транзакции показываются пользователями - они обрабатываются. Например, Вася может видеть не только информацию по транзакции, но и некоторые связующие данные. Таким образом, когда Вася ходит по страницам, каждый раз из БД дёргаются эти транзакции, с ними проводятся некоторые операции (подсчет, формирование и т.д.) зависящие от пользователя и выдаются на страницу. Т.е. вот этот механизм подготовки получается нужно переложить на механизм сборки транзакций. За счет этого и увеличится нагрузка на обработчик, потому как ему придётся не просто собирать, но и готовить для каждого пользователя своё "представление".
Как альтернативу рассматриваю запуск отдельной задачи, которая будет запускаться при появлении новых транзакций, чтобы не задерживать сборщик. Но это ладно, главное, что понятно стало как со стороны пользователя уменьшить нагрузку.
TODO: Здесь будут ворованные умные мысли, типа мои
У вас ответ в вопросе )
Я не очень знаком с кэшированием
Ознакомьтесь.
Храните данные панелей в Memcached. Показывайте "устаревшие" данные, если это не критично для приложения.
Либо подгружайте данные через ajax.
Это не критично, но пользователю важна эта информация. Скажем так, он будет чуть напрягаться, если будет видеть опоздавшие данные. А я не хотел бы, чтобы он напрягался.
По моему нубскому мнению тут нужно понять какие данные можно использовать для того, чтобы понять появились новые транзакции, или нет. А потом их использовать - либо получать каждый раз данные, достаточные для этого вывода, либо где-то(напр. в таблице) хранить информацию о дате последней транзакции и, если она отличается от записанной в кэше, то обновлять.
Технически, когда появляется новая транзакция, я могу где-то делать пометку. Но получается, что между переходами, нужно как-то смотреть что в кэше и насколько актуально это.
Вы неправы, кэширование прокатит. Но кэш нужно обновлять и это сильно зависит где и как хранятся ваши данные о транзакциях. В общем случае есть следующие варианты:
1) Сбрасывать кэш по истечении определенного интервала времени. Например каждый час.
2) Сбрасывать кэш по событию. Например функция по созданию транзакций сбрасывает кэш
3) Сбрасывать кэш по дате обновления кэшируемого объекта. Это когда хранятся время создания кэшированных данных и при обращении к кэшу сравнивается со временем модификации объекта, если оно позже то кэш обновляется.
Как уже вам посоветовали - ознакомьтесь с системой кэширования. В 4 рельсах она из коробки и достаточно продвинутая.