Pull to refresh
3
Андрей Погорелый@Andrey_Pogorely

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

5
Subscribers
Send message

Хорошее замечание! Компонент 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 от обычной нагрузки.

Information

Rating
Does not participate
Registered
Activity

Specialization

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