При розростанні каталогу товарів у 1С-Бітрікс до 200 тисяч елементів стандартний пошук перестає справлятися — час відгуку сягає понад 10 секунд. На одному з проєктів із каталогом на 500 тисяч товарів пошук через LIKE займав до 18 секунд. Після впровадження FULLTEXT з ngram-парсером час знизився до 0.2 секунди — це в 90 разів швидше. Ми провели аудит, налаштували my.cnf, створили індекси та розробили кастомний компонент. Вартість робіт починається від 500$ (залежить від обсягу). Зв'яжіться з нами для оцінки вашого проєкту.
Стандартний пошук Бітрікс працює через компонент 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:
- Мінімальна довжина слова за замовчуванням:
innodb_ft_min_token_size = 3. Слова коротші не індексуються. - Стоп-слова (innodb_ft_server_stopword_table) — їх потрібно відключити або переналаштувати.
- Для російської мови потрібне правильне кодування (utf8mb4) і, бажано, зовнішній парсер (ngram для InnoDB або Sphinx/Manticore як альтернатива).
Вибір парсера та архітектури пошуку
Ngram парсер для кирилиці
Стандартний FULLTEXT-парсер MySQL орієнтований на англійську мову: мінімальна довжина слова 3 символи, стоп-слова. Кириличні слова часто коротші (прийменники, сполучники), тому вони не індексуються. ngram парсер розбиває текст на біграми (по 2 символи) і не залежить від мови. Це гарантує, що будь-який запит із двох і більше символів знайде відповідності. Налаштування ngram_token_size=2 у my.cnf вмикає біграмний режим.
Використання FULLTEXT індексів
Якщо пошук потрібен лише за каталогом, ефективніше індексувати безпосередньо таблиці b_iblock_element (NAME, DETAIL_TEXT) і b_iblock_section (NAME). Це знижує навантаження на b_search_content і спрощує архітектуру. Однак для загальносайтового пошуку зручніше використовувати b_search_content — він включає всі типи контенту.
Налаштування та створення індексів
Налаштування my.cnf для російського FULLTEXT
[mysqld]
innodb_ft_min_token_size = 2
innodb_ft_enable_stopword = OFF
ngram_token_size = 2
Після зміни потрібно перестворити всі FULLTEXT-індекси — просто перезапуску MySQL недостатньо.
створити FULLTEXT-індекс на b_search_content і таблицях інфоблоків
Таблиця b_search_content — центральна точка пошуку Бітрікс. Ключові колонки: TITLE, BODY. Для каталогу індексуємо b_iblock_element з колонкою SEARCHABLE_CONTENT, яку Бітрікс заповнює автоматично.
ALTER TABLE b_search_content
ADD FULLTEXT INDEX ft_search_content (TITLE, BODY) WITH PARSER ngram;
ALTER TABLE b_iblock_element
ADD FULLTEXT INDEX ft_iblock_element_search (NAME, SEARCHABLE_CONTENT) WITH PARSER ngram;
WITH PARSER ngram — вбудований у MySQL 5.7+ парсер, що розбиває текст на біграми/триграми. Добре працює для кирилиці, не потребує зовнішніх інструментів.
Моніторинг та обслуговування індексів
Перевірка стану індексу:
-- Статистика 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;
Ці запити допомагають відстежувати наповнення індексу та за потреби перебудовувати його.
Розробка кастомного компонента для FULLTEXT
Перевизначення пошуку в Бітрікс
Стандартний 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
{
$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();
Порівняння продуктивності та парсерів
Порівняльні метрики
| Метод | Час при 100k записів | CPU usage | Індексація |
|---|---|---|---|
LIKE %запит% |
5–15 сек | 100% одного ядра | Не потребує |
| FULLTEXT (BOOLEAN) | 0.1–0.5 сек | 10–20% | Потрібна початкова |
| Парсер | Мінімальна довжина слова | Підтримка кирилиці | Потребує конфігурації |
|---|---|---|---|
| Стандартний | 3 символи | Часткова | Ні |
| ngram (біграми) | 2 символи | Повна | ngram_token_size |
Як впровадити FULLTEXT за 5 кроків
- Аудит: замір поточного часу пошуку, аналіз обсягу даних.
- Конфігурація MySQL: встановлення
ngram_token_size=2, відключення стоп-слів. - Створення індексів: виконання ALTER TABLE з FULLTEXT і парсером ngram.
- Розробка компонента: створення кастомного
FulltextSearcherі шаблону. - Тестування: навантажувальне тестування, порівняння «до/після».
Що входить у роботу
- Аудит поточного пошукового індексу, замір часу запитів.
- Налаштування конфігурації MySQL:
ngram_token_size, відключення стоп-слів. - Створення FULLTEXT-індексів на
b_search_contentта/або таблицях інфоблоків. - Розробка кастомного компонента пошуку з FULLTEXT-запитами.
- Перезбирання індексу Бітрікс-пошуку (
BXSearch::reindex()). - Навантажувальне тестування зі звітом «до/після».
- Надання документації та скриптів міграції.
- Безкоштовна підтримка протягом місяця.
Наш досвід у Бітрікс-розробці — понад 10 років, ми успішно прискорили пошук на 50+ проєктах. Вартість робіт від 500$ (залежить від складності). Отримайте консультацію — пишіть, оцінимо ваш проєкт за 1 день.
Згідно з документацією MySQL 8.0, ngram парсер забезпечує коректну індексацію кирилиці без зовнішніх залежностей (MySQL Ngram Full-Text Parser).
Як FULLTEXT порівнюється з LIKE?
FULLTEXT в 50-100 разів швидше за LIKE при 100k записів. Тому для великих каталогів це єдине прийнятне рішення.
Як налаштувати ngram парсер?
Додайте в my.cnf: ngram_token_size=2, innodb_ft_min_token_size=2, innodb_ft_enable_stopword=OFF та перестворіть індекси.







