惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

P
Privacy International News Feed
Hacker News: Ask HN
Hacker News: Ask HN
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Application and Cybersecurity Blog
Application and Cybersecurity Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
G
GRAHAM CLULEY
W
WeLiveSecurity
H
Heimdal Security Blog
S
Secure Thoughts
L
Lohrmann on Cybersecurity
A
Arctic Wolf
N
News and Events Feed by Topic
Spread Privacy
Spread Privacy
S
Securelist
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
T
Tor Project blog
TaoSecurity Blog
TaoSecurity Blog
MyScale Blog
MyScale Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
L
LINUX DO - 热门话题
The GitHub Blog
The GitHub Blog
WordPress大学
WordPress大学
C
CERT Recently Published Vulnerability Notes
大猫的无限游戏
大猫的无限游戏
Project Zero
Project Zero
Google Online Security Blog
Google Online Security Blog
博客园_首页
博客园 - 叶小钗
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Cloudbric
Cloudbric
T
The Blog of Author Tim Ferriss
云风的 BLOG
云风的 BLOG
Cyberwarzone
Cyberwarzone
IT之家
IT之家
Help Net Security
Help Net Security
N
Netflix TechBlog - Medium
Martin Fowler
Martin Fowler
小众软件
小众软件
Last Week in AI
Last Week in AI
Hugging Face - Blog
Hugging Face - Blog
V2EX - 技术
V2EX - 技术
H
Help Net Security
Simon Willison's Weblog
Simon Willison's Weblog
Stack Overflow Blog
Stack Overflow Blog
Cisco Talos Blog
Cisco Talos Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
H
Hackread – Cybersecurity News, Data Breaches, AI and More
GbyAI
GbyAI
NISL@THU
NISL@THU
雷峰网
雷峰网

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Почему CRM в Битрикс24 тормозит на 50К сделок и что с этим делать
badcasedaily · 2026-05-06 · via Все публикации подряд на Хабре

Уровень сложностиСредний

Время на прочтение8 мин

Охват и читатели1.2K

Туториал

Когда в CRM пара тысяч сделок, всё летает. На десяти тысячах появляются первые паузы. На пятидесяти тысячах список сделок открывается по 8–15 секунд, фильтрация по пользовательским полям зависает, менеджеры жалуются, что «Битрикс тормозит». Обычно кто‑то предлагает «перейти на другую CRM» или «купить сервер помощнее», но оба варианта мимо: проблема не в Битриксе и не в железе, а в том, как данные хранятся и запрашиваются.

Рассмотрим конкретные причины и посмотрим, что с каждой делать.

Начинаем с диагностики, а не с оптимизации

Прежде чем что‑то чинить, нужно понять, что именно тормозит. Включите slow query log в MySQL:

# my.cnf
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
log_queries_not_using_indexes = 1

Последняя строка очень важна: она логирует запросы, которые не используют индексы, даже если они укладываются в секунду. На малых объёмах они проходят незаметно, на больших встают в пробку.

Перезапустите MySQL, воспроизведите проблему (откройте тот самый тормозящий список сделок) и посмотрите в лог. Обычно первые три‑четыре запроса в логе — это 80% проблемы.

Для каждого подозрительного запроса делайте EXPLAIN:

EXPLAIN SELECT d.ID, d.TITLE, d.STAGE_ID 
FROM b_crm_deal d
JOIN b_uts_crm_deal uf ON uf.VALUE_ID = d.ID
WHERE uf.UF_CRM_1234567890_REGION = 'Moscow'
ORDER BY d.DATE_CREATE DESC
LIMIT 50;

Если в колонке type видите ALL — это full table scan, запрос читает всю таблицу. Если rows показывает число, сопоставимое с количеством записей в таблице то же самое. Ищите такие запросы и работайте с ними.

Для D7 ORM можно вытащить сгенерированный SQL прямо из кода. Это помогает, когда вы не знаете, какой запрос генерирует конкретный getList:

\Bitrix\Main\Application::getConnection()->startTracker();

$deals = \Bitrix\Crm\DealTable::getList([
    'filter' => ['=STAGE_ID' => 'NEW'],
    'select' => ['ID', 'TITLE'],
    'limit' => 50,
]);

$tracker = \Bitrix\Main\Application::getConnection()->getTracker();
foreach ($tracker->getQueries() as $query) {
    error_log($query->getSql());
    error_log("Time: " . $query->getTime());
}

Теперь вы видите конкретный SQL с конкретным временем выполнения.

📌 Если вы работаете с кастомизацией, CRM, пользовательскими полями и логикой Bitrix24, короткое бесплатное вступительное тестирование поможет понять, где уже всё уверенно, а какие темы стоит подтянуть. ➞ Проверьте свой уровень в Bitrix24

Пользовательские поля без индексов

Это причина номер один, и встречается она практически в каждом проекте, где CRM выросла за 10К сделок.

Битрикс24 хранит пользовательские поля (UF_*) в отдельных таблицах: b_uts_crm_deal для обычных полей, b_utm_crm_deal для множественных. Связь с основной таблицей сделок идёт через VALUE_ID. Битрикс не создаёт индексы на эти поля автоматически: он не знает, по каким из них вы будете фильтровать.

Проверяем:

SHOW INDEX FROM b_uts_crm_deal;

Увидите индекс только на VALUE_ID (первичный ключ). На ваших UF_CRM_* полях индексов нет.

Чтобы найти правильное имя поля (в таблице оно отличается от того, что видно в интерфейсе):

SELECT FIELD_NAME, USER_TYPE_ID, SORT 
FROM b_user_field 
WHERE ENTITY_ID = 'CRM_DEAL'
ORDER BY SORT;

Допустим, нашли поле UF_CRM_1695283174_REGION. Добавляем индекс:

ALTER TABLE b_uts_crm_deal 
ADD INDEX ix_region (UF_CRM_1695283174_REGION);

Проверяем результат через EXPLAIN на тот же запрос, который раньше показывал ALL. Теперь в колонке type должно быть ref или range, а rows уменьшится на порядки.

На практике это ускоряет фильтрацию в 10–50 раз. Один ALTER TABLE, и список сделок, который открывался 12 секунд, начинает открываться за полсекунды.

Не создавайте индексы на всё подряд: каждый индекс замедляет запись (INSERT и UPDATE). Индексируйте только те поля, по которым реально фильтруют и сортируют. Если не уверены — посмотрите в slow query log, какие UF_* поля фигурируют в WHERE.

Для множественных полей (b_utm_crm_deal) принцип тот же, но таблица устроена иначе: одно поле = несколько строк на одну сделку. Индекс нужен на комбинацию (FIELD_ID, VALUE):

ALTER TABLE b_utm_crm_deal 
ADD INDEX ix_field_value (FIELD_ID, VALUE(100));

Фильтры в ORM: LIKE вместо = по умолчанию

Это ловушка, в которую попадают разработчики, знакомые с SQL, но не знакомые с особенностями Битрикса.

Когда вы пишете фильтр в D7 ORM без префикса:

$deals = \Bitrix\Crm\DealTable::getList([
    'filter' => ['TITLE' => 'Сделка №123'],
]);

Битрикс генерирует не WHERE TITLE = 'Сделка №123', а WHERE TITLE LIKE 'Сделка №123'. LIKE по строковым полям игнорирует индексы (если нет wildcard в начале, индекс используется, но LIKE всё равно медленнее точного сравнения).

Чтобы получить точное сравнение, нужен префикс =:

$deals = \Bitrix\Crm\DealTable::getList([
    'filter' => ['=TITLE' => 'Сделка №123'],
]);

То же касается старого API:

// LIKE:
\CCrmDeal::GetListEx([], ['TITLE' => 'Сделка №123']);

// Точное сравнение:
\CCrmDeal::GetListEx([], ['=TITLE' => 'Сделка №123']);

На 50К сделок разница между LIKE и = на индексированном поле может составлять 3–5 раз по времени. На неиндексированном оба медленные, но = всё равно быстрее, потому что MySQL может остановить поиск при первом совпадении.

Проблема N+1 в списках

Откройте список сделок и посмотрите в DevTools вкладку Network (или в slow query log). На каждый элемент списка CRM генерирует дополнительные запросы: подгрузка контакта, компании, ответственного, значений множественных полей. Если на странице 50 сделок, получается 200–300 запросов к базе на одну загрузку.

На уровне ядра CRM это не исправить, так устроена архитектура. Но можно уменьшить количество данных, которые запрашиваются.

  • Уберите из колонок списка всё, что менеджерам не нужно каждый день. Каждая колонка с пользовательским полем или связанной сущностью (контакт, компания) — это дополнительные JOIN‑ы и подзапросы. Оставьте 5–7 колонок вместо пятнадцати, и количество запросов на загрузку страницы упадёт вдвое‑втрое.

  • Используйте сохранённые фильтры вместо «показать всё». Фильтр по стадии + ответственному отсекает 90% данных, и подзапросы к связанным сущностям выполняются для десятков записей, а не для тысяч.

Если вы пишете свой код, работающий со сделками, следите за тем, чтобы не делать запросы в цикле:

// Плохо: N+1
$deals = \Bitrix\Crm\DealTable::getList([
    'select' => ['ID', 'TITLE'],
    'limit' => 50,
])->fetchAll();

foreach ($deals as $deal) {
    // Отдельный запрос на каждую сделку!
    $contacts = \CCrmDeal::GetContactIDs($deal['ID']);
}

// Лучше: собрать ID и запросить пачкой
$dealIds = array_column($deals, 'ID');
$contacts = \Bitrix\Crm\Binding\DealContactTable::getList([
    'filter' => ['=DEAL_ID' => $dealIds],
])->fetchAll();

Разница на 50 сделках: 50 запросов в первом варианте, один во втором.

Select *

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

// Плохо: тащит все поля, включая UF_*
$deals = \Bitrix\Crm\DealTable::getList([
    'filter' => ['=STAGE_ID' => 'WON'],
]);

// Лучше: только то, что нужно
$deals = \Bitrix\Crm\DealTable::getList([
    'filter' => ['=STAGE_ID' => 'WON'],
    'select' => ['ID', 'TITLE', 'OPPORTUNITY'],
]);

Без указания select Битрикс запрашивает все поля сущности, включая пользовательские, что означает JOIN к b_uts_crm_deal. Если пользовательских полей двадцать, каждое добавляет колонку в SELECT. На 50К сделок разница в объёме данных, которые MySQL читает с диска, ощутимая.

Обработчики событий и агенты

CRM Битрикс24 генерирует события на каждое действие: создание сделки, обновление, смену стадии. Если на эти события подписаны обработчики, которые сами делают тяжёлые запросы, каждая операция замедляется.

Обработчик OnAfterCrmDealUpdate, который при каждом обновлении сделки пересчитывает сумму всех сделок контакта:

// Плохо: тяжёлый запрос на каждое обновление
AddEventHandler('crm', 'OnAfterCrmDealUpdate', function($fields) {
    $deals = \CCrmDeal::GetListEx(
        [],
        ['CONTACT_ID' => $fields['CONTACT_ID']],
        false, false,
        ['OPPORTUNITY']
    );
    $sum = 0;
    while ($deal = $deals->Fetch()) {
        $sum += $deal['OPPORTUNITY'];
    }
    // обновляем контакт...
});

Если менеджер массово обновляет 100 сделок (например, меняет стадию через групповое действие), этот обработчик вызовется 100 раз, каждый раз делая запрос ко всем сделкам контакта. При 500 сделках на контакт это 50 000 строк, прочитанных из базы ради одного группового действия.

Решение в том, чтобы вынести тяжёлые пересчёты в агент или очередь, а в обработчике только ставить задачу на пересчёт:

AddEventHandler('crm', 'OnAfterCrmDealUpdate', function($fields) {
    // Только помечаем, что нужен пересчёт
    \CAgent::AddAgent(
        "RecalcContactSum({$fields['CONTACT_ID']});",
        "", "N", 0, "", "Y"
    );
});

Агент выполнится один раз, даже если 100 сделок обновились одновременно (если агент с таким именем уже стоит в очереди, повторно он не добавится).

Хайлоад‑блоки для больших справочников

Когда поле типа «Список» содержит больше 200–300 значений, оно тормозит. Битрикс хранит значения в b_user_field_enum и при каждой загрузке формы запрашивает все значения для каждого поля‑списка. На справочнике из 5000 городов это больно.

Хайлоад‑блок — это отдельная таблица в MySQL с ORM‑обвязкой Битрикса:

use Bitrix\Highloadblock as HL;

$result = HL\HighloadBlockTable::add([
    'NAME' => 'City',
    'TABLE_NAME' => 'app_city',
]);

$hlblockId = $result->getId();

$oUserTypeEntity = new CUserTypeEntity();
$oUserTypeEntity->Add([
    'ENTITY_ID' => 'HLBLOCK_' . $hlblockId,
    'FIELD_NAME' => 'UF_NAME',
    'USER_TYPE_ID' => 'string',
    'MANDATORY' => 'Y',
]);

После создания добавляем индекс напрямую:

ALTER TABLE app_city ADD INDEX ix_name (UF_NAME(50));

Теперь при загрузке формы Битрикс не тащит все 5000 городов, а подгружает через AJAX с фильтрацией на стороне базы. На больших справочниках разница между «форма открывается 5 секунд» и «форма открывается мгновенно».

Но хайлоад‑блоки ускоряют загрузку справочных данных, а не список сделок. Если тормозит список, нужны индексы на UF‑полях.

Настройка MySQL

Помимо индексов есть настройки самого MySQL, которые влияют на производительность CRM. Самые важные для Битрикса:

# my.cnf
innodb_buffer_pool_size = 1G   # 70-80% доступной RAM на выделенном сервере
innodb_log_file_size = 256M    # уменьшает количество checkpoint-ов
join_buffer_size = 4M          # для JOIN без индексов (пока не добавили)
sort_buffer_size = 4M          # для ORDER BY без индексов
tmp_table_size = 256M          # для временных таблиц
max_heap_table_size = 256M     # связано с tmp_table_size

Самая важная настройка — innodb_buffer_pool_size. Если у сервера 4 ГБ RAM и buffer pool стоит в дефолтных 128 МБ, MySQL читает данные с диска на каждый запрос. Увеличение buffer pool до 2–3 ГБ может ускорить всё в разы без единого изменения в коде.

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

SHOW ENGINE INNODB STATUS\G
-- Ищите секцию BUFFER POOL AND MEMORY
-- Buffer pool hit rate должен быть > 99%
-- Если ниже — buffer pool мал

Порядок действий

Включите slow query log. Найдите самые медленные запросы. Сделайте EXPLAIN на каждый. Добавьте индексы на UF‑поля, по которым фильтруете. Проверьте, что в фильтрах ORM стоят префиксы = для точных сравнений. Укажите select явно, не тащите все поля. Вынесите тяжёлые обработчики событий в агенты. Переведите большие справочники на хайлоад‑блоки. Поднимите innodb_buffer_pool_size.

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

Проблемы с производительностью CRM редко решаются одной галочкой в настройках. Чтобы уверенно дорабатывать Битрикс24, нужно понимать, как устроены данные, интерфейсы, события, пользовательские поля и ограничения платформы.

12 мая в 20:00 в рамках курса «Разработчик Битрикс24» пройдёт бесплатный открытый урок «Кастомизация интерфейса Bitrix24: создание уникальных пользовательских решений».
Разберём, как дорабатывать интерфейс и функциональность Bitrix24 без изменения ядра системы, какие возможности кастомизации доступны разработчику и как создавать решения, которые не ломаются при обновлениях.

Записаться на открытый урок

Это возможность познакомиться с преподавателем‑практиком, посмотреть на формат обучения и задать вопросы по разработке под Bitrix24.