Каталог в час пик открывается всё дольше, панель хостинга показывает загрузку процессора базой под потолок, а после ночного обмена с 1С сайт вообще «залипает» на минуты. Разработчик первым делом смотрит на кэш и фронтенд — и часто зря: узкое место сидит глубже, в самой базе данных. Медленные запросы без индексов, распухшие таблицы свойств и заказов, дефолтный конфиг MySQL, в котором буфер InnoDB меньше базы, — вот что реально держит магазин за горло под нагрузкой. И лечится это не «добавить памяти», а прицельной работой с БД.
Ниже — системный разбор оптимизации MySQL и MariaDB для 1С-Битрикс: как найти медленные запросы (slow query log, монитор производительности, EXPLAIN), когда и какие ставить индексы (в том числе свои — на свойства инфоблоков), как настроить сервер (innodb_buffer_pool_size, временные таблицы, соединения), что делать с большими таблицами и как их обслуживать, чистить и архивировать. Это выжимка из наших работ по ускорению интернет-магазинов на 1С-Битрикс — без тюнинга вслепую и мифических «волшебных» значений конфига.
Коротко
- Сначала измерение: slow query log, «Монитор производительности» Битрикса и отладочная панель показывают, какие запросы тормозят.
- Каждый тяжёлый запрос разбирают через EXPLAIN; чаще всего лечится индексом — иногда своим, на колонку свойства или пользовательского поля.
- Индексы не бесплатны: лишние замедляют запись и обмен с 1С, поэтому индексируют то, по чему реально фильтруют и сортируют.
- Движок для Битрикса — InnoDB; ключевая настройка памяти — innodb_buffer_pool_size под размер «горячих» данных.
- Большие таблицы (свойства, заказы, логи, статистика, поиск) обслуживают, чистят и архивируют; кэш Битрикса и настройки БД дополняют друг друга, а не заменяют.
Как база данных влияет на скорость магазина
Каждая страница магазина на 1С-Битрикс — это набор SQL-запросов к базе. Список каталога выбирает элементы инфоблока и их свойства, фильтр обращается к фасетному индексу, карточка тянет предложения и остатки, корзина и заказ пишут и читают свои таблицы. Пока данных немного и запросы попадают в индексы, база отвечает за миллисекунды и остаётся невидимой. Но стоит каталогу вырасти, а запросу — начать сканировать таблицу целиком, как база из «невидимого помощника» превращается в главный тормоз страницы.
Важно понимать, что нагрузка на базу растёт нелинейно к трафику. Один тяжёлый запрос без индекса на пустом сайте отработает быстро, потому что данных мало. Тот же запрос на большом каталоге и под сотней параллельных посетителей выстраивается в очередь: пока один сканирует таблицу, остальные ждут, процессор базы упирается в потолок, и время ответа растёт лавинообразно. Именно поэтому «на тесте было быстро, а на проде встало» — почти всегда история про базу и объём данных.
Оптимизация БД работает по двум направлениям сразу. Первое — сократить число обращений к базе: этим занимается кэш Битрикса, который отдаёт готовый результат, не доходя до SQL. Второе — ускорить те запросы, которые всё же доходят: первая сборка страницы, персональные некэшируемые данные, обмен с 1С. Кэш убирает массу повторной работы, но не отменяет необходимость держать базу в порядке — иначе каждая «холодная» сборка и каждый обмен будут больно бить по скорости.
База и «зелёные» метрики
Скорость базы напрямую влияет на время генерации страницы на сервере, а значит на TTFB — время до первого байта, фундамент под метриками Core Web Vitals. Медленный запрос удлиняет генерацию, TTFB растёт, LCP проседает. Поэтому работа с БД — не отдельная «серверная» задача в вакууме, а часть той же цепочки, что кэш и фронтенд. Как метрики складываются в общую картину скорости магазина, мы разбираем в материале про Core Web Vitals для интернет-магазина.
Где БД становится узким местом: общая карта
Прежде чем чинить, полезно уложить в голове карту типичных узких мест базы в магазине на Битриксе. Они повторяются из проекта в проект, и знание карты сразу подсказывает, где искать.
- Запросы без индексов. Фильтр или сортировка по колонке, на которой нет индекса, заставляет базу сканировать таблицу целиком. Самая частая и самая устранимая причина.
- Распухшие большие таблицы. Свойства элементов, заказы, история заказов, логи и статистика годами растут, а обслуживание не проводится. Тяжёлые запросы по ним замедляются.
- Дефолтный конфиг сервера. Буфер InnoDB меньше рабочего набора данных, маленькие временные таблицы, узкий лимит соединений — база упирается в память и диск там, где могла бы работать из ОЗУ.
- Эффект N+1. Список из ста товаров тянет свойства сотней отдельных запросов вместо одного. Формально каждый запрос быстрый, но их количество душит базу.
- Обмен с 1С в час пик. Массовая запись конкурирует с чтением витрины: посетители ждут, пока обмен пишет тысячи строк.
Дальше в статье мы пройдём по этой карте сверху вниз: сначала научимся находить конкретные медленные запросы, потом разберём индексы и настройки сервера, затем большие таблицы, их обслуживание и архивацию, и в конце — highload-инструменты вроде партиционирования и репликации, которые нужны не всем.
Поиск медленных запросов: инструменты
Оптимизация начинается с ответа на вопрос «что именно тормозит». Отвечают на него три инструмента, и лучше пользоваться всеми тремя — они смотрят на проблему с разных сторон.
| Инструмент | Что показывает | Когда применять |
|---|---|---|
| Slow query log MySQL/MariaDB | Все запросы дольше порога по всему сайту | Найти системно тяжёлые запросы за период |
| Монитор производительности Битрикса | Тяжёлые запросы, привязанные к страницам | Связать медленный SQL с конкретной страницей |
| Отладка и панель Битрикса | Число и время SQL на одной странице | Точечная диагностика конкретного URL |
| EXPLAIN | План выполнения одного запроса | Понять, почему запрос медленный, и как чинить |
Логика связки простая. Slow query log и монитор дают список подозреваемых — какие запросы и на каких страницах тяжелее всего. Отладочная панель помогает воспроизвести проблему на конкретном URL и увидеть общее число запросов (часто оказывается, что их не десятки, а сотни — привет, N+1). А EXPLAIN отвечает на вопрос почему: сканирует ли запрос таблицу целиком, использует ли индекс, создаёт ли временную таблицу. Дальше — точечное лечение.
Slow query log: включение и анализ
Журнал медленных запросов — штатная возможность MySQL и MariaDB: сервер сам пишет в файл каждый запрос, который выполнялся дольше заданного порога. Это лучший способ увидеть системную картину «что тормозит по всему сайту за сутки», а не только на той странице, которую вы открыли. Включается в конфиге сервера (обычно секция [mysqld] в my.cnf / my.ini).
[mysqld]
# Включить журнал медленных запросов
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
# Порог: писать запросы дольше 1 секунды
long_query_time = 1
# Также логировать запросы, не использующие индексы
log_queries_not_using_indexes = 1
# (опционально) ограничить объём таких «безиндексных» записей в минуту
log_throttle_queries_not_using_indexes = 60
После включения журнал быстро накапливает записи, и читать его вручную неудобно. Для сводки применяют утилиты, которые группируют похожие запросы и показывают суммарное время: штатную mysqldumpslow или более подробную pt-query-digest из Percona Toolkit. Они выводят топ запросов по суммарной нагрузке — именно с верхушки этого списка и начинают оптимизацию, потому что она даёт максимальный эффект.
# Топ медленных запросов по среднему времени
mysqldumpslow -s t /var/log/mysql/slow.log | head -n 40
# Подробный разбор с группировкой (Percona Toolkit)
pt-query-digest /var/log/mysql/slow.log > slow-report.txt
long_query_time спрячет «средне-медленные» запросы, которые по отдельности терпимы, но выполняются тысячи раз и в сумме дают основную нагрузку. Слишком маленький — раздует лог и создаст лишнюю запись на диск. Разумно начать с порога около секунды, найти самые тяжёлые запросы, а потом при необходимости опускать порог, чтобы выловить «массовые» частые запросы.
Монитор производительности 1С-Битрикс
У Битрикса есть собственный инструмент, который смотрит на базу глазами приложения, — модуль «Монитор производительности» (perfmon). Его ценность в том, что он привязывает тяжёлые запросы к страницам сайта: вы видите не абстрактный SQL из лога, а «этот запрос выполняется на странице раздела каталога и занимает столько-то». Это резко сокращает путь от симптома к причине.
Монитор ведёт свою таблицу собранных запросов и умеет показывать самые тяжёлые, самые частые, запросы без индексов. Там же в разделе производительности есть общий тест конфигурации и «Панель производительности», которая оценивает скорость сервера, PHP и базы. Для разработчика это удобная отправная точка: включил сбор на период реальной нагрузки, посмотрел топ, пошёл разбирать конкретные запросы через EXPLAIN.
Помимо монитора, для точечной диагностики одной страницы включают режим отладки Битрикса — отладочная панель показывает число выполненных SQL-запросов и суммарное время работы базы на этом URL. Если на одной странице каталога база выполняет сотни запросов, это почти наверняка либо выключенный (или несрабатывающий) кэш, либо эффект N+1 в выборке свойств. Как навести порядок с кэшем компонентов и теговой инвалидацией, подробно разобрано в материале про кэширование в 1С-Битрикс — база и кэш всегда чинятся в связке.
EXPLAIN: читаем план запроса
Когда конкретный тяжёлый запрос найден, следующий шаг — понять, почему он медленный. Это делает команда EXPLAIN: она показывает план выполнения — как база собирается доставать данные, какие индексы использует и сколько строк рассчитывает просмотреть. Достаточно приписать EXPLAIN перед запросом.
EXPLAIN
SELECT E.ID, E.NAME
FROM b_iblock_element E
WHERE E.IBLOCK_ID = 21
AND E.ACTIVE = 'Y'
AND E.XML_ID = 'ABC-12345';
В выводе смотрят на несколько ключевых колонок. type показывает способ доступа: ALL — это полный скан таблицы (плохо на больших данных), ref/eq_ref/const — доступ по индексу (хорошо). key — какой индекс реально выбран (если NULL — индекс не используется). rows — оценка числа просматриваемых строк (чем меньше, тем лучше). В колонке Extra тревожные сигналы — Using filesort (сортировка без индекса) и Using temporary (создание временной таблицы).
| Колонка EXPLAIN | Хороший знак | Тревожный знак |
|---|---|---|
| type | const, eq_ref, ref, range | ALL (полный скан таблицы) |
| key | используется подходящий индекс | NULL — индекс не выбран |
| rows | небольшое число строк | число, близкое к размеру таблицы |
| Extra | Using index (покрывающий индекс) | Using filesort, Using temporary |
Практический алгоритм: увидели type = ALL и большой rows на фильтрующей колонке — почти наверняка не хватает индекса. Увидели Using filesort на сортировке, которая идёт часто, — подумайте об индексе, покрывающем сортировку. Увидели Using temporary на группировке — возможно, поможет индекс или пересмотр запроса. В новых версиях есть ещё EXPLAIN ANALYZE, который реально выполняет запрос и показывает фактическое время по шагам, — это уже точная диагностика, а не только оценка.
Индексы: скан таблицы против индекса
Индекс — главный инструмент ускорения запросов, поэтому разберём его суть на пальцах. Без индекса, чтобы найти строки по условию, база вынуждена прочитать таблицу целиком и проверить каждую строку — это полный скан. На тысяче строк он незаметен, на миллионах — это секунды и загрузка диска. Индекс — это заранее построенная и отсортированная структура (в InnoDB — B-дерево), по которой база находит нужные строки почти мгновенно, не читая остальные, как оглавление книги вместо перелистывания всех страниц.
Разница нагляднее всего на схеме: слева запрос без индекса вынужден пройти всю таблицу, справа — прыгает по индексу сразу к нужным строкам.
В Битриксе штатные таблицы уже покрыты индексами под типовые запросы ядра: по IBLOCK_ID, ACTIVE, XML_ID, привязкам к разделам и так далее. Проблемы начинаются на нетиповых выборках — по вашим свойствам, пользовательским полям, кастомным фильтрам и сортировкам, для которых готового индекса нет. Именно там и нужны свои индексы, о которых ниже.
Когда индекс помогает, а когда бесполезен
Индекс — не универсальное «сделать быстрее». Он ускоряет строго определённые операции и в некоторых случаях не даёт ничего или даже мешает. Понимание этого экономит время и бережёт базу от бесполезных индексов.
Индекс помогает, когда по колонке идёт выборочный поиск, диапазонный фильтр или сортировка, и колонка достаточно «различающая». Классика — фильтр по конкретному значению свойства, сортировка по цене или дате, поиск по артикулу. Особенно эффективен составной индекс, покрывающий сразу условие и сортировку: база и отбирает, и уже отсортированно отдаёт, без filesort.
Индекс бесполезен или почти, когда:
- Колонка малоразличающая. Если у поля два-три значения (например, флаг «активность»), индекс по нему сам по себе даёт мало — база всё равно возьмёт огромную долю строк. Такое поле работает как часть составного индекса, а не отдельно.
- Условие с функцией или «не начинается с». Поиск вида
LIKE '%текст%'или обёртка колонки в функцию мешают базе использовать обычный индекс — тут нужен другой подход (полнотекстовый индекс, вынесенное вычисляемое поле). - Таблица крошечная. На нескольких сотнях строк скан быстрее прыжков по индексу, и оптимизатор сам выберет скан.
- Порядок колонок в составном индексе не тот. Составной индекс работает «слева направо»: индекс по (A, B) помогает фильтру по A и по A+B, но не по одному B.
Поэтому индексы не «навешивают на всё подряд», а ставят прицельно — под конкретные медленные запросы, подтверждённые через EXPLAIN. Сначала находим тяжёлый запрос, смотрим план, добавляем индекс ровно под это условие, перепроверяем план. Это дисциплина, а не угадайка.
Свои индексы на пользовательские поля и свойства
Самая частая практическая задача — доиндексировать то, чего Битрикс не проиндексировал сам: значения свойств инфоблоков и пользовательские поля. Значения свойств для инфоблоков «версии 2» (рекомендованная схема хранения) лежат в таблицах вида b_iblock_element_prop_s{IBLOCK_ID} для одиночных свойств и b_iblock_element_prop_m{IBLOCK_ID} для множественных. Каждое свойство — это колонка PROPERTY_{ID}. Когда по такому свойству идёт частый фильтр или сортировка, а индекса нет, база сканирует таблицу целиком.
Лечится добавлением своего индекса. Сначала — убедиться через EXPLAIN, что запрос действительно упирается в скан этой колонки, потом создать индекс:
-- Индекс на одиночное свойство (напр. бренд) инфоблока с ID = 21
CREATE INDEX ix_prop_brand
ON b_iblock_element_prop_s21 (PROPERTY_137);
-- Составной индекс: фильтр по свойству + сортировка по другому свойству
CREATE INDEX ix_prop_brand_price
ON b_iblock_element_prop_s21 (PROPERTY_137, PROPERTY_145);
-- Индекс на колонку пользовательского поля (highload-блок / UF)
CREATE INDEX ix_uf_region
ON b_hlbd_region (UF_REGION_CODE);
Несколько практических правил. Порядок колонок в составном индексе выбирают под реальный запрос: сначала колонка равенства/фильтра, потом колонка сортировки. Для highload-блоков (большие справочники характеристик, регионы, справочники совместимости) индексы на пользовательские поля так же критичны, как на обычных таблицах, — по ним идут те же фильтры. При этом штатный интерфейс highload-блоков и умного фильтра умеет создавать часть индексов сам; проверьте, что нужные уже есть, прежде чем добавлять руками.
Как свойства и характеристики участвуют в фасетной фильтрации и почему грамотные индексы там особенно важны, мы разбираем в связке с умным фильтром и торговыми предложениями. Если проект строится вокруг больших каталогов и высоких нагрузок, схему хранения и индексы стоит проектировать сразу под highload-проект на Битриксе, где база рассчитывается с запасом по объёму и параллелизму.
Не переусердствовать: цена лишних индексов
Раз индексы так помогают чтению, почему не проиндексировать всё? Потому что у индексов есть обратная сторона, и на активно пишущем магазине она заметна. Каждый индекс нужно поддерживать в актуальном состоянии: при вставке, обновлении и удалении строки база обновляет не только саму таблицу, но и все её индексы. Чем больше индексов на таблице, тем дороже каждая запись.
Для магазина это прямо бьёт по двум сценариям. Первый — обмен с 1С, который массово пишет в таблицы свойств, товаров, остатков и цен: лишние индексы делают каждую из тысяч операций записи тяжелее. Второй — оформление заказов в пик, когда идёт активная запись в таблицы заказа и корзины. Плюс индексы занимают место на диске и в буфере InnoDB, вытесняя оттуда полезные данные.
- Не дублируйте индексы. Если есть составной индекс по (A, B), отдельный индекс по A обычно избыточен — префикс покрывается составным.
- Убирайте неиспользуемые. Индексы, оставшиеся от старых фильтров, только грузят запись. Их находят по статистике использования и удаляют.
- Индексируйте по факту. Добавляйте индекс под подтверждённый медленный запрос, а не про запас.
Здоровый баланс — минимально достаточный набор индексов, каждый из которых работает на реальный запрос. Периодически стоит ревизовать индексы: какие используются, какие нет, нет ли дублей. Это часть регулярного обслуживания базы, а не разовая настройка.
InnoDB против MyISAM: почему Битрикс — InnoDB
Движок хранения определяет, как таблица работает с блокировками, транзакциями и восстановлением. Для 1С-Битрикс ответ однозначный — InnoDB, и понимать почему полезно, чтобы не оставить в базе случайных MyISAM-таблиц, которые станут узким местом.
| Критерий | InnoDB (нужен Битриксу) | MyISAM (устаревший) |
|---|---|---|
| Блокировки | На уровне строки | На уровне всей таблицы |
| Транзакции | Есть (ACID) | Нет |
| Запись + чтение | Параллельны, не мешают | Запись блокирует чтение таблицы |
| Восстановление после сбоя | Журнал, авто-восстановление | Ручная проверка, риск потери |
| Внешние ключи | Поддерживаются | Нет |
Ключевое отличие для магазина — блокировки. MyISAM блокирует таблицу целиком на время записи: пока обмен с 1С обновляет строку в таблице товаров, все посетители, которые пытаются читать эту таблицу, ждут. На активном магазине с постоянным обменом и заказами это превращается в очередь и «залипание». InnoDB блокирует только затрагиваемые строки, поэтому чтение витрины и запись обмена идут параллельно. Добавьте сюда транзакции (важны для корректности заказов и оплат) и надёжное восстановление после сбоя — и выбор очевиден.
Современные дистрибутивы «Битрикс: Управление сайтом» по умолчанию используют InnoDB. Но в старых базах, переезжавших через много версий, иногда остаются отдельные таблицы MyISAM. Их находят и переводят на InnoDB:
-- Найти таблицы, которые ещё на MyISAM
SELECT TABLE_NAME, ENGINE
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE()
AND ENGINE = 'MyISAM';
-- Перевести конкретную таблицу на InnoDB
ALTER TABLE b_example_table ENGINE = InnoDB;
ALTER TABLE ... ENGINE = InnoDB перестраивает таблицу целиком и на больших объёмах может надолго её заблокировать. На активном проде такие операции делают в окно обслуживания или через онлайн-инструменты (например, pt-online-schema-change), которые перестраивают таблицу без длительной блокировки.
Настройки MySQL/MariaDB под Битрикс
Дефолтный конфиг MySQL/MariaDB рассчитан на «работает у всех», а не на «быстро под нагрузкой». Несколько ключевых параметров определяют, будет ли база работать из оперативной памяти или упираться в диск. Ниже — карта самых важных настроек; конкретные значения подбирают под объём базы и ресурсы сервера, а не берут универсальными.
| Параметр | За что отвечает | На что влияет |
|---|---|---|
innodb_buffer_pool_size | Память под данные и индексы InnoDB | Главный параметр: работа из ОЗУ вместо диска |
innodb_log_file_size | Размер журнала транзакций | Пропускная способность записи, обмен с 1С |
innodb_flush_log_at_trx_commit | Стратегия сброса журнала на диск | Баланс скорости записи и надёжности |
tmp_table_size / max_heap_table_size | Размер временных таблиц в памяти | Тяжёлые сортировки и группировки без диска |
max_connections | Лимит одновременных соединений | Устойчивость в пик, отказ «too many connections» |
thread_cache_size | Переиспользование потоков соединений | Накладные расходы на новые подключения |
join_buffer_size / sort_buffer_size | Буферы под соединения и сортировку | Скорость запросов без индекса под сортировку |
Отдельно про кэш запросов (query cache): в старых версиях MySQL он существовал, но на активно пишущем магазине часто вредил (сбрасывался при каждой записи в таблицу), а в MySQL 8 его убрали совсем. MariaDB кэш запросов сохраняет, но на нагруженном магазине его обычно не включают. Полагаться на него как на «ускоритель» не стоит — реальную работу по кэшированию делает кэш Битрикса и буфер InnoDB.
[mysqld]
# --- InnoDB ---
innodb_buffer_pool_size = 4G # под размер «горячих» данных, с запасом ОС
innodb_log_file_size = 512M # крупнее — легче пиковой записи (обмен)
innodb_flush_log_at_trx_commit = 2 # компромисс скорость/надёжность
innodb_file_per_table = 1 # каждая таблица — свой файл
# --- Временные таблицы ---
tmp_table_size = 128M
max_heap_table_size = 128M
# --- Соединения ---
max_connections = 200
thread_cache_size = 32
# --- Буферы на сессию (умеренно, это память НА каждое соединение) ---
join_buffer_size = 4M
sort_buffer_size = 4M
join_buffer_size, sort_buffer_size и подобные выделяются на каждое соединение. Задрав их до сотен мегабайт при большом max_connections, легко исчерпать всю память сервера в пик. Их держат умеренными, а память вкладывают в общий буфер InnoDB, который один на всех.
innodb_buffer_pool_size и буфер InnoDB
Если из всего конфига выбрать один самый важный параметр, это innodb_buffer_pool_size. Буфер InnoDB — это область оперативной памяти, в которой движок держит данные таблиц и индексы. Когда запрос обращается к данным, которые уже в буфере, он читает их из памяти — быстро. Когда данных в буфере нет, движок идёт за ними на диск — медленно. Цель настройки — чтобы «горячий» рабочий набор (данные и индексы, к которым сайт обращается постоянно) помещался в буфер и читался из ОЗУ.
Схема ниже показывает, как это работает вместе с кэшем приложения: запрос сначала может быть перехвачен кэшем Битрикса и вообще не дойти до базы; если дошёл — данные ищутся в буфере InnoDB, и только при промахе идёт обращение к диску.
Как понять, что буфер мал? Косвенные признаки — база под нагрузкой активно читает с диска, растёт нагрузка на диск, а не только на процессор; статус InnoDB показывает низкую долю попаданий в буфер. Если буфер меньше рабочего набора, база постоянно «выгружает» одни данные, чтобы загрузить другие, и толком не пользуется памятью. Решение — увеличить буфер (в пределах доступной памяти, оставив запас ОС, веб-серверу и PHP) или, если памяти физически мало, переезжать на сервер помоложе. Конкретный размер зависит от объёма базы и того, что ещё живёт на сервере, поэтому его подбирают под инфраструктуру.
Связка с железом здесь прямая: сколько памяти можно отдать под буфер, определяет хостинг. Как выбрать конфигурацию сервера и распределить ресурсы между базой, PHP и кэшем, мы разбираем в материале про хостинг и инфраструктуру для Битрикса и BitrixVM.
Временные таблицы, сортировки и соединения
Кроме буфера InnoDB, на скорость влияют настройки, связанные с обработкой самих запросов. Когда запрос сортирует или группирует большой объём данных, база создаёт временную таблицу. Если она помещается в память (tmp_table_size / max_heap_table_size), операция быстрая; если превышает лимит — временная таблица «падает» на диск, и запрос резко замедляется. В EXPLAIN это видно как Using temporary, а на диске — как временные файлы под тяжёлые группировки.
Лечится это двумя путями. Идеально — убрать саму необходимость временной таблицы и сортировки: подходящий индекс отдаёт данные уже отсортированными, без filesort и temporary. Когда это невозможно (сложная аналитика, отчёты), помогает поднятие лимитов временных таблиц в памяти — но умеренно, помня, что часть буферов выделяется на каждое соединение.
Отдельная тема — соединения. Параметр max_connections задаёт, сколько клиентов могут одновременно работать с базой. В пик (распродажа, наплыв трафика, параллельный обмен) лимит может исчерпаться, и появляется ошибка «too many connections» — новые запросы просто не обслуживаются. Но и задирать лимит бесконтрольно нельзя: каждое соединение потребляет память, и слишком большой лимит при больших сессионных буферах способен исчерпать ОЗУ. Здоровый путь — разумный лимит соединений плюс кэш Битрикса, который уменьшает само число обращений к базе, и вынесение сессий из базы в память.
Большие таблицы Битрикса и их обслуживание
В магазине на Битриксе есть таблицы, которые растут особенно быстро и первыми становятся тяжёлыми. Их полезно знать «в лицо» — чтобы понимать, где искать распухание и что обслуживать в первую очередь.
| Таблица | Что хранит | Почему растёт / что делать |
|---|---|---|
b_iblock_element_prop_s* / _m* | Значения свойств элементов | Строка на каждое значение каждого товара — индексы, кэш, обслуживание |
b_iblock_element | Элементы инфоблоков (товары) | Большой каталог — следить за индексами под фильтры |
b_sale_order, b_sale_basket | Заказы и корзины | Копятся годами — архивация старых, чистка брошенных корзин |
b_sale_order_history | История изменений заказов | Растёт быстрее самих заказов — архивировать/чистить |
b_search_content* | Поисковый индекс | Дублирует контент — переиндексация, контроль объёма |
b_stat_* | Данные модуля статистики | Огромный объём хитов — чистка старых периодов |
b_event_log | Журнал событий | Логи безопасности и ошибок — ограничить срок хранения |
b_perf_* | Данные монитора производительности | Чистить после диагностики, не держать сбор постоянно |
Большой размер таблицы сам по себе — не диагноз. Таблица свойств обязана быть большой на большом каталоге, это нормально. Обслуживание нужно, когда по таблице идут медленные запросы или когда таблица разрослась «мусором» (устаревшие логи, статистика за годы, брошенные корзины анонимов). Базовое обслуживание InnoDB-таблиц:
-- Обновить статистику оптимизатора (быстро, помогает выбирать индексы)
ANALYZE TABLE b_iblock_element_prop_s21;
-- Дефрагментация и пересборка таблицы (тяжелее, делать в окно обслуживания)
OPTIMIZE TABLE b_sale_order_history;
-- Посмотреть размеры таблиц базы (data + index), топ по объёму
SELECT TABLE_NAME,
ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024) AS size_mb
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE()
ORDER BY (DATA_LENGTH + INDEX_LENGTH) DESC
LIMIT 20;
ANALYZE TABLE обновляет статистику, по которой оптимизатор выбирает индексы, — это дёшево и полезно делать регулярно. OPTIMIZE TABLE для InnoDB фактически пересобирает таблицу, устраняя фрагментацию после массовых удалений; операция тяжёлая и блокирующая, её планируют на окно обслуживания или проводят онлайн-инструментами. Запрос к information_schema даёт список самых больших таблиц — с него удобно начинать ревизию распухания.
Чистка и архивация: журнал, статистика, индекс
Значительная часть «распухания» базы — это не боевые данные, а служебное и устаревшее: логи, статистика хитов, старые сессии, дубли поискового индекса, брошенные корзины анонимов. Их можно и нужно чистить и архивировать, не трогая заказы и товары. Схема ниже показывает идею: распухшую таблицу разделяют на «живое» (оставить) и «устаревшее» (в архив или удалить), после чего обслуживают.
Что и как чистят в магазине на Битриксе, штатными средствами и по расписанию:
- Журнал событий. В настройках главного модуля задают срок хранения журнала событий (
b_event_log). Без ограничения он растёт бесконечно. Ставят разумный срок — и старые записи чистятся автоматически. - Статистика. Модуль статистики копит хиты и сессии в таблицах
b_stat_*— это одни из самых объёмных данных. Если детальная статистика не нужна, её ограничивают по сроку хранения или отключают сбор, оставив внешнюю аналитику. - Поисковый индекс. Таблицы поиска дублируют контент и разрастаются. Их периодически переиндексируют начисто и контролируют объём; если внутренний поиск не используется, индексацию отключают.
- Брошенные корзины и сессии. Корзины анонимных пользователей (
b_sale_basketс истёкшимfuser) и старые сессии копятся. Их чистят штатными агентами по сроку. - История заказов и старые заказы.
b_sale_order_historyрастёт быстрее заказов. Очень старые заказы и историю архивируют по бизнес-правилам (с учётом требований к хранению). - Данные монитора. После диагностики сбор монитора производительности выключают и чистят его таблицы — держать его включённым постоянно незачем.
Партиционирование больших таблиц (обзорно)
Партиционирование — способ разбить одну большую таблицу на несколько физических секций по какому-то ключу, оставаясь для приложения одной таблицей. Например, историю заказов или логи можно партиционировать по годам. Идея в том, что запрос, который бьёт по одному году, читает только соответствующую секцию, а не всю таблицу, а старые секции можно целиком отцепить и заархивировать одной быстрой операцией вместо массового DELETE.
Звучит привлекательно, но для типичного магазина партиционирование — не первый и обычно не нужный инструмент. Причин несколько. Во-первых, выигрыш появляется, только когда запросы почти всегда попадают в одну секцию (по ключу партиционирования); если фильтры идут по другим полям, база всё равно обходит все секции. Во-вторых, штатные таблицы Битрикса не рассчитаны на ручное партиционирование, и вмешательство в их структуру усложняет обновления и поддержку. В-третьих, грамотные индексы, кэш и настройка буфера дают несравнимо больший эффект при меньшем риске.
Партиционирование оправдано в highload-сценариях с действительно огромными и быстро растущими таблицами — история заказов за много лет, логи, аналитика, — где данные естественно делятся по времени и старые периоды регулярно архивируют. Подходить к нему стоит осознанно и только после того, как исчерпаны индексы, кэш и настройки сервера. Это инструмент «когда база большая и растёт по времени», а не «когда хочется, чтобы было быстрее».
Репликация и разделение чтения/записи
Когда одному серверу базы перестаёт хватать мощности даже после оптимизации, следующий архитектурный шаг — репликация: копия базы на отдельном сервере, которая автоматически повторяет за основным все изменения. Основной сервер (мастер) принимает запись, реплики (слейвы) держат актуальную копию и обслуживают чтение. Поскольку в магазине чтений на порядок больше, чем записи, чтение распределяют по репликам, разгружая мастер.
Хорошая новость: в 1С-Битрикс есть штатный модуль веб-кластера, который умеет работать с репликацией из коробки. Он знает про несколько подключений к базе и сам направляет запись на мастер, а чтение — на реплики, не требуя переписывать код компонентов. Подключения настраиваются в конфигурации (.settings.php), а модуль распределяет запросы по ролям серверов.
Важная тонкость — задержка репликации. Реплика получает изменения с небольшим отставанием от мастера, поэтому данные, только что записанные, могут ещё не появиться на реплике. Для критичных сценариев (сразу после оформления заказа показать этот заказ) чтение направляют на мастер, а на реплики отдают то, где секундная задержка некритична (каталог, витрина). Модуль кластера это учитывает. Репликация и разделение ролей — инструмент highload: для среднего магазина сначала выжимают всё из индексов, кэша и настроек одного сервера, и только при реальном упоре в мощность переходят к кластеру. Такие архитектуры мы проектируем в рамках highload-проектов на Битриксе.
Кэш БД и кэш Битрикса: в чём разница
Слово «кэш» звучит и про базу, и про Битрикс, и их регулярно путают, теряя эффект. На самом деле это разные уровни, которые дополняют друг друга.
| Уровень | Что кэширует | Что даёт |
|---|---|---|
| Буфер InnoDB | Данные и индексы таблиц в памяти | Быстрое выполнение самого SQL-запроса |
| Кэш запросов MySQL (устар.) | Результаты одинаковых SELECT | Мало пользы на пишущем магазине, в MySQL 8 убран |
| Кэш компонентов Битрикса | Результат блока (список, меню) | Запрос вообще не доходит до базы |
| Теговый кэш Битрикса | Кэш с инвалидацией по событию | Свежесть данных без лишней пересборки |
| Композит / HTML-кэш | Собранную страницу целиком | Отдача статики до запуска ядра и базы |
Ключевая мысль: кэш на стороне базы ускоряет выполнение запроса, но запрос всё равно доходит до базы, разбирается и выполняется. Кэш Битрикса работает выше и вообще не даёт запросу дойти до базы — готовый результат берётся из кэша приложения. Поэтому правильная стратегия двухуровневая: кэш Битрикса убирает большинство обращений к базе, а настройки MySQL/MariaDB ускоряют те запросы, которые всё же доходят (первая «холодная» сборка, персональные некэшируемые данные, обмен с 1С). Одно без другого работает вполсилы: идеальный конфиг базы не спасёт сайт с выключенным кэшем компонентов, а отличный кэш не поможет тяжёлому обмену, который пишет напрямую в базу.
Подробный разбор уровней кэша приложения — теговый кэш, автокомпозит, memcached и Redis, прогрев и инвалидация — в отдельном материале про кэширование в 1С-Битрикс. Здесь важно запомнить одно: база и кэш — не конкуренты, а два этажа одной системы, и оптимизируют их вместе.
Обмен с 1С и нагрузка на базу
Обмен с учётной системой — главный источник тяжёлой записи в базе магазина, и именно вокруг него возникает больше всего проблем производительности БД. Обмен массово пишет и обновляет товары, свойства, цены, остатки — тысячи операций записи, каждая из которых ещё и обновляет индексы затронутых таблиц. Если обмен идёт в час пик, он конкурирует с чтением витрины за ресурсы базы: посетители ждут, пока обмен занимает диск и процессор.
Несколько принципов, которые снимают эту боль:
- Обмен — в часы низкой нагрузки. Тяжёлые полные выгрузки планируют на ночь или окно минимального трафика, оставляя днём только лёгкие инкрементальные обновления остатков и цен.
- Инкрементальность вместо полной перезаписи. Обмен, который присылает только изменившиеся позиции, пишет в базу на порядок меньше, чем полная выгрузка всего каталога каждый раз.
- Минимум лишних индексов на таблицах свойств. Каждый индекс удорожает запись обмена — держат только те, что реально работают на фильтры витрины.
- InnoDB, а не MyISAM. Построчные блокировки InnoDB позволяют обмену писать, не блокируя чтение витрины целиком.
- Инвалидация и прогрев после обмена. После массовой записи сбрасывают теговый кэш затронутых инфоблоков и прогревают верхушку, чтобы днём посетители видели свежие данные и не ловили холодные пересборки.
Связка «база + обмен + кэш» — единая цепочка: обмен пишет в базу, база обслуживает запись быстрее при правильных настройках и разумных индексах, а кэш после обмена инвалидируется и прогревается, чтобы чтение шло не из базы. Механику самого обмена, форматы и расписание мы разбираем в материалах рубрики про интеграции; здесь важно, что обмен нужно рассматривать как нагрузку на БД и планировать соответственно.
Мониторинг базы данных
Оптимизация — не разовая акция, а процесс: каталог растёт, добавляются фильтры, копятся заказы и логи, и то, что работало полгода назад, снова становится узким местом. Чтобы ловить деградацию до того, как её заметят посетители, нужен постоянный мониторинг базы. Смотрят на несколько групп показателей.
| Что мониторить | О чём говорит |
|---|---|
| Доля попаданий в буфер InnoDB | Хватает ли памяти под горячие данные |
| Число и время медленных запросов | Не появились ли новые тяжёлые запросы |
| Число активных соединений | Близость к лимиту в пик |
| Временные таблицы на диске | Тяжёлые сортировки/группировки без индексов |
| Задержка репликации (если есть) | Насколько реплики отстают от мастера |
| Рост размеров ключевых таблиц | Когда пора чистить и обслуживать |
Инструменты бывают разные: встроенный монитор производительности Битрикса и его панель, статус-переменные MySQL (SHOW GLOBAL STATUS, SHOW ENGINE INNODB STATUS), внешние системы мониторинга с дашбордами и алертами по метрикам базы. Практичный минимум — регулярно смотреть slow query log и размеры таблиц, а на нагруженных проектах поставить внешний мониторинг с оповещениями. Как выстроить логирование и наблюдаемость на проекте в целом, включая базу, разбирается в материалах по инфраструктуре и мониторингу; здесь важно завести привычку: измерять не только когда «уже тормозит», а постоянно.
Частые ошибки оптимизации БД
Проблемы с базой в магазинах на Битриксе повторяются. Зная их заранее, вы сэкономите недели отладки.
- Тюнинг конфига вслепую. Крутят параметры «по совету из интернета», не измерив, что тормозит. Начинать нужно с диагностики, а не с конфига.
- Нет индексов под свои фильтры. Фильтр и сортировка по свойствам идут по неиндексированным колонкам — база сканирует таблицы свойств целиком. Лечится своими индексами под подтверждённые запросы.
- Индексы «на всё». Обратная крайность: навесили индексы на все свойства — обмен с 1С и запись стали тяжёлыми без пользы для чтения. Индексируют по факту.
- Дефолтный конфиг сервера. Буфер InnoDB меньше рабочего набора, база читает с диска вместо памяти. Настраивают под объём базы и ресурсы сервера.
- Таблицы на MyISAM. В старой базе остались MyISAM-таблицы, которые блокируются целиком на запись. Переводят на InnoDB.
- Распухшие логи и статистика. Журнал событий, статистика и монитор растут без ограничения срока. Ставят срок хранения и чистят.
- Эффект N+1. Список товаров тянет свойства сотней запросов вместо одного. Лечится правильной выборкой и кэшем инфоблоков.
- Обмен в час пик. Тяжёлая выгрузка конкурирует с витриной за ресурсы базы. Переносят на ночь, делают инкрементальным.
- Кэш и база противопоставлены. Считают, что «настроим базу — и кэш не нужен» или наоборот. Их оптимизируют вместе, это два уровня одной системы.
- Партиционирование ради скорости. Внедряют сложное партиционирование там, где хватило бы индексов и кэша. Это инструмент highload, а не первый шаг.
Чек-лист оптимизации БД
Короткий список, по которому удобно свериться при работе с базой магазина:
- Включён slow query log, тяжёлые запросы собраны и разобраны через EXPLAIN.
- Проверен «Монитор производительности» Битрикса и число SQL на ключевых страницах.
- Под подтверждённые медленные фильтры и сортировки добавлены индексы (в том числе свои — на свойства и пользовательские поля).
- Лишние и дублирующие индексы убраны, чтобы не грузить запись и обмен.
- Все таблицы на InnoDB; случайных MyISAM не осталось.
innodb_buffer_pool_sizeнастроен под размер горячих данных и ресурсы сервера.- Разумные лимиты соединений и временных таблиц; сессионные буферы не задраны.
- Известны самые большие таблицы; проводится ANALYZE/OPTIMIZE и обслуживание.
- Настроены срок хранения журнала событий, чистка статистики, переиндексация поиска, чистка брошенных корзин.
- Обмен с 1С идёт в часы низкой нагрузки, по возможности инкрементально; после обмена — инвалидация кэша и прогрев.
- Кэш Битрикса включён и работает (кэш компонентов, теги, композит) — база и кэш оптимизированы в связке.
- Партиционирование и репликация рассматриваются только после того, как исчерпаны индексы, кэш и настройки одного сервера.
- Настроен постоянный мониторинг базы с вниманием к медленным запросам и росту таблиц.
Вывод
Оптимизация базы данных для 1С-Битрикс — это не одна волшебная настройка, а дисциплина «измерил → нашёл узкое место → устранил → перепроверил». Сначала находят конкретные медленные запросы через slow query log, монитор производительности и EXPLAIN. Потом лечат их точечно: чаще всего не хватает индекса — иногда своего, на свойство инфоблока или пользовательское поле, — а иногда узкое место в памяти, и помогает буфер InnoDB под размер горячих данных. Всё это на движке InnoDB, который единственно правильный для магазина с его постоянной записью и обменом.
Дальше — гигиена больших таблиц: знать самые тяжёлые (свойства, заказы, история, логи, статистика, поиск), обслуживать их, чистить служебное и архивировать устаревшее, не трогая боевые данные. И только когда одному серверу базы после всей оптимизации перестаёт хватать мощности, в игру входят highload-инструменты — партиционирование и репликация с разделением чтения и записи через модуль веб-кластера. Важнее всего помнить: база и кэш Битрикса — два этажа одной системы, и работают они в связке. Наведите порядок в обоих по этому чек-листу — и магазин будет держать нагрузку, а не падать в час пик и после обмена. Другие материалы по производительности и архитектуре собраны в рубрике «Разработка и архитектура».