Как кэшировать популярные запросы, чтобы избежать как задержек, так и пустых результатов

На настраиваемой главной странице нашего веб-сайта мы предлагаем пользователям возможность отображать модули, показывающие недавно обновленный контент, выбирая из более чем 100 модулей.

Все данные генерируются запросами MySQL, результаты которых кэшируются через memcached. Наша текущая система работает следующим образом: когда пользователь загружает страницу, содержащую модули, модуль, ему немедленно передаются данные из кеша, и запрос добавляется в очередь для обновления отдельным процессом Gearman (так что загрузка страницы не выполняется). не ждать запроса mysql). Затем этот запрос запускается каждые 15 минут для обновления данных в кеше. Сама очередь запросов периодически очищается, чтобы мы не обновляли данные, которые не запрашивались в последнее время.

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

Наш трафик таков, что, если бы мы попытались выполнить запрос в режиме реального времени для пользователя, когда кэш пуст, у нас возникла бы серьезная проблема с паническим бегством — мы бы выполнили один и тот же (возможно, медленный) запрос много раз, как многие пользователи загружали страницу. Есть ли способ решить проблему «пустого модуля», не открывая риск панического бегства?


person Karptonite    schedule 20.08.2012    source источник


Ответы (1)


Это интересная реализация, хотя она немного отличается от наиболее типичной реализации memcached для MySQL.

В большинстве случаев пользователи настраивают место, где запросы сначала оцениваются в memcached, чтобы увидеть, есть ли доступная запись. Если это так, они серверуют его из memcached и вообще никогда не запрашивают базу данных. В случае промаха кеша выполняется запрос к базе данных, результаты добавляются в memcached, а информация возвращается вызывающей стороне. Именно так вы обычно создаете свой кеш для запросов на чтение.

В тех случаях, когда данные обновляются, обновление будет производиться для базы данных, а затем соответствующие данные в memcached аннулируются и/или обновляются. Аналогично для вставок, вы можете либо ничего не делать с кешем (и позволить следующему чтению этой записи заполнить кеш), либо вы можете активно добавлять данные, связанные со вставкой, в кеш, в зависимости от потребностей вашего приложения.

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

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

На стороне клиента, когда вы получаете промах кеша или «ожидающий» результат, вы можете просто инициировать повторную попытку кеша через определенный период времени (который вы можете увеличить в геометрической прогрессии). Так что, возможно, сначала подождите 1 секунду, затем попробуйте вернуть усиление через 2 секунды, если они все еще получают «ожидающие» результаты, затем повторите попытку через 4 секунды и так далее.

Это, возможно, приведет к увеличению количества запросов к серверу memcached, но должно решить любые проблемы на уровне базы данных.

person Mike Brant    schedule 20.08.2012
comment
Спасибо за ваш ответ. На самом деле для большинства запросов мы используем более типичную реализацию, в которой сначала проверяем memcache, а затем запрашиваем базу данных, если результаты отсутствуют, и кэшируем результаты. Однако для запросов, которые находятся на первой странице, у нас была бы давка, если бы мы это сделали. К сожалению, у нас есть достаточно различных способов возврата данных (сортировка, фильтрация и т. д.), поэтому повторное создание результатов всякий раз, когда данные становятся недействительными, в настоящее время не вариант, хотя он находится в нашем долгосрочном списке пожеланий. - person Karptonite; 20.08.2012
comment
ОК Это имеет смысл. Я обновил свой ответ возможными предложениями. - person Mike Brant; 20.08.2012
comment
Спасибо - мне пришла в голову идея ожидающего результата, но это звучит как очень хорошая реализация. Его недостатком является потенциальная задержка загрузки страниц для ожидания регенерации кеша, но, по крайней мере, он не перегружает базу данных. - person Karptonite; 20.08.2012