Настройка полнотекстовых индексов MySQL для 1С-Битрикс
Стандартный поиск Битрикс работает через компонент bitrix:search.page и модуль search. Он строит собственный индекс в таблице b_search_content — туда попадают все элементы инфоблоков, страницы, форумы. Поиск идёт через LIKE '%запрос%', что при объёме свыше 100 тысяч элементов превращается в table scan с деградацией до 5–15 секунд. Решение — полнотекстовые индексы MySQL (FULLTEXT) напрямую на таблицах инфоблоков или на b_search_content.
Как работает FULLTEXT в MySQL
FULLTEXT-индекс строится поверх текстовых колонок (CHAR, VARCHAR, TEXT). Запросы — через MATCH() AGAINST(). Два режима: IN NATURAL LANGUAGE MODE (ранжирование по релевантности) и IN BOOLEAN MODE (поддержка операторов: +обязательно, -исключить, *префикс).
Ограничения MySQL FULLTEXT:
- Минимальная длина слова по умолчанию:
ft_min_word_len = 4(для MyISAM),innodb_ft_min_token_size = 3(для InnoDB). Слова короче — не индексируются. - Стоп-слова (innodb_ft_server_stopword_table) — их нужно отключить или перенастроить.
- Для русского языка требуется правильная кодировка (utf8mb4) и, желательно, внешний парсер (ngram для InnoDB или Sphinx/Manticore как альтернатива).
Настройка my.cnf для русского FULLTEXT
[mysqld]
# Минимальная длина токена
innodb_ft_min_token_size = 2
# Отключаем стоп-слова по умолчанию (они для английского)
innodb_ft_enable_stopword = OFF
# Включаем ngram-парсер для поддержки CJK и коротких слов
# (альтернатива — использовать ngram в DDL)
После изменения innodb_ft_min_token_size нужно пересоздать все FULLTEXT-индексы — просто перезапуска MySQL недостаточно.
Создание FULLTEXT-индекса на b_search_content
Таблица b_search_content — центральная точка поиска Битрикс. Ключевые колонки: TITLE, BODY.
-- Проверяем текущие индексы
SHOW INDEX FROM b_search_content;
-- Создаём FULLTEXT-индекс
ALTER TABLE b_search_content
ADD FULLTEXT INDEX ft_search_content (TITLE, BODY)
WITH PARSER ngram;
WITH PARSER ngram — встроенный в MySQL 5.7+ парсер, разбивающий текст на биграммы/триграммы. Хорошо работает для кириллицы, не требует внешних инструментов.
Размер токена ngram настраивается:
[mysqld]
ngram_token_size = 2 # оптимально для русского
Переопределение поиска в Битрикс
Стандартный bitrix:search.page не использует FULLTEXT — он работает через ORM Битрикс с LIKE. Чтобы подключить FULLTEXT, переопределяем запрос в кастомном компоненте-обёртке или через событие OnBeforeIBlockElementGetList.
Минимальный кастомный поиск через FULLTEXT:
namespace Local\Search;
class FulltextSearcher
{
private \Bitrix\Main\DB\Connection $db;
public function __construct()
{
$this->db = \Bitrix\Main\Application::getConnection();
}
public function search(string $query, int $page = 1, int $limit = 20): array
{
$query = $this->sanitizeQuery($query);
$offset = ($page - 1) * $limit;
// BOOLEAN MODE с префиксным поиском
$boolQuery = '+' . implode('* +', explode(' ', $query)) . '*';
$sql = "
SELECT
sc.ID,
sc.TITLE,
sc.URL,
sc.MODULE_ID,
sc.ITEM_ID,
MATCH(sc.TITLE, sc.BODY) AGAINST (? IN BOOLEAN MODE) AS relevance
FROM b_search_content sc
WHERE
MATCH(sc.TITLE, sc.BODY) AGAINST (? IN BOOLEAN MODE)
AND sc.SITE_ID = ?
AND sc.PUBLIC = 'Y'
ORDER BY relevance DESC
LIMIT ? OFFSET ?
";
$result = $this->db->query($sql, [$boolQuery, $boolQuery, SITE_ID, $limit, $offset]);
$rows = [];
while ($row = $result->fetch()) {
$rows[] = $row;
}
return $rows;
}
public function count(string $query): int
{
$boolQuery = '+' . implode('* +', explode(' ', $query)) . '*';
$result = $this->db->query(
"SELECT COUNT(*) AS cnt FROM b_search_content
WHERE MATCH(TITLE, BODY) AGAINST (? IN BOOLEAN MODE)
AND SITE_ID = ? AND PUBLIC = 'Y'",
[$boolQuery, SITE_ID]
);
return (int)$result->fetch()['cnt'];
}
private function sanitizeQuery(string $query): string
{
// Убираем операторы FULLTEXT, оставляем только слова
$query = preg_replace('/[+\-><()\~*"@]+/', ' ', $query);
$query = preg_replace('/\s+/', ' ', trim($query));
return mb_substr($query, 0, 255);
}
}
Кастомный компонент поиска
Шаблон компонента использует FulltextSearcher вместо стандартного модуля:
// /local/components/local/search.fulltext/component.php
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die();
$query = trim($_GET['q'] ?? '');
if (mb_strlen($query) < 2) {
$this->arResult['ITEMS'] = [];
$this->arResult['TOTAL'] = 0;
$this->IncludeComponentTemplate();
return;
}
$searcher = new \Local\Search\FulltextSearcher();
$page = max(1, (int)($_GET['PAGEN_1'] ?? 1));
$this->arResult['ITEMS'] = $searcher->search($query, $page);
$this->arResult['TOTAL'] = $searcher->count($query);
$this->arResult['QUERY'] = htmlspecialchars($query);
$this->arResult['PAGE'] = $page;
$this->SetResultCacheKeys([]); // поиск не кешируем
$this->IncludeComponentTemplate();
FULLTEXT на таблицах инфоблоков
Для поиска только по каталогу эффективнее индексировать напрямую таблицы инфоблоков: b_iblock_element (NAME, DETAIL_TEXT) и b_iblock_section (NAME).
ALTER TABLE b_iblock_element
ADD FULLTEXT INDEX ft_iblock_element_search (NAME, SEARCHABLE_CONTENT)
WITH PARSER ngram;
Колонка SEARCHABLE_CONTENT — заполняется Битрикс при сохранении элемента, содержит объединённый текст для поиска.
Мониторинг индекса
-- Статистика FULLTEXT-индексов InnoDB
SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_INDEX_CACHE;
SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_INDEX_TABLE;
-- Принудительная пересборка
SET GLOBAL innodb_optimize_fulltext_only = ON;
OPTIMIZE TABLE b_search_content;
SET GLOBAL innodb_optimize_fulltext_only = OFF;
Состав работ
- Аудит текущего поиска, замер производительности
- Настройка
my.cnf:ngram_token_size, отключение стоп-слов - Создание FULLTEXT-индексов на
b_search_contentи/или таблицах инфоблоков - Разработка кастомного компонента поиска с FULLTEXT-запросами
- Пересборка индекса Битрикс-поиска (
BXSearch::reindex()) - Нагрузочное тестирование до/после
Сроки: настройка индексов и кастомного компонента — 1–2 недели.







