Обновить
3
Андрей Погорелый@Andrey_Pogorely

CEO digital-агенства «Рецифра»

5
Подписчики
Отправить сообщение

Хорошее замечание! Компонент bitrix:news.list на момент нашей работы (конец 2023 года) был стандартным, из актуального ядра Битрикс. Но здесь есть нюанс.

Действительно, в актуальном ядре CIBlockElement::GetList() с третьим параметром false не выполняет отдельный SELECT COUNT(*). Однако проблема возникала не в самом вызове GetList, а в механизме пагинации стандартного компонента. Когда в компоненте включена пагинация (DISPLAY_BOTTOM_PAGER => "Y"), стандартный bitrix:news.list использует CDBResult::NavStart(), который внутри себя делает отдельный COUNT-запрос для определения общего количества элементов — это нужно для построения навигации вида “Страница 1 из N”.

То есть сам GetList с false действительно не считает — но навигационная обвязка вокруг него считает. И именно этот COUNT через NavStart блокировал таблицу.

Что касается mysqli_num_rows / pg_num_rows — это подсчет количества строк в уже полученном результате выборки, он работает в памяти PHP и не создает дополнительной нагрузки на БД. Но для построения полной пагинации (с номерами страниц и общим числом) Битриксу нужно знать общее количество элементов в таблице с учетом фильтра, а не только количество возвращенных строк на текущей странице. Вот для этого и выполняется отдельный SELECT COUNT(*).

Наш кастомный компонент nocount:news.list — это форк стандартного, в котором мы заменили логику пагинации: вместо NavStart используем nTopCount + nOffset и запрашиваем на 1 элемент больше, чем нужно для отображения. Если “лишний” элемент пришел — значит есть следующая страница. Визуально получается навигация типа “Назад / Вперед” вместо “Страница 1 из 347”, зато ни одного COUNT-запроса.

Сайт начинал испытывать проблемы уже при ~10 rps (запросов в секунду) — это примерно 20 000-30 000 посетителей в день при равномерном распределении, но на практике трафик приходит волнами (утренние и дневные пики), поэтому проблемы начинались и при меньшем суточном трафике.

Ключевой момент: сайт падал не из-за абсолютного числа посетителей, а из-за совпадения нескольких факторов:

  • COUNT-запросы блокировали таблицу b_iblock_element при каждом хите с пагинацией

  • Инвалидация кеша редакторами (правки статей) создавала моменты, когда кеш отсутствовал и все запросы шли напрямую в БД

  • Ночные боты обходили страницы с пагинацией, генерируя COUNT-запросы даже без живых пользователей

Да, контент создается динамически из базы данных MySQL. Сайт работает на 1С-Битрикс — это CMS, в которой все данные (новости, статьи, рубрики) хранятся в инфоблоках в единой таблице b_iblock_element. При каждом запросе пользователя (если кеш не валиден) PHP-компоненты Битрикса выполняют SQL-запросы к MySQL для формирования страницы. Кеширование есть, но из-за описанных в статье проблем оно фактически не работало.

После оптимизации сайт стабильно выдерживал нагрузочный тест в 50 rps (~60% CPU), то есть запас по мощности составил минимум x5 от обычной нагрузки.

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность

Специализация

Бэкенд разработчик, Фронтенд разработчик
Старший
PHP
Laravel
Python
Базы данных
MySQL
Symfony
REST