Работа с файлами кеша ошибка не работает

Устранение ошибок CMS Битрикс

Опция «Проверка системы» в CMS Битрикс позволяет проверить соответствие настроек хостинга требованиям CMS и вашего сайта. При запуске этой опции на хостинге Timeweb, Битрикс, как правило, приводит несколько ошибок и замечаний. В этой статье мы рассмотрим, как их исправить.

Отправка почты

Речь идет о двух параметрах: «Отправка почты» и «Отправка почтового сообщения больше 64Кб».

Уведомление: Ошибка! Не работает

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

Режим работы MySQL

Уведомление: Ошибка! innodb_strict_mode=ON, требуется OFF. Переменная sql_mode в MySQL должна быть пустая, текущее значение: NO_ENGINE_SUBSTITUTION

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

Решение:

Необходимо внести изменения в файлы конфигурации Bitrix, в которых задаются параметры базы данных, и указать в них нужные значения. Отредактировать файлы можно, например, с помощью Файлового менеджера.

В файл /public_html/bitrix/php_interface/after_connect.php под уже имеющимися строками и до символов ?> добавьте:

В файл /public_html/bitrix/php_interface/after_connect_d7.php под уже имеющимися строками и до символов ?> добавьте:

После этого проверка Bitrix пройдет успешно.

Размер стека и pcre.recursion_limit

Уведомление: Замечание: Возможны проблемы в работе с длинными строками из за системных ограничений

Это замечание некритично и, как правило, на работе сайтов не сказывается, однако оно может влиять на обработку очень длинного текста, например, если в нем сотни тысяч символов.

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

Решение:

  • Если у вас тариф мощного хостинга, решением будет переключение сайта на PHP 7.4. Это можно сделать в разделе «Сайты» по нашей инструкции.
  • Если у вас тариф обычного хостинга или если смена версии PHP не помогла, исправить ошибку, добавьте в файл /public_html/.htaccess строки:

Выполнение агентов на cron

Уведомление: Замечание: Агенты выполняются на хитах, рекомендуется перенести их выполнение на cron

Это замечание некритично. В Битрикс предусмотрен внутренний планировщик задач (агентов). Данное замечание влияет на то, насколько точно по времени сработают запланированные задачи. Грубо говоря, скрипты сайта выполняются, только когда его страницы кто-то посещает. Если нет посещений, то и таймер работать не будет. Cron позволяет полностью решить проблему с планированием задач.

Решение:

Чтобы перенести работу непериодических агентов (и передачу почты) на Сron, необходимо установить константу BX_CRONTAB_SUPPORT .

Для этого добавьте в файл /public_html/bitrix/php_interface/dbconn.php строку:

1. В разделе Crontab нажмите «Добавить новую задачу» и задайте удобное имя для задачи.

2. В пункте «Исполнитель» выберите «Исполняемый бинарный файл».

3. В пункте «Путь до файла» укажите команду следующего вида:

  • В /opt/php72/bin/php укажите ту версию php, которую использует сайт. Например, /opt/php74/bin/php ( или /opt/php7.4/bin/php ).
  • Вместо /home/u/user/site.ru/public_html укажите корректный путь до папки сайта и замените u и user на первую букву вашего логина и сам логин.

4. В пункте «Периодичность» выберите «Каждую минуту».

Уведомление: Ошибка! Не настроен запуск cron_events.php на cron

Эта ошибка может возникнуть, если в пункте выше вы только добавили константу, но не добавили задачу cron. Без настройки задачи в crontab будет остановлена часть агентов, в том числе с сайта перестанет отправляться почта. Письма будут накапливаться в очередь и отправляться по частям после устранения ошибки.

Решение:

Выполните приведенную выше инструкцию по добавлению задачи cron, и ошибка будет устранена.

Обязательные параметры PHP

Уведомление: Ошибка! Значение max_input_vars должно быть не ниже 10000

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

Решение:

Необходимо добавить следующую строку в файл /public_html/.htaccess :

Источник

Не работает кеширование компонентов на странице

По результатам отладки удалось обнаружить, что не работает кеширование компонентов (bitrix.news) на странице
и выполняются запросы вместо кеша

  1. Автокеширование компонентов включено .
  2. Управляемый кеш компонентов включен .
  3. Кеширование HTML выключено .
  4. В настройках стандартного компонента bitrix:news.list:
  • Тип кеширования: авто+управляемое
  • Время кеширования (сек.): 3600

Используется php 5.3.16 + wincache.

realpath_cache_size 8000k 8000k
realpath_cache_ttl 120 120

Как выяснить причину?

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

общий ужас выглядит так:

У вас в юрле, случайно ckear_cache=Y не установлен?

Если ни чего не помогает — в ТП. Возможно проблемы с хостингом (дата, время, каталоги под кеш не верно формируются)

Цитата
Евгений Смолин пишет:
У вас в юрле, случайно ckear_cache=Y не установлен?

Детальный анализ выявил проблемы в работе wincache
тех.поддержка заявила о нем как «не гарантируется поддержка» и рекомендовала перейти на APC, Xcache
Apc не удалось подружить с многопоточным IIS, в рузальтате данные хоть и кешировались, но страница выдавалась со значительной задержкой
Xcache вместе с IIS 7 показал себя неплохо, на нем и остановился

возник некий глюк с Xcache
компонент bitrix:news.list содержит в себе другой нетиповой компонент , кеш работает нормально — без запросов. но стилевое оформление вложенного компонента сбивается. На уровне html в коде страницы с и без кеширования различий не заметил.
будем разбираться дальше.

Может кто уже знает ответ на вопрос проблемы со стилями при кешировании.. ?

Источник

Увеличение производительности

Коллеги, добры день!

Столкнулись с проблемой после запуска с корпоративного портала. Работает в БД Oracle. Может быть кто то сталкивался с похожей проблемой?

Как видно сильно проседают БД.

Также есть вопросы по файлу конфигурации httpd.conf. Правильно ли настроен модуль prefork? Сайт имеет около 2500-3000 одновременных подключений.

StartServers 60
MinSpareServers 60
MaxSpareServers 60
ServerLimit 60
MaxClients 60
MaxRequestsPerChild 10000

И вопрос по файлу dbconn.php, параметр memory_limit = 1024, насколько верна такая настройка?

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

bogdan.dudarev, портал работает в окружении, которое затащил на сервер скрипт Битрикс или сами настраивали все службы?

Автотест что показывает? /bitrix/admin/site_checker.php?lang=ru

Кеширование включено? /bitrix/admin/cache.php?lang=ru

СУБД на отдельном сервере?

Низкая скорость записи в базу — это скорее над править настройки базы или ей просто не хватает ресурсов.

Какая-то кастомизация была? Что показывает тестирование монитора производительности минут на 10?

Тест на масштабируемость показывает ошибки при генерации страниц? /bitrix/admin/perfmon_panel.php?lang=ru

Цитата
Максим Кучук пишет:
/bitrix/admin/cache.php?lang=ru

1 . Oracle — т.к. все на нем. Требование компании.
2 . Автотест показывает это:
2014-Sep-26 10:52:31 Наличие необходимых модулей php (check_php_modules): OkУспешно
2014-Sep-26 10:52:32 Обязательные параметры PHP (check_php_settings): OkУспешно
2014-Sep-26 10:52:32 Значения переменных сервера (check_server_vars): OkУспешно
2014-Sep-26 10:52:32 Модуль suhosin отключен (check_suhosin): OkУспешно
2014-Sep-26 10:52:32 Модули веб сервера (check_security): OkУспешно
2014-Sep-26 10:52:32 Служебные скрипты в корне сайта (check_install_scripts): OkУспешно
2014-Sep-26 10:52:32 Параметры настройки UTF (mbstring и константа BX_UTF) (check_mbstring): OkУспешно. Сайт работает в UTF кодировке
2014-Sep-26 10:52:33 Установка параметра pcre.backtrack_limit (check_backtrack_limit): OkУспешно
2014-Sep-26 10:52:33 Размер стека и pcre.recursion_limit (check_pcre_recursion): FailОшибка
2014-Sep-26 10:52:33 Вызов method_exists на строке (check_method_exists): Fail== Request ==GET /bitrix/admin/site_checker.php?test_type=method_exists&unique_id=d9387285f7b232bcc766833ba1 ­8d9ef8 HTTP/1.1Host: msk03portal.sibur.local== Response ==HTTP/1.1 401 Authorization RequiredDate: Fri, 26 Sep 2014 06:52:33 GMTServer: ApacheWWW-Authenticate: NTLMContent-Length: 401Content-Type: text/html; charset=iso-8859-1== Body ==401 Authorization RequiredAuthorization RequiredThis server could not verify that youare authorized to access the documentrequested. Either you supplied the wrongcredentials (e.g., bad password), or yourbrowser doesn’t understand how to supplythe credentials required.==========Ошибка
2014-Sep-26 10:52:33 Настройки сайтов (check_sites): OkУспешно
2014-Sep-26 10:52:33 Работа с сокетами (check_socket): FailConnection to msk03portal.sibur.local:80== Request ==GET /bitrix/admin/site_checker.php?test_type=socket_test&unique_id=d9387285f7b232bcc766833ba18d ­9ef8 HTTP/1.1Host: msk03portal.sibur.local== Response ==HTTP/1.1 401 Authorization RequiredDate: Fri, 26 Sep 2014 06:52:33 GMTServer: ApacheWWW-Authenticate: NTLMContent-Length: 401Content-Type: text/html; charset=iso-8859-1== Body ==401 Authorization RequiredAuthorization RequiredThis server could not verify that youare authorized to access the documentrequested. Either you supplied the wrongcredentials (e.g., bad password), or yourbrowser doesn’t understand how to supplythe credentials required.==========Ошибка
2014-Sep-26 10:52:33 Лишний вывод в файлах конфигурации (check_dbconn): WarningНе удалось проверить из-за ошибки в работе с сокетами
2014-Sep-26 10:52:33 Загрузка файла (check_upload): WarningНе удалось проверить из-за ошибки в работе с сокетами
2014-Sep-26 10:52:34 Загрузка файла больше 4Мб (check_upload_big): WarningНе удалось проверить из-за ошибки в работе с сокетами
2014-Sep-26 10:52:34 Загрузка файла через php://input (check_upload_raw): WarningНе удалось проверить из-за ошибки в работе с сокетами
2014-Sep-26 10:52:34 Передача POST запроса с большим числом параметров (check_post): WarningНе удалось проверить из-за ошибки в работе с сокетами
2014-Sep-26 10:52:34 Отправка почты (check_mail): OkУспешно
2014-Sep-26 10:52:34 Отправка почтового сообщения больше 64Кб (check_mail_big): OkУспешно
2014-Sep-26 10:52:34 Проверка на наличие неотправленных сообщений (check_mail_b_event): OkУспешно
2014-Sep-26 10:52:35 Внутреннее перенаправление (функция LocalRedirect) (check_localredirect): WarningНе удалось проверить из-за ошибки в работе с сокетами
2014-Sep-26 10:52:35 Фактическое ограничение памяти (check_memory_limit): WarningНе удалось проверить из-за ошибки в работе с сокетами2014-Sep-26 10:52:35 Сохранение сессии (check_session): Ok50% done
2014-Sep-26 10:52:35 Сохранение сессии (check_session): OkУспешно
2014-Sep-26 10:52:35 Сохранение сессии без UserAgent (check_session_ua): WarningНе удалось проверить из-за ошибки в работе с сокетами2014-Sep-26 10:52:36 Работа с файлами кеша (check_cache): OkУспешно
2014-Sep-26 10:52:36 Доступ к серверу обновлений (check_update): FailНет соединения с сервером обновлений (Connection refused)2014-Sep-26 10:52:36 HTTP авторизация (check_http_auth): WarningНе удалось проверить из-за ошибки в работе с сокетами
2014-Sep-26 10:52:36 Создание и запуск файла (check_exec): WarningНе удалось проверить из-за ошибки в работе с сокетами
2014-Sep-26 10:52:36 Константа BX_CRONTAB (не должна быть определена) (check_bx_crontab): OkУспешно
2014-Sep-26 10:52:37 Разделитель дробной части (check_divider): OkУспешно
2014-Sep-26 10:52:37 Значение параметра precision (не менее 10) (check_precision): OkУспешно
2014-Sep-26 10:52:38 Передача объектов по ссылке (check_clone): OkУспешно
2014-Sep-26 10:52:40 Функция getimagesize для swf (check_getimagesize): OkУспешно
3 . Автокеширование компонентов включено.
4 . СУБД на отдельном сервере
5. Кастомизация была не значительная
6. Тестирование минут на 10:

7. Масштабируемость:

1. Переведите работу агентов на cron с хитов, если это так

2. Попробуйте включить html кеш

3. Тест на масштабриуемость показывает наличие ошибок при генерации страницы. Их быть не должно.

Источник

Проблемы при работе с кэшем и способы их решения

Меня зовут Виктор Пряжников, я работаю в SRV-команде Badoo. Наша команда занимается разработкой и поддержкой внутреннего API для наших клиентов со стороны сервера, и кэширование данных — это то, с чем мы сталкиваемся каждый день.

Существует мнение, что в программировании есть только две по-настоящему сложные задачи: придумывание названий и инвалидация кэша. Я не буду спорить с тем, что инвалидация — это сложно, но мне кажется, что кэширование — довольно хитрая вещь даже без учёта инвалидации. Есть много вещей, о которых следует подумать, прежде чем начинать использовать кэш. В этой статье я попробую сформулировать некоторые проблемы, с которыми можно столкнуться при работе с кэшем в большой системе.

Я расскажу о проблемах разделения кэшируемых данных между серверами, параллельных обновлениях данных, «холодном старте» и работе системы со сбоями. Также я опишу возможные способы решения этих проблем и приведу ссылки на материалы, где эти темы освещены более подробно. Я не буду рассказывать, что такое кэш в принципе и касаться деталей реализации конкретных систем.

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

Деление данных между кэширующими серверами

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

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

Самый очевидный способ разбивки данных — вычисление номера сервера псевдослучайным образом в зависимости от ключа кэширования.

Есть разные алгоритмы для реализации этого. Самый простой — вычисление номера сервера как остатка от целочисленного деления численного представления ключа (например, CRC32) на количество кэширующих серверов:

Такой алгоритм называется хешированием по модулю (англ. modulo hashing). CRC32 здесь использован в качестве примера. Вместо него можно взять любую другую хеширующую функцию, из результатов которой можно получить число, большее или равное количеству серверов, с более-менее равномерно распределённым результатом.

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

Я написал небольшой скрипт, который продемонстрирует эту проблему.

В нём генерируется 1 млн уникальных ключей, распределённых по пяти серверам с помощью хеширования по модулю и CRC32. Я эмулирую выход из строя одного из серверов и перераспределение данных по четырём оставшимся.

В результате этого «сбоя» примерно 80% ключей изменят своё местоположение, то есть окажутся недоступными для последующего чтения:

Total keys count: 1000000
Shards count range: 4, 5

ShardsBefore ShardsAfter LostKeysPercent LostKeys
5 4 80.03% 800345

Самое неприятное тут то, что 80% — это далеко не предел. С увеличением количества серверов процент потери кэша будет расти и дальше. Единственное исключение — это кратные изменения (с двух до четырёх, с девяти до трёх и т. п.), при которых потери будут меньше обычного, но в любом случае не менее половины от имеющегося кэша:


Я выложил на GitHub скрипт, с помощью которого я собрал данные, а также ipynb-файл, рисующий данную таблицу, и файлы с данными.

Для решения этой проблемы есть другой алгоритм разбивки — согласованное хеширование (англ. consistent hashing). Основная идея этого механизма очень простая: здесь добавляется дополнительное отображение ключей на слоты, количество которых заметно превышает количество серверов (их могут быть тысячи и даже больше). Сами слоты, в свою очередь, каким-то образом распределяются по серверам.

При изменении количества серверов количество слотов не меняется, но меняется распределение слотов между этими серверами:

  • если один из серверов выходит из строя, то все слоты, которые к нему относились, распределяются между оставшимися;
  • если добавляется новый сервер, то ему передаётся часть слотов от уже имеющихся серверов.

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

На картинке начального разбиения все слоты одного сервера расположены подряд, но в реальности это не обязательное условие — они могут быть расположены как угодно.

Основное преимущество этого способа перед предыдущим заключается в том, что здесь каждому серверу соответствует не одно значение, а целый диапазон, и при изменении количества серверов между ними перераспределяется гораздо меньшая часть ключей ( k / N , где k — общее количество ключей, а N — количество серверов).

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

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

Наряду с согласованным хешированием есть и другие способы решения этой проблемы (например, rendezvous hashing), но они гораздо менее распространены.

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

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

В нашем случае почти все кэшируемые данные относятся к одному пользователю, поэтому мы используем User ID в качестве параметра шардирования данных в кэше. Благодаря этому у нас получается распределить данные более-менее равномерно. Кроме того, мы получаем бонус — возможность использования multi_get для загрузки сразу нескольких разных ключей с информацией о юзере (что мы используем в предзагрузке часто используемых данных для текущего пользователя). Если бы положение каждого ключа определялось динамически, то невозможно было бы использовать multi_get при таком сценарии, так как не было бы гарантии, что все запрашиваемые ключи относятся к одному серверу.

Параллельные запросы на обновление данных

Посмотрите на такой простой кусочек кода:

Что произойдёт при отсутствии запрашиваемых данных в кэше? Судя по коду, должен запуститься механизм, который достанет эти данные. Если код выполняется только в один поток, то всё будет хорошо: данные будут загружены, помещены в кэш и при следующем запросе взяты уже оттуда. А вот при работе в несколько параллельных потоков всё будет иначе: загрузка данных будет происходить не один раз, а несколько.

Выглядеть это будет примерно так:

На момент начала обработки запроса в процессе №2 данных в кэше ещё нет, но они уже читаются из базы данных в процессе №1. В этом примере проблема не такая существенная, ведь запроса всего два, но их может быть гораздо больше.

Количество параллельных загрузок зависит от количества параллельных пользователей и времени, которое требуется на загрузку необходимых данных.

Предположим, у вас есть какой-то функционал, использующий кэш с нагрузкой 200 запросов в секунду. Если на на загрузку данных нужно 50 мс, то за это время вы получите 50 / (1000 / 200) = 10 запросов.

То есть при отсутствии кэша один процесс начнёт загружать данные, и за время загрузки придут ещё девять запросов, которые не увидят данные в кэше и тоже станут их загружать.

Эта проблема называется cache stampede (русского аналога этого термина я не нашёл, дословно это можно перевести как «паническое бегство кэша», и картинка в начале статьи показывает пример этого действия в дикой природе), hit miss storm («шторм непопаданий в кэш») или dog-pile effect («эффект собачьей стаи»). Есть несколько способов её решения:

Блокировка перед началом выполнения операции пересчёта/ загрузки данных

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

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

Кроме того, нужно тщательно выбирать время самой блокировки — его гарантированно должно хватить на то, чтобы загрузить данные из источника и положить в кэш. Если не хватит, то повторную загрузку данных может начать другой параллельный процесс. С другой стороны, если этот временной промежуток будет слишком большим и процесс, получивший блокировку, умрёт, не записав данные в кэш и не освободив блокировку, то другие процессы также не смогут получить эти данные до окончания времени блокировки.

Вынос обновлений в фон

Основная идея этого способа — разделение по разным процессам чтения данных из кэша и записи в него. В онлайн-процессах происходит только чтение данных из кэша, но не их загрузка, которая идёт только в отдельном фоновом процессе. Данный вариант делает невозможными параллельные обновления данных.

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

Этот вариант мы в Badoo используем, например, для счётчика общего количества пользователей, про который ещё пойдёт речь дальше.

Вероятностные методы обновления

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

Для корректной работы такого механизма нужно, чтобы в начале срока жизни закэшированных данных вероятность пересчёта была небольшой, но постепенно увеличивалась. Добиться этого можно с помощью алгоритма XFetch, который использует экспоненциальное распределение. Его реализация выглядит примерно так:

В данном примере $ttl — это время жизни значения в кэше, $delta — время, которое потребовалось для генерации кэшируемого значения, $expiry — время, до которого значение в кэше будет валидным, $beta — параметр настройки алгоритма, изменяя который, можно влиять на вероятность пересчёта (чем он больше, тем более вероятен пересчёт при каждом запросе). Подробное описание этого алгоритма можно прочитать в white paper «Optimal Probabilistic Cache Stampede Prevention», ссылку на который вы найдёте в конце этого раздела.

Нужно понимать, что при использовании подобных вероятностных механизмов вы не исключаете параллельные обновления, а только снижаете их вероятность. Чтобы исключить их, можно «скрестить» несколько способов сразу (например, добавив блокировку перед обновлением).

«Холодный» старт и «прогревание» кэша

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

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

От такой проблемы нельзя полностью избавиться, но можно «размазать» загрузки данных по времени, исключив тем самым резкое количество параллельных запросов к базе. Добиться этого можно несколькими способами:

  • плавным включением нового функционала. Для этого необходим механизм, который позволит это сделать. Простейший вариант реализации — выкатывать новый функционал включённым на небольшую часть пользователей и постепенно её увеличивать. При таком сценарии не должно быть сразу большого вала обновлений, так как сначала функционал будет доступен только части пользователей, а по мере её увеличения кэш уже будет «прогрет».
  • разным временем жизни разных элементов набора данных. Данный механизм можно использовать, только если система в состоянии выдержать пик, который наступит при выкатке всего функционала. Его особенность заключается в том, что при записи данных в кэш у каждого элемента будет своё время жизни, и благодаря этому вал обновлений сгладится гораздо быстрее за счёт распределения последующих обновления во времени. Простейший способ реализовать такой механизм — умножить время жизни кэша на какой-то случайный множитель:

Если по какой-то причине не хочется использовать случайное число, можно заменить его псевдослучайным значением, полученным с помощью хеш-функции на базе каких-нибудь данных (например, User ID).

Пример

Я написал небольшой скрипт, который эмулирует ситуацию «непрогретого» кэша.
В нём я воспроизвожу ситуацию, при которой пользователь при запросе загружает данные о себе (если их нет в кэше). Конечно, пример синтетический, но даже на нём можно увидеть разницу в поведении системы.

Вот как выглядит график количества hit miss-ов в ситуации с фиксированным (fixed_cache_misses_count) и различным (random_cache_misses_count) сроками жизни кэша:

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

«Горячие» ключи

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

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

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

Мы в Badoo решаем эту проблему тем, что пишем данные во все кэширующие серверы сразу. Благодаря этому при чтении мы можем использовать общий механизм выбора сервера — в коде можно использовать обычный механизм шардирования по User ID, и при чтении не нужно ничего знать про специфику этого «горячего» ключа. В нашем случае это работает, поскольку у нас сравнительно немного серверов (примерно десять на площадку).

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

Если вы используете определение сервера по ключу кэша, то можно добавить к нему ограниченное количество псевдослучайных значений (сделав из total_users_count что-то вроде t otal_users_count_1 , total_users_count_2 и т. д.). Подобный подход используется, например, в Etsy.

Если вы используете явные указания параметра шардирования, то просто передавайте туда разные псевдослучайные значения.

Главная проблема с обоими способами — убедиться, что разные значения действительно попадают на разные кэширующие серверы.

Сбои в работе

Система не может быть надёжной на 100%, поэтому нужно предусмотреть, как она будет вести себя при сбоях. Сбои могут быть как в работе самого кэша, так и в работе базы данных.

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

При сбоях в работе базы данных и отсутствии кэша мы можем попасть в ситуацию cache stampede, про которую я тоже уже рассказывал раньше. Выйти из неё можно уже описанными способами, а можно записать в кэш заведомо некорректное значение с небольшим сроком жизни. В этом случае система сможет определить, что источник недоступен, и на какое-то время перестанет пытаться запрашивать данные.

Источник

Читайте также:  Не работает приборная панель ленд ровер
Оцените статью