10 июня 2018
В статеь я расскажу почему так происходит.
Когда я разобрал все устройство типового компонента и убрал все лишнее мне удалось оставить только два запроса при таких же условиях.
Отсюда родился совет, даже не мой а его часто можно встретить на форумах от гуру битрикса:
И этот совет оказывается верным. В этом уроке мы напишем собственный компонент и сравним его работу с типовым компонентом новостей. Я покажу в чем разница и в чем проблема типового компонента.
Начнем.
Напомню, что компонент Битрикс состоит из следующих файлов:
Структура статьи будет следующая:
Код:
В параметрах компонента появляется возможность выбрать тип инфобока и сам инфоблок. Массив $arCurrentValues содержит значения текущих параметров выбранных в форме настроек.
Далее определяются поля для настройки сортировки, тут просто указан предопределенный массив полей для сортировки $arSortFields.
Код:
На самом деле полей для сортировки гораздо больше, тут указаны самые основные. Все поля сортировки можно посмотреть в документации. https:/dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/getlist.php.
Далее тот же фокус проделывается со свойствами инфоблоков, выбираются все свойства инфоблока и подготавливается список к выбору.
Код:
Вот и все. Подготовительные этапы закончены и далее строится сам массив параметров $arComponentParameters.
Код:
Ключевым здесь является конструкция "VALUES" => $arTypesEx и подобные, где параметру компонента присваивается массив, который определили выше по коду.
В итоге мы получили совсем не сложную форму настроек компонента.
С параметрами закончили и переходим к главному файлу компонента Битрикс component.php.
В начале происходит валидация массива $arParams и переопределение его значений. Например, есть конструкция, которая может поставить в тупик на первый взгляд.
Код:
Но если вы обратите внимание на настройки компонента (см. скриншот выше) то увидите, что под списком полей есть возможность вписать дополнительное значение. И оно уходит в массив $arParams["FIELD_CODE"]. Так вот в цикле проверяется это значение и если оно пустое то очищается.
Далее идет самый главный кусок кода - выборка данных из инфоблока с проверкой на наличие кеша Битрикс.
Код:
Тут вам должно быть все понятно, кроме первых 2 строчек. Но если вдруг вы все же не знакомы ещё с API Bitrix и выборкой данных из инфоблока через GetList то рекомендую урок про разработку собственного компонента Битрикс Урок 9. Создание собственного компонента слайдера, а также посмотреть подробную информацию об устройстве инфоблока (из каких таблиц состоит, как настраивается и т.д.) Урок 5. Инфоблоки битрикс (структура и создание).
Я же остановлюсь на кешировании в Битрикс. За кеширование отвечает функция $this->startResultCache($arParams['CACHE_TIME'], $additionalCacheID).
Что она делает? Эта функция проверяет существует ли кеш для этого компонента, а также не прошло ли его время, т.е. не устарел ли он. Если возвращается значение ложь, то код ниже пропускается и данные берутся из кеша. Если же кеша нет или он не актуален то выполняется код внутри условия. В нашем случае выполняется запрос к инфоблоку.
Теперь ответим на вопрос: Как проверяется кеш? Кеш зависит от нескольких параметров:
Последними строчками проверяется получены-ли данные из инфоблока и если нет то сбрасывается кеш $this->abortResultCache(). В противном случае будет не оправданно расти кеш.
Подробнее о кешировании в Битрикс можно посмотреть в документации https:/dev.1c-bitrix.ru/api_help/main/reference/cbitrixcomponent/startresultcache.php.
Код:
Завершается работа компонента созданием массива $arResult с подмассивом $arItems для удобства и функцией $this->includeComponentTemplate() вывод перенаправляется в шаблон компонента.
Код:
Все элементарно в цикле мы выводим данные массива.
Сначала создадим тестовую страницу и разместим на ней наш компонент.
Включаем отладку.
Смотрим результат после сброса кеша.
Следующее обновления страницы берет данные уже из кеша.
Теперь усложним задачу и добавим свойства в компонент. Сначала добавим всего одно свойство.
И в результате у нас уже два запроса к базе, что вполне логично т.к. свойства хранятся в отдельных таблицах.
Теперь добавляем 5 свойств, все что есть в этом инфоблоке.
И что мы видим - у нас уже 6 запросов к базе (один к инфоблоку и 5 к свойствам). Т.е. на каждое свойство в инфоблоке выполняется по одному запросу к базе. Это нужно запомнить и не выбирать не нужные свойства из инфоблока при настройке компонентов.
А теперь, как обещал сравним с типовым компонентом bitrix:news.list (Список новостей). Будет ли наш компонент работать более оптимально или может зря я кучу времени потратил?
Разместим на странице компонент список новостей, шаблон создадим точно такой же как и в существующем компоненте. Настройки сделаем идентичные: 5 записей, одно поле наименование без свойств.
Смотрим результат.
Кеширование конечно работает и в этом компоненте Результат bitrix:news.list: 0.0531 с; кеш: 10 КБ, уж не буду загромождать статью скриншотами.
Ну и последнее добавим свойства в выборку компонента. Добавим только одно свойство.
И снова жесть - 10 запросов к базе. откуда они взялись??? Это же кошмар просто.
А что если увеличить выборку, я поставил 200 строк.
Результат 205 запросов.
А взялись они из особенностей работы типового компонента. Он выбирает всегда все свойство из базы даже когда выбрано только одно.
Обратите внимание на кусок коду из bitrix:news.list. Причем
Код:
Как видите если в $arParams есть хоть одно свойство, то выбираются сразу все. А ниже уже в цикле обхода выполняется запрос к заначениям свойств. Из-за этого мы и получаем 200 запросов при выборке 200 строк. В моем компоненте даже при выводе 200 строк остаются все те же 6 запросов, т.к. нет выборок в цикле это видано из кода.
И это ещё у меня хорошо мало свойств, а если вы работаете с каталогом товаров в интернет-магазине, где этих свойств куча. Что тогда произойдет? А тогда происходят вот такие вещи.
86 запросов чтобы вывести 4 просмотренных товара.
69 запросов, чтобы просто вывести список брендов.
Надеюсь замотивировал вас на то, чтобы разбираться и писать собственные компоненты. Убирать все лишнее и добавится минимальных запросов к базе.
Исходники моего универсального компонента.
От автора:
В этом уроке я рассмотрел наверное самую сложную тему из базового функционала инфоблоков Битрикс - кеширование. Собственно увиденное на последних 2 скринах меня и побудило разобраться, как же работают компоненты Битрикса. И я не пожалел, что разобрался. Без этих знаний такое огромное число запросов шокирует и даже начинаешь сомневаться в адекватности функции отладки. Но нет она не врет к сожалению. Надеюсь и вы почерпнете важные для себя знания и "черный ящик-битрикс" станет для вас немного меньше. В следующем уроке я планирую рассмотреть, как влияет массив $additionalCacheID на кеширование. Ответить на вопрос, как делать кастомный кеш в Битриксе. Обязательно подпишитесь на новые статьи и до скорого!
Урок 14. Как работает Кеш Битрикс на примере собственного компонента
Приветствую, мои читатели! В этом уроке мы создадим собственный универсальный компонент Битрикс для вывода данных из инфоблока. Для чего он нужен спросите вы, когда уже есть типовой компонент bitrix:news.list? А дело в том, что типовые компоненты Битрикс слишком универсальны и содержат много лишнего кода. Но это было бы под беды, главное они генерируют лишние запросы к базе данных. Забегая в перед покажу скрин, как типовой компонент списка новостей генерирует 200 запросов к базе.В статеь я расскажу почему так происходит.
Когда я разобрал все устройство типового компонента и убрал все лишнее мне удалось оставить только два запроса при таких же условиях.
Отсюда родился совет, даже не мой а его часто можно встретить на форумах от гуру битрикса:
Для простых задач подобно выборки данных из инфоблока не пользуйтесь типовыми компонентами новостей, а пишите собственные компоненты.
Начнем.
Напомню, что компонент Битрикс состоит из следующих файлов:
- component.php - основной фал компонента, реализация логики работы компонента на API Bitrix.
- .description.php - файл описания компонента.
- .parameters.php - файл параметров компонента.
- templates/.default/template.php - файл шаблона по умолчанию для компонента.
Структура статьи будет следующая:
- Разбор файла .parameters.php
- Разбор файла component.php
- Простой макет шаблона template.php
- Анализ SQL запросов, которые генерирует компонент
Разбор файла .parameters.php
В начале инициируются переменные со списком выбора типов инфоблоков и самих инфоблоков.Код:
$arTypesEx = CIBlockParameters::GetIBlockTypes(array("-"=>" "));/инициируется массив типов инфоблоков
$arIBlocks=array();/инициируется массив ID инфоблоков
$db_iblock = CIBlock::GetList(array("SORT"=>"ASC"), array("SITE_ID"=>$_REQUEST["site"], "TYPE" => ($arCurrentValues["IBLOCK_TYPE"]!="-"?$arCurrentValues["IBLOCK_TYPE"]:"")));
while($arRes = $db_iblock->Fetch())
$arIBlocks[$arRes["ID"]] = "[".$arRes["ID"]."] ".$arRes["NAME"];
В параметрах компонента появляется возможность выбрать тип инфобока и сам инфоблок. Массив $arCurrentValues содержит значения текущих параметров выбранных в форме настроек.
Далее определяются поля для настройки сортировки, тут просто указан предопределенный массив полей для сортировки $arSortFields.
Код:
$arSortFields = array(
"ID"=>GetMessage("T_IBLOCK_DESC_FID"),
"NAME"=>GetMessage("T_IBLOCK_DESC_FNAME"),
"ACTIVE_FROM"=>GetMessage("T_IBLOCK_DESC_FACT"),
"SORT"=>GetMessage("T_IBLOCK_DESC_FSORT"),
"TIMESTAMP_X"=>GetMessage("T_IBLOCK_DESC_FTSAMP")
);
На самом деле полей для сортировки гораздо больше, тут указаны самые основные. Все поля сортировки можно посмотреть в документации. https:/dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/getlist.php.
Далее тот же фокус проделывается со свойствами инфоблоков, выбираются все свойства инфоблока и подготавливается список к выбору.
Код:
$arProperty_LNS = array();
$rsProp = CIBlockProperty::GetList(array("sort"=>"asc", "name"=>"asc"), array("ACTIVE"=>"Y", "IBLOCK_ID"=>(isset($arCurrentValues["IBLOCK_ID"])?$arCurrentValues["IBLOCK_ID"]:$arCurrentValues["ID"])));
while ($arr=$rsProp->Fetch())
{
$arProperty[$arr["CODE"]] = "[".$arr["CODE"]."] ".$arr["NAME"];
if (in_array($arr["PROPERTY_TYPE"], array("L", "N", "S")))
{
$arProperty_LNS[$arr["CODE"]] = "[".$arr["CODE"]."] ".$arr["NAME"];
}
}
Вот и все. Подготовительные этапы закончены и далее строится сам массив параметров $arComponentParameters.
Код:
$arComponentParameters = array(
"GROUPS" => array(
),
"PARAMETERS" => array(
"IBLOCK_TYPE" => array(
"PARENT" => "BASE",
"NAME" => GetMessage("T_IBLOCK_DESC_LIST_TYPE"),
"TYPE" => "LIST",
"VALUES" => $arTypesEx,
"DEFAULT" => "news",
"REFRESH" => "Y",
),
"IBLOCK_ID" => array(
"PARENT" => "BASE",
"NAME" => GetMessage("T_IBLOCK_DESC_LIST_ID"),
"TYPE" => "LIST",
"VALUES" => $arIBlocks,
"DEFAULT" => '={$_REQUEST["ID"]}',
"ADDITIONAL_VALUES" => "Y",
"REFRESH" => "Y",
),
"TOP_COUNT" => array(
"PARENT" => "BASE",
"NAME" => GetMessage("T_IBLOCK_DESC_LIST_CONT"),
"TYPE" => "STRING",
"DEFAULT" => "20",
),
"SORT_BY1" => array(
"PARENT" => "DATA_SOURCE",
"NAME" => GetMessage("T_IBLOCK_DESC_IBORD1"),
"TYPE" => "LIST",
"DEFAULT" => "ACTIVE_FROM",
"VALUES" => $arSortFields,
"ADDITIONAL_VALUES" => "Y",
),
"SORT_ORDER1" => array(
"PARENT" => "DATA_SOURCE",
"NAME" => GetMessage("T_IBLOCK_DESC_IBBY1"),
"TYPE" => "LIST",
"DEFAULT" => "DESC",
"VALUES" => $arSorts,
"ADDITIONAL_VALUES" => "Y",
),
"FIELD_CODE" => $arFieldsCode,
"PROPERTY_CODE" => array(
"PARENT" => "DATA_SOURCE",
"NAME" => GetMessage("T_IBLOCK_PROPERTY"),
"TYPE" => "LIST",
"MULTIPLE" => "Y",
"VALUES" => $arProperty_LNS,
"ADDITIONAL_VALUES" => "Y",
),
"CACHE_TIME" => array("DEFAULT"=>36000000),
),
);
Ключевым здесь является конструкция "VALUES" => $arTypesEx и подобные, где параметру компонента присваивается массив, который определили выше по коду.
В итоге мы получили совсем не сложную форму настроек компонента.
С параметрами закончили и переходим к главному файлу компонента Битрикс component.php.
Разбор файла component.php
Файл занимает сто строк, так что я буду приводить его частями по ходу статьи. А сейчас расскажу о самых важных моментах.В начале происходит валидация массива $arParams и переопределение его значений. Например, есть конструкция, которая может поставить в тупик на первый взгляд.
Код:
if(!is_array($arParams["FIELD_CODE"]))
$arParams["FIELD_CODE"] = array();
foreach($arParams["FIELD_CODE"] as $key=>$val)
if(!$val)
unset($arParams["FIELD_CODE"][$key]);
Но если вы обратите внимание на настройки компонента (см. скриншот выше) то увидите, что под списком полей есть возможность вписать дополнительное значение. И оно уходит в массив $arParams["FIELD_CODE"]. Так вот в цикле проверяется это значение и если оно пустое то очищается.
Далее идет самый главный кусок кода - выборка данных из инфоблока с проверкой на наличие кеша Битрикс.
Код:
$additionalCacheID = false;
if($this->startResultCache($arParams['CACHE_TIME'], $additionalCacheID))
{
/SELECT
$arSelect = array_merge($arParams["FIELD_CODE"], array(
"ID",
"IBLOCK_ID",
));
foreach ($arParams["PROPERTY_CODE"] as $prop_name) {
$arSelect[]="PROPERTY_".$prop_name;
}
/WHERE
$arFilter = array (
"IBLOCK_ID" => $arParams["IBLOCK_ID"],
"IBLOCK_LID" => SITE_ID,
"ACTIVE" => "Y",
);
/ORDER BY
$arSort = array(
$arParams["SORT_BY1"]=>$arParams["SORT_ORDER1"]
);
if(!array_key_exists("ID", $arSort))
$arSort["ID"] = "DESC";
$arNavParams["nTopCount"] = $arParams['TOP_COUNT'];
/GETLIST
$rsElement = CIBlockElement::GetList($arSort, $arFilter, false, $arNavParams, $arSelect);
if (!$rsElement)
{
$this->abortResultCache();
}
Тут вам должно быть все понятно, кроме первых 2 строчек. Но если вдруг вы все же не знакомы ещё с API Bitrix и выборкой данных из инфоблока через GetList то рекомендую урок про разработку собственного компонента Битрикс Урок 9. Создание собственного компонента слайдера, а также посмотреть подробную информацию об устройстве инфоблока (из каких таблиц состоит, как настраивается и т.д.) Урок 5. Инфоблоки битрикс (структура и создание).
Я же остановлюсь на кешировании в Битрикс. За кеширование отвечает функция $this->startResultCache($arParams['CACHE_TIME'], $additionalCacheID).
Что она делает? Эта функция проверяет существует ли кеш для этого компонента, а также не прошло ли его время, т.е. не устарел ли он. Если возвращается значение ложь, то код ниже пропускается и данные берутся из кеша. Если же кеша нет или он не актуален то выполняется код внутри условия. В нашем случае выполняется запрос к инфоблоку.
Теперь ответим на вопрос: Как проверяется кеш? Кеш зависит от нескольких параметров:
- времени жизни
- текущего сайта ( SITE_ID),
- имени компонента,
- имени шаблона,
- параметров относящихся к кешированию из $arParams
Последними строчками проверяется получены-ли данные из инфоблока и если нет то сбрасывается кеш $this->abortResultCache(). В противном случае будет не оправданно расти кеш.
Подробнее о кешировании в Битрикс можно посмотреть в документации https:/dev.1c-bitrix.ru/api_help/main/reference/cbitrixcomponent/startresultcache.php.
Код:
$rsElement->SetUrlTemplates($arParams["DETAIL_URL"], "", $arParams["IBLOCK_URL"]);
while($obElement = $rsElement->GetNextElement())
{
$arItem = $obElement->GetFields();
$arResult["ITEMS"][] = $arItem;
}
$this->includeComponentTemplate();
}/cache
Завершается работа компонента созданием массива $arResult с подмассивом $arItems для удобства и функцией $this->includeComponentTemplate() вывод перенаправляется в шаблон компонента.
Простой макет шаблона template.php
Шаблон у нас будет совсем простой, а сложнее нам и не нужно.Код:
<div class="news-list">
<?foreach($arResult["ITEMS"] as $arItem):?>
<pre>
<? print_r($arItem);?>
</pre>
<?endforeach;?>
</div>
Все элементарно в цикле мы выводим данные массива.
Анализ SQL запросов, которые генерирует компонент
А теперь самое интересное, собственно для чего и задумывалась эта статья - анализ работы компонентов Битрикс. Сейчас мы включим встроенную отладку и проанализируем, как работает кеш Битрикса и сколько формируется запросов к базе данных.Сначала создадим тестовую страницу и разместим на ней наш компонент.
Данные я буду выбирать у себя на сайте из инфоблока с подписчиками. (Не забудьте подписаться сами!) Буду выбирать 5 записей, из полей только наименование (ID инфоблока и ID типа инфоблока у нас выбраны по умолчанию всегда). Свойства выбирать не буду, это важный момент.
Включаем отладку.
Смотрим результат после сброса кеша.
Видим, что выполнился всего один SQL запрос и создалось 2 Кб кеша. Отличный результат надо сказать!
Следующее обновления страницы берет данные уже из кеша.
Отлично, кеш работает!
Теперь усложним задачу и добавим свойства в компонент. Сначала добавим всего одно свойство.
И в результате у нас уже два запроса к базе, что вполне логично т.к. свойства хранятся в отдельных таблицах.
Теперь добавляем 5 свойств, все что есть в этом инфоблоке.
И что мы видим - у нас уже 6 запросов к базе (один к инфоблоку и 5 к свойствам). Т.е. на каждое свойство в инфоблоке выполняется по одному запросу к базе. Это нужно запомнить и не выбирать не нужные свойства из инфоблока при настройке компонентов.
А теперь, как обещал сравним с типовым компонентом bitrix:news.list (Список новостей). Будет ли наш компонент работать более оптимально или может зря я кучу времени потратил?
Разместим на странице компонент список новостей, шаблон создадим точно такой же как и в существующем компоненте. Настройки сделаем идентичные: 5 записей, одно поле наименование без свойств.
Смотрим результат.
Результат одного элемента не поместился на экран. Жесть, что тут скажешь. А мы ведь выбрали только одно поле Наименование. Откуда взялись остальные? А вот так работает типовой компонент, вытаскивая из базы кучу лишних данных. Он же универсальный. И главное обратите внимание - целых 4 запроса к базе данных! Наш же компонент давал только один запрос.
Кеширование конечно работает и в этом компоненте Результат bitrix:news.list: 0.0531 с; кеш: 10 КБ, уж не буду загромождать статью скриншотами.
Ну и последнее добавим свойства в выборку компонента. Добавим только одно свойство.
Смотрим результат выполнения.
И снова жесть - 10 запросов к базе. откуда они взялись??? Это же кошмар просто.
А что если увеличить выборку, я поставил 200 строк.
Результат 205 запросов.
А взялись они из особенностей работы типового компонента. Он выбирает всегда все свойство из базы даже когда выбрано только одно.
Обратите внимание на кусок коду из bitrix:news.list. Причем
Код:
**** $ipropValues = new Iblock\InheritedProperty\ElementValues($arItem["IBLOCK_ID"], $arItem["ID"]);$bGetProperty = count($arParams["PROPERTY_CODE"])>0;
if($bGetProperty)
$arSelect[]="PROPERTY_*";
$arItem["IPROPERTY_VALUES"] = $ipropValues->getValues();
Как видите если в $arParams есть хоть одно свойство, то выбираются сразу все. А ниже уже в цикле обхода выполняется запрос к заначениям свойств. Из-за этого мы и получаем 200 запросов при выборке 200 строк. В моем компоненте даже при выводе 200 строк остаются все те же 6 запросов, т.к. нет выборок в цикле это видано из кода.
И это ещё у меня хорошо мало свойств, а если вы работаете с каталогом товаров в интернет-магазине, где этих свойств куча. Что тогда произойдет? А тогда происходят вот такие вещи.
86 запросов чтобы вывести 4 просмотренных товара.
69 запросов, чтобы просто вывести список брендов.
Надеюсь замотивировал вас на то, чтобы разбираться и писать собственные компоненты. Убирать все лишнее и добавится минимальных запросов к базе.
Исходники моего универсального компонента.
От автора:
В этом уроке я рассмотрел наверное самую сложную тему из базового функционала инфоблоков Битрикс - кеширование. Собственно увиденное на последних 2 скринах меня и побудило разобраться, как же работают компоненты Битрикса. И я не пожалел, что разобрался. Без этих знаний такое огромное число запросов шокирует и даже начинаешь сомневаться в адекватности функции отладки. Но нет она не врет к сожалению. Надеюсь и вы почерпнете важные для себя знания и "черный ящик-битрикс" станет для вас немного меньше. В следующем уроке я планирую рассмотреть, как влияет массив $additionalCacheID на кеширование. Ответить на вопрос, как делать кастомный кеш в Битриксе. Обязательно подпишитесь на новые статьи и до скорого!
Комментарии
Да, писать свой компонент для простых выборок - это хорошая и тонкая оптимизация. Но вот у вас хороший пример на скрине с компонентом bitrix:catalog.products.viewed на вывод 4 товаров 86 запросов. Как с эти компонентом бороться? тут уже писать вручную сложновато, это товары а тут много чего нужно делать.
Не дошли у меня руки посмотреть как он работает. И что делать я не знаю, если нет возможности все переписать, то просто смирится. Самое обидное, что этот компонент не кешируется. Из рекомендаций могу сказать, что нужно в настройках компонента отключать все что не используется (свойства, цены) все это поражает лишние запросы.
Вот получить всего один запрос во в компоненте Битрикс можно только в самых простых случаях, а если например, обращаться к категориям то запросы резко возрастают. Почему так?
Да есть такая особенность. Дело в том, что категории (группы) хранятся в отдельной позиции и если они нужны хотя бы для того чтобы составить URL детальной страницы, и Битрикс при этом каждый раз делает отдельный запрос. Я скорее всего рассмотрю этот момент подробнее в следующей статье и доработаю свой компонент.