Pull to refresh

О кэшировании ресурсоемких SQL-запросов на веб-сервере

SQL *
Sandbox
В этой статье я постараюсь описать распространенную ошибку создателей систем кэширования

Началось всё в далекие времена, когда я управлял сайтами, которые были расположены на хостинге в FreeBSD jail, который был весьма ограничен в ресурсах. Почему так? Потому, что я использовал для отображения отчетов и печатных форм расширение pdflib, которого в наборе расширений на стандартном хостинге не было. Я скомпилировал там свой apache и php, залил туда документы и сайт заработал.

Всё было хорошо, пока не возникла необходимость показать на страницах сайта top10 продаваемых в магазине товаров. SQL запрос, который создавал нужный набор данных, выполнялся около 10 секунд. Ключи, explain'ы и всякое другое шаманство не помогали. Нужно было делать кэширование данных. И я, подсмотрев как это делают другие, написал код который кэшировал данные запроса.

Что делал мой код.

1. Проверял, есть ли в кэше объект с нужными данными, и, если есть, брал его из кэша и использовал полученные данные. Если его не было, я выполнял код, который формировал необходимые данные и сохранял их в кэш.

2. После использования данных запускалась процедура «сбора мусора». Объекты с просроченным временем жизни удалялись.

Вроде как всё в порядке. Я проверил работу кэша на тестовой машине с помощью утилиты ab и получил обнадеживающие результаты. После этого я залил код в jail. И лег спокойно спать.

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

Ключ к разгадке мне дали графики нагрузки на сервер. Они показывали скачкообразный рост нагрузки с периодичностью примерно равной времени жизни объектов кэша. Что же было на самом деле? Всё очень просто.

В момент когда истекало время жизни объекта кэша, при выполнении запроса объект кэша удалялся. Следующий http-запрос запускал процедуру создания объекта кэша, которая длилась какое-то время и выполняла дорогостоящий запрос к SQL-серверу. За это время происходил ещё один http-запрос. Который тоже запускал процедуру создания объекта кэша. Нагрузка на сервер возрастала в два раза, что влекло увеличение времени исполнения SQL-запросов в два раза. За возросшее время ожидания исполнения SQL-запросов происходил ещё один http-запрос. И так далее.

image

Как этого избежать?

1. Процесс, который обнаружил что объект кэша устарел не должен его удалять.

2. Процесс, который выполняет запрос, создающий заново объект кэша должен установить флаг, чтобы другие процессы не запускали процедуру обновления.

3. После получения свежих данных объект кэша должен быть подменен атомарной(быстрой) операцией и после этого снят флаг.

В качестве домашнего задания проверьте, пожалуйста, как построена ваша система кеширования.
Tags:
Hubs:
Total votes 24: ↑17 and ↓7 +10
Views 7.1K
Comments Comments 24