Пост

Оптимизация производительности сайта на Битрикс: пошаговое руководство

Подробное руководство по оптимизации производительности сайта на Битрикс. Узнайте, как ускорить загрузку страниц, настроить кеширование и улучшить индексацию.

Оптимизация производительности сайта на Битрикс: пошаговое руководство

В этой статье я поделюсь опытом оптимизации производительности сайта на Битрикс. Мы рассмотрим основные методы оптимизации, которые помогут ускорить работу вашего сайта и улучшить пользовательский опыт.

Важное примечание: Описанные в статье методы оптимизации требуют определенных условий на сервере:

  • Доступ к настройкам PHP и сервера
  • Возможность установки дополнительного ПО (Redis)
  • Достаточные ресурсы сервера

Если вы используете обычный хостинг с ограниченным доступом, некоторые методы (например, установка Redis или настройка PHP-FPM) могут быть недоступны. В этом случае рекомендую начать с базовых методов оптимизации:

  • Настройка кеширования компонентов
  • Оптимизация запросов к базе данных
  • Минификация статических файлов

1. Оптимизация кеширования компонентов

Одним из самых важных шагов в оптимизации является правильная настройка кеширования компонентов. Давайте рассмотрим пример на популярном компоненте новостей.

Настройка кеширования в компоненте новостей

  1. Откройте файл компонента local/components/bitrix/news.list/class.php:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?php
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();

class NewsListComponent extends CBitrixComponent
{
    public function executeComponent()
    {
        // Создаем экземпляр кеша
        $cache = Bitrix\Main\Data\Cache::createInstance();
        
        // Формируем ID кеша на основе параметров
        $cacheId = md5(serialize([
            $this->arParams["IBLOCK_ID"],
            $this->arParams["NEWS_COUNT"],
            $this->arParams["SORT_BY1"]
        ]));
        
        // Проверяем наличие кеша
        if ($cache->initCache($this->arParams["CACHE_TIME"], $cacheId, "/news/")) {
            // Получаем данные из кеша
            $vars = $cache->getVars();
            $this->arResult = $vars["arResult"];
        } else {
            // Начинаем кеширование
            $cache->startDataCache();
            
            // Получаем новости
            $this->arResult["ITEMS"] = $this->getNews();
            
            // Завершаем кеширование
            $cache->endDataCache([
                "arResult" => $this->arResult
            ]);
        }
        
        $this->includeComponentTemplate();
    }
    
    private function getNews()
    {
        // Ваш код получения новостей
        return [];
    }
}
  1. В шаблоне компонента local/components/bitrix/news.list/templates/.default/template.php:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?php
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();

// Получаем экземпляр кеша
$cache = Bitrix\Main\Data\Cache::createInstance();

// Формируем ID кеша для шаблона
$cacheId = md5(serialize([
    $arResult["ITEMS"],
    $arParams["CACHE_TIME"]
]));

// Проверяем наличие кеша
if ($cache->initCache($arParams["CACHE_TIME"], $cacheId, "/news/template/")) {
    // Получаем данные из кеша
    $vars = $cache->getVars();
    $templateData = $vars["templateData"];
} else {
    // Начинаем кеширование
    $cache->startDataCache();
    
    // Формируем данные для шаблона
    $templateData = [
        "title" => "Новости",
        "items" => $arResult["ITEMS"]
    ];
    
    // Завершаем кеширование
    $cache->endDataCache([
        "templateData" => $templateData
    ]);
}

// Выводим заголовок
echo '<h1>' . $templateData["title"] . '</h1>';

// Выводим новости
foreach ($templateData["items"] as $news) {
    echo '<div class="news-item">';
    echo '<h3>' . $news["TITLE"] . '</h3>';
    echo '<p>' . $news["PREVIEW_TEXT"] . '</p>';
    echo '</div>';
}

Преимущества такого подхода:

  1. Производительность: Кеширование значительно ускоряет работу компонента, так как данные не нужно получать из базы при каждом запросе.
  2. Нагрузка на сервер: Уменьшается нагрузка на сервер и базу данных.
  3. Гибкость: Можно настраивать время кеширования для разных типов данных.
  4. **Не кешируйте динамические данные, которые часто меняются.

Рекомендации по кешированию:

  1. Устанавливайте разумное время кеширования (например, 3600 секунд для новостей).
  2. Используйте уникальные ID кеша, включающие все важные параметры.
  3. Разделяйте кеширование данных и шаблона для большей гибкости.
  4. Не кешируйте динамические данные, которые часто меняются.

2. Настройка Redis для кеширования

Что такое Redis и зачем он нужен?

Redis (Remote Dictionary Server) - это быстрая база данных в памяти, которая идеально подходит для кеширования. Она работает намного быстрее, чем файловое кеширование, и позволяет хранить данные в структурированном виде.

Основные преимущества Redis:

  1. Работает в памяти - обеспечивает мгновенный доступ к данным
  2. Поддерживает различные типы данных (строки, списки, хеши, множества)
  3. Автоматическое сохранение данных на диск
  4. Встроенная поддержка репликации
  5. Атомарные операции

Установка и настройка Redis

1. Установка Redis на сервер

  1. Установите Redis на сервер:
1
2
3
4
5
# Установка Redis сервера
sudo apt-get install redis-server

# Проверка статуса Redis
sudo systemctl status redis-server
  1. Установите PHP-расширение для Redis:
1
2
3
4
5
# Установка PHP-расширения Redis
sudo apt-get install php-redis

# Перезапуск PHP-FPM после установки
sudo systemctl restart php-fpm

2. Настройка Redis

  1. Откройте файл конфигурации Redis:
1
sudo nano /etc/redis/redis.conf
  1. Настройте основные параметры:
1
2
3
4
5
6
7
8
9
10
11
12
13
# Максимальный размер памяти (например, 2GB)
maxmemory 2gb

# Политика очистки памяти
maxmemory-policy allkeys-lru

# Настройка сохранения данных
save 900 1    # Сохранять каждые 900 секунд, если изменился хотя бы 1 ключ
save 300 10   # Сохранять каждые 300 секунд, если изменилось хотя бы 10 ключей
save 60 10000 # Сохранять каждые 60 секунд, если изменилось хотя бы 10000 ключей

# Установка пароля (рекомендуется)
requirepass ваш_пароль

3. Настройка Битрикс для работы с Redis

  1. Откройте файл local/php_interface/init.php и добавьте следующий код:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
// Подключаем Redis
$redis = new Redis();

// Подключаемся к Redis-серверу
// 127.0.0.1 - адрес сервера Redis (localhost)
// 6379 - стандартный порт Redis
// 2.5 - таймаут подключения в секундах
$redis->connect('127.0.0.1', 6379, 2.5);

// Проверяем подключение
if (!$redis->ping()) {
    // Если подключение не удалось, логируем ошибку
    error_log('Ошибка подключения к Redis');
}

// Создаем экземпляр кеша Битрикс
$cache = Bitrix\Main\Data\Cache::createInstance();

// Устанавливаем Redis как хранилище кеша
$cache->setRedis($redis);
  1. В файле .settings.php добавьте настройки для использования Redis:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
return array(
    'cache' => array(
        'value' => array(
            'type' => 'redis',        // Тип кеширования - Redis
            'host' => '127.0.0.1',    // Адрес сервера Redis
            'port' => 6379,           // Порт Redis
            'password' => 'ваш_пароль', // Пароль Redis (если установлен)
            'database' => 0,          // Номер базы данных Redis
            'timeout' => 2.5,         // Таймаут подключения
        ),
        'readonly' => true,           // Файл только для чтения
    ),
);

Проверка работоспособности Redis

  1. Создайте файл local/php_interface/redis_test.php:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
// Подключаем Redis
$redis = new Redis();

try {
    // Подключаемся к Redis
    $redis->connect('127.0.0.1', 6379);
    
    // Проверяем подключение
    if ($redis->ping()) {
        echo "Redis подключен успешно\n";
        
        // Тестовый запись и чтение
        $redis->set('test_key', 'test_value');
        $value = $redis->get('test_key');
        echo "Тестовое значение: " . $value . "\n";
        
        // Проверка производительности
        $start = microtime(true);
        for ($i = 0; $i < 1000; $i++) {
            $redis->set('key_' . $i, 'value_' . $i);
        }
        $end = microtime(true);
        echo "Время записи 1000 ключей: " . ($end - $start) . " сек\n";
        
    } else {
        echo "Ошибка подключения к Redis\n";
    }
} catch (Exception $e) {
    echo "Ошибка: " . $e->getMessage() . "\n";
}

Мониторинг Redis

  1. Установите Redis Commander для визуального мониторинга:
1
npm install -g redis-commander
  1. Запустите Redis Commander:
1
redis-commander
  1. Откройте в браузере: http://localhost:8081

Рекомендации по использованию Redis

  1. Размер кеша:
    • Настройте maxmemory в зависимости от доступной памяти сервера
    • Используйте maxmemory-policy allkeys-lru для автоматической очистки старых данных
  2. Персистентность:
    • Включите сохранение данных на диск
    • Настройте частоту сохранения в зависимости от нагрузки
  3. Безопасность:
    • Установите пароль для Redis
    • Ограничьте доступ к Redis только с локального сервера
    • Используйте SSL для удаленного доступа
  4. Мониторинг:
    • Регулярно проверяйте использование памяти
    • Следите за количеством подключений
    • Мониторьте производительность

3. Переход на D7

D7 - это современный подход к разработке на Битрикс, который предоставляет более удобный и эффективный способ работы с базой данных. Давайте создадим компонент, который будет использовать D7 вместо устаревшего подхода.

  1. Создайте новый компонент в local/components/bitrix/catalog.section/:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?php
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();

// Подключаем необходимые классы Битрикс D7
use Bitrix\Main\Loader;           // Класс для подключения модулей
use Bitrix\Iblock\ElementTable;   // Класс для работы с таблицей элементов

/**
 * Компонент для вывода раздела каталога
 * Использует D7 для оптимизированных запросов к базе данных
 */
class CatalogSectionComponent extends CBitrixComponent
{
    /**
     * Основной метод компонента
     */
    public function executeComponent()
    {
        // Подключаем модуль инфоблоков
        Loader::includeModule('iblock');
        
        // Создаем объект запроса к таблице элементов
        $query = new \Bitrix\Main\Entity\Query(ElementTable::getEntity());
        
        // Указываем поля для выборки
        $query->setSelect([
            'ID',                    // ID элемента
            'NAME',                  // Название элемента
            'IBLOCK_ID',            // ID инфоблока
            'PROPERTY_PRICE',       // Свойство "Цена"
            'PROPERTY_STOCK'        // Свойство "Наличие"
        ]);
        
        // Устанавливаем фильтр
        $query->setFilter([
            'IBLOCK_ID' => $this->arParams['IBLOCK_ID'],  // ID инфоблока из параметров
            'ACTIVE' => 'Y'                               // Только активные элементы
        ]);
        
        // Сортировка по полю SORT
        $query->setOrder(['SORT' => 'ASC']);
        
        // Выполняем запрос
        $result = $query->exec();
        
        // Формируем массив результатов
        while ($row = $result->fetch()) {
            $this->arResult['ITEMS'][] = $row;
        }
        
        // Подключаем шаблон компонента
        $this->includeComponentTemplate();
    }
}
?>
  1. Создайте файл .parameters.php для настройки параметров компонента:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();

$arComponentParameters = array(
    "GROUPS" => array(),
    "PARAMETERS" => array(
        "IBLOCK_ID" => array(
            "NAME" => "ID инфоблока",
            "TYPE" => "STRING",
            "DEFAULT" => "",
            "REFRESH" => "Y",
        ),
        "CACHE_TIME" => array(
            "NAME" => "Время кеширования (сек)",
            "TYPE" => "STRING",
            "DEFAULT" => "3600",
        ),
    ),
);
?>
  1. Создайте файл .description.php с описанием компонента:
1
2
3
4
5
6
7
8
<?php
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();

$arComponentDescription = array(
    "NAME" => "Раздел каталога (D7)",
    "DESCRIPTION" => "Выводит раздел каталога с использованием D7",
);
?>

Как использовать компонент

  1. В шаблоне сайта или на странице добавьте вызов компонента:
1
2
3
4
5
6
7
8
9
10
<?php
$APPLICATION->IncludeComponent(
    "bitrix:catalog.section",
    "",
    array(
        "IBLOCK_ID" => 5,        // ID вашего инфоблока каталога
        "CACHE_TIME" => 3600,    // Время кеширования в секундах
    )
);
?>

Преимущества использования D7:

  1. Оптимизация запросов: D7 автоматически оптимизирует запросы к базе данных, объединяя их в пакеты
  2. Типизация: Строгая типизация данных помогает избежать ошибок
  3. Кеширование: Встроенная поддержка кеширования на уровне запросов
  4. Безопасность: Автоматическая защита от SQL-инъекций
  5. Производительность: Меньше запросов к базе данных

Пример старого подхода (для сравнения):

1
2
3
4
5
6
7
8
9
10
11
// Старый вариант (много запросов)
$res = CIBlockElement::GetList(
    array("SORT" => "ASC"),
    array("IBLOCK_ID" => 5),
    false,
    false,
    array("ID", "NAME", "PROPERTY_PRICE")
);
while ($row = $res->GetNext()) {
    $arResult['ITEMS'][] = $row;
}

Как видите, новый подход с D7 более структурированный и эффективный. Он позволяет:

  • Легко добавлять новые поля в выборку
  • Использовать сложные фильтры
  • Оптимизировать запросы
  • Кешировать результаты

4. Оптимизация фронтенда

Настройка загрузки скриптов и стилей

  1. В файле header.php добавьте:
1
2
3
4
5
6
7
<?php
// Подключаем минифицированные CSS
\Bitrix\Main\Page\Asset::getInstance()->addCss(SITE_TEMPLATE_PATH.'/css/style.min.css');

// Отложенная загрузка скриптов
\Bitrix\Main\Page\Asset::getInstance()->addJs(SITE_TEMPLATE_PATH.'/js/script.min.js');
?>
  1. В файле footer.php добавьте:
1
2
3
4
<?php
// Отложенная загрузка скриптов
\Bitrix\Main\Page\Asset::getInstance()->addString('<script defer src="'.SITE_TEMPLATE_PATH.'/js/lazy.js"></script>');
?>

Настройка минификации

Установите необходимые npm-пакеты:

1
2
# Устанавливаем инструменты для минификации CSS и JavaScript
npm install -g clean-css-cli uglify-js

Создайте скрипт tools/minify.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const { exec } = require('child_process');

// Пути к файлам относительно корня проекта
const CSS_SOURCE = 'assets/css/style.css';    // Исходный CSS файл
const CSS_MIN = 'assets/css/style.min.css';   // Минифицированный CSS файл
const JS_SOURCE = 'assets/js/script.js';      // Исходный JavaScript файл
const JS_MIN = 'assets/js/script.min.js';     // Минифицированный JavaScript файл

// Минификация CSS с дополнительными параметрами
exec(`cleancss -o ${CSS_MIN} ${CSS_SOURCE} --compatibility "ie8" --level 2`, (error) => {
    if (error) {
        console.error('Ошибка при минификации CSS:', error);
    } else {
        console.log('CSS успешно минифицирован');
    }
});

// Минификация JavaScript с дополнительными параметрами
exec(`uglifyjs ${JS_SOURCE} -o ${JS_MIN} --compress --mangle --source-map`, (error) => {
    if (error) {
        console.error('Ошибка при минификации JavaScript:', error);
    } else {
        console.log('JavaScript успешно минифицирован');
    }
});

5. Оптимизация изображений

Настройка автоматической оптимизации

  1. Создайте файл local/php_interface/events.php:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php
// Регистрируем обработчик события добавления элемента в инфоблок
AddEventHandler("iblock", "OnBeforeIBlockElementAdd", "OnBeforeIBlockElementAddHandler");

/**
 * Обработчик события добавления элемента в инфоблок
 * @param array &$arFields - Массив полей элемента
 */
function OnBeforeIBlockElementAddHandler(&$arFields) {
    // Проверяем, что это наш инфоблок каталога (ID = 5)
    if ($arFields["IBLOCK_ID"] == 5) {
        // Оптимизация превью-изображения
        if (!empty($arFields["PREVIEW_PICTURE"])) {
            // Создаем превью размером 800x600 пикселей
            // BX_RESIZE_IMAGE_PROPORTIONAL - сохраняем пропорции
            // true - создаем физический файл
            $arFields["PREVIEW_PICTURE"] = CFile::ResizeImageGet(
                $arFields["PREVIEW_PICTURE"],
                array(
                    "width" => 800,    // Максимальная ширина
                    "height" => 600,   // Максимальная высота
                    "quality" => 85    // Качество JPEG (0-100)
                ),
                BX_RESIZE_IMAGE_PROPORTIONAL,
                true
            );
        }
        
        // Оптимизация детального изображения
        if (!empty($arFields["DETAIL_PICTURE"])) {
            // Создаем детальное изображение размером 1200x800 пикселей
            $arFields["DETAIL_PICTURE"] = CFile::ResizeImageGet(
                $arFields["DETAIL_PICTURE"],
                array(
                    "width" => 1200,   // Максимальная ширина
                    "height" => 800,   // Максимальная высота
                    "quality" => 90    // Качество JPEG (0-100)
                ),
                BX_RESIZE_IMAGE_PROPORTIONAL,
                true
            );
        }
    }
}
?>

6. Настройка кеширования статических файлов

Настройка Apache

  1. Откройте файл .htaccess в корне сайта и добавьте:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Кеширование статических файлов
<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType image/jpg "access plus 1 year"
    ExpiresByType image/jpeg "access plus 1 year"
    ExpiresByType image/gif "access plus 1 year"
    ExpiresByType image/png "access plus 1 year"
    ExpiresByType text/css "access plus 1 month"
    ExpiresByType application/javascript "access plus 1 month"
</IfModule>

# Включение сжатия
<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/plain
    AddOutputFilterByType DEFLATE text/html
    AddOutputFilterByType DEFLATE text/xml
    AddOutputFilterByType DEFLATE text/css
    AddOutputFilterByType DEFLATE application/xml
    AddOutputFilterByType DEFLATE application/xhtml+xml
    AddOutputFilterByType DEFLATE application/rss+xml
    AddOutputFilterByType DEFLATE application/javascript
    AddOutputFilterByType DEFLATE application/x-javascript
</IfModule>

Настройка Nginx

Если вы используете Nginx, добавьте в конфигурацию:

1
2
3
4
5
6
7
8
9
# Кеширование статических файлов
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
    expires 1y;
    add_header Cache-Control "public, no-transform";
}

# Включение сжатия
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

Заключение

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

  • Время загрузки страниц уменьшилось с 3-4 секунд до 0.5-1 секунды
  • Нагрузка на сервер снизилась на 40%
  • Улучшился пользовательский опыт
  • Сайт стал работать стабильнее

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

Полезные ссылки:

Авторский пост защищен лицензией CC BY 4.0 .

© Oleg Dobrynin. Некоторые права защищены.