317  /  382
Справочник

Примеры. Кешируем правильно

Просмотров: 25336
Дата последнего изменения: 15.11.2023
Марина Павлова
Сложность урока:
4 уровень - сложно, требуется сосредоточиться, внимание деталям и точному следованию инструкции.
1
2
3
4
5
Недоступно в лицензиях:
Ограничений нет

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

  Кешируем правильно

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

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

Комплексный пример: класс кеширования пользователя

Как неправильно:

// Класс кеширования пользователя с некорретным кешированием
class User
{
	private $userData;
	function __construct($userId = false)
	{
		if (!$userId)
		{
			global $USER;
			$userId = $USER->GetID();
		}

		$cntStartCacheId = __CLASS__.'::'.__FUNCTION__.'|'.SITE_ID.'|'.$userId;
		$cache = new \CXxxCache($cntStartCacheId.'sid0',3600,'user_data');
		$this->userData = $cache->Init();

		if (null == $this->userData)
		{
			$this->putUserData(array("ID"=>$userId));
			$this->putUserData(\CUser::GetByID($userId)->Fetch());
			$this->putUserData(array("DEPARTMENT" => $this->getDepartment()));

			$cache->registerTag('USER_NAME_'.$userId);
			$cache->set($this->userData);
		}
	}
}
Что не так в примере выше:
1. Общая папка для кеша пользователей
Такой пример кеша практически не работает на проекте. На портале, где много пользователей, мы постоянно будем терять кеш Большое количество хитов попадает в том момент, когда кеш уже скинут. Происходит это из-за того, что кеш пользователей уникальный на каждого человека и его хотелось бы скидывать отдельно. В примере указана общая папка для кеша, а не отдельная для пользователя. Следовательно, когда мы хотим скинуть кеш для пользователя, мы скидываем его его для всех. , что негативно отразиться на его работе.
2. Время кеширования
Кеширование сущности в примере на 1 час - неэффективно. Сохранение на малое время может привести к массовой генерации кеша Простой пример: сотрудники ушли на обед и вернулись через час, а кеш уже у всех сброшен. . Такая проблема актуальна для больших компаний и проектов.
3. Заполнение пользователя методом GetByID
В кеш попадает большой объем данных о пользователе, а именно много ненужной информации (пароль, подтверждение пароля, настройки синхронизации и т.д.).
4. Использование Fetch
При использовании Fetch данные не экранируются и никак не проверяются, что приводит к ошибкам безопасности.

Как правильно:

GetID();
		}

		$cntStartCacheId = __CLASS__.'::'.__FUNCTION__.'|'.SITE_ID.'|'.$userId;
		$cache = new \CXxxCache(
			$cntStartCacheId.'sid0',
			// увеличили время кеширования
			604800,
			// путь для ключей кеша сделали зависимым от $userID
			'user_data/'.substr(md5($userId),2,2).'/'.$userId 
		);

		$this->userData = $cache->Init();

		if (null == $this->userData)
		{
			$this->putUserData(array("ID"=>$userId));

			// Выбираем только нужные поля
			$this->putUserData(\CUser::GetList(...)->GetNext(true, false));
			$this->putUserData(array("DEPARTMENT" => $this->getDepartment()));

			$cache->registerTag('USER_NAME_'.$userId);
			$cache->set($this->userData);
		}
	}
}
  • Увеличили время кеширования до недели;
  • Изменили папку хранения кеша (разложили персонализированный кеш по подпапкам);
  • Изменили сбор информации по пользователю с GetByID на GetList Достаточно часто в проектах многие используют вызов CIBlockElement::GetById. Простой, удобный метод, когда надо вытащить какое-то поле для элемента инфоблока. Но этот метод тянет все поля и все свойства элемента. В случае инфоблока с большим количеством свойств и большого числа посетителей на сайте этот простой запрос приводит к снижению производительности.

    Подробнее...
    ;
  • Заменили Fetch на GetNext(true, false).

Время кеширования (cache key and ttl)

Пример на компоненте, который отображает дни рождения. Установлено большое время кеширования и добавлен дополнительный параметр, который сам компонент никак не отрабатывает и его не обрабатывает шаблон. Но так как в параметры подставлен date, то в 0 часов получим новый ключ у кеша данного компонента.

IncludeComponent(
	"bitrix:intranet.structure.birthday.nearest",
	"widget",
	Array(
		"CACHE_TYPE" => "A",
		"CACHE_TIME" => "86450",
		"DATE_FORMAT" => "j F",
		"DETAIL_URL" => "#SITE_DIR#company/personal/user/#USER_ID#/",
		"DEPARTMENT" => "0",
		.....
		"CACHE_DATE" => date('dmy')
	)
);

Пример: как в API правильно подставлять ключи

В таком случае часто проставляют лишние параметры, что приводит к увеличению кеша. (например date без параметров приводит к обновлению кеша каждую секунду).

<?php

// Пример добавление в ключ кеша метки времени для корректного переключения кеша. Метка может быть и не из времени.

$cache = Bitrix\Main\Data\Cache::createInstance();
if ($cache->initCache(86450, '/some_key/'.date('myd').'/', '/some_dir/'))
{
	$var = $cache->getVars();
}
else
{
	// Получение данных
	$cache->startDataCache();
	$cache->endDataCache($var);
}

Отключаем сброс ключей

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

  1. Отключите кеширование элементов (сбрасывание тегированного кеша) перед импортом;
  2. Включите его по окончании процесса импорта элементов;
  3. Сбросьте те теги инфоблока, которые сбрасывались в данном случае.
В таком вариант кеш сбросится один раз после полной загрузки, а не после загрузки каждого элемента.

Индексация фасетного индекса также может быть отложена (т.е. отключена перед импортом и включена по окончании).

<?php
// Отключение сброса тегированного кеша инфоблоков и пересчета фасетного индекса, во время импорта.

Bitrix\Iblock\PropertyIndex\Manager::enableDeferredIndexing();
Bitrix\Catalog\Product\Sku::enableDeferredCalculation();

\CAllIBlock::disableTagCache($iblockID);

// Импорт элементов 

\CAllIBlock::enableTagCache($iblockID);
\CAllIBlock::clearIblockTagCache($iblockID);

Bitrix\Catalog\Product\Sku::disableDeferredCalculation();
Bitrix\Catalog\Product\Sku::calculate();

Bitrix\Iblock\PropertyIndex\Manager::disableDeferredIndexing();
Bitrix\Iblock\PropertyIndex\Manager::runDeferredIndexing($iblockID);

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


Итоги рекомендаций по кешированию

ХорошоПлохо
  • Уникальные ключи, в отдельных папках;
  • Только нужные поля в кеше;
  • Максимальное время хранения ключей;
  • Конечные списки в одном ключе;
  • Разумное количество тегов в ключе;
  • Сброс ключей по пути.
  • Закрывать кешем долгие вычисления или страницы;
  • Часто сбрасывать ключи, особенно в большом количестве.

Внимание! Не забывайте, что управляемый кеш инфоблоков очищается только при вызове CIBlockElement::Update. При изменении, например, свойств с помощью CIBlockElement::SetPropertyValueCode очистки не произойдет. Делаем вручную после изменения:
if(defined('BX_COMP_MANAGED_CACHE'))
   $GLOBALS['CACHE_MANAGER']->ClearByTag('iblock_id_'.$arParams['IBLOCK_ID']);



15
Курсы разработаны в компании «1С-Битрикс»

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