У вас є VPS. У вас є користувачі, які вводять «invoice 2021 acme» і очікують магії. Також у вас є бюджет на операційну підтримку, який підозріло схожий на «ви, після вечері». Питання не в тому, чи OpenSearch шукає краще за MySQL. Він шукає. Питання в тому, чи запускати його самому на маленькій машині — розумне обмеження… або повільно розв’язуючася аварія.
Це практична різниця між «пошук як функція» та «пошук як розподілена система, що завжди ввімкнена і образливо ставиться до малої кількості оперативної пам’яті». Виберіть невірний інструмент — і ваш VPS навчить вас смиренності, по одному своп-шторму за раз.
Рамка рішення: що ви насправді обираєте
Якщо звести рішення до «що швидше», отримаєте дороге рішення й ненадійну систему. Ви обираєте:
- Якість пошуку проти операційної складності. MySQL може забезпечити гідний ключовий пошук. OpenSearch дає тонке налаштування релевантності, нечіткий пошук, мовні аналайзери, синоніми, фасети, підсвічування тощо. Він також вимагає пам’яті, дискового I/O і постійної уваги.
- Одна система проти двох систем запису. Якщо додати OpenSearch, MySQL залишається джерелом істини. Тепер у вас також є індекс, який «згодом буде коректним». Потрібно побудувати та експлуатувати синхронізацію.
- Режими відмов. Коли MySQL повільний — зазвичай через запити, блокування або I/O. Коли OpenSearch повільний — це може бути тиск на heap, мерджі сегментів, інертність кешу, паузи GC, розклад шардів, поведінка refresh/flush або диск, який бреше про свою продуктивність.
- Що ви можете дозволити собі неправильно оцінити. На маленькому VPS хибне припущення може перетворитися на «все добре» аж до моменту, коли ядро OOM-kill’ить Java.
Пряма порада:
- Якщо пошук простий (ключові слова в заголовку/тексті, фільтри за кількома колонками, невеликий корпус) — почніть з MySQL і не вибачайтеся. Використовуйте коректні індекси, можливо FULLTEXT, і прийміть, що релевантність буде «достатньою».
- Якщо пошук — ключова цінність продукту (релевантність, толерантність до помилок, фасети, багатопольове зважування, синоніми, мови) — використовуйте OpenSearch, але розгляньте керовані сервіси перед самохостингом на крихітному VPS.
- Якщо ви все ще хочете самостійно хостити OpenSearch на VPS, робіть це так, ніби ви запускаєте продакшн-сервіс: моніторинг, знімки, налаштування heap, перевірки диска та протестований план відновлення. Інакше ви не самохостите — ви імпровізуєте.
Цитата, яка має висіти на стіні кожного, хто самохостить пошук: «Все ламається постійно.»
— Werner Vogels.
Факти й історія, що пояснюють сучасний біль
Трохи контексту робить компроміси менш довільними. Ось конкретні, корисні факти, що безпосередньо відповідають на операційну реальність:
- Lucene — це ядро під Elasticsearch і OpenSearch. Існує з кінця 1990-х, і його модель сегментів/мерджів — причина, чому дисковий I/O так важливий.
- MySQL FULLTEXT передував сучасним «пошуковим платформам». Його створили, щоб додати ключовий пошук до реляційних навантажень, а не як лабораторію релевантності.
- Elasticsearch почався у 2010 році як розподілений пошуковий сервер на Lucene, щоб зробити «кластерний пошук» доступним — до того моменту, поки не треба було ним керувати.
- OpenSearch відгалузився від Elasticsearch 7.10 після змін у ліцензуванні. Операційно він успадкував більшість сильних сторін і підводних каменів Elasticsearch.
- Інвертовані індекси — чому OpenSearch швидкий у текстовому пошуку. Це не «БД робить LIKE»; це інша структура даних, оптимізована для вибірки.
- MySQL і LIKE ‘%term%’ не можуть використовувати звичайний B-tree індекс для початкових шаблонів з підрядками, тому люди роблять повні перегляди таблиць і звинувачують «MySQL-пошук».
- Refresh vs commit — реальна концепція в системах на Lucene. Документ може бути «пошуковим» швидко (refresh), але не обов’язково стійким до відмов до наступного commit/знімка.
- Малі диски VPS часто мають кредити на прискорення. Ваш бенчмарк може виглядати відмінно, поки кредити не закінчаться, і тоді мерджі сегментів перетворяться на болото.
MySQL як пошуковий механізм: що працює, що ламається
Що MySQL робить добре для пошуку
MySQL — нудний у найкращому сенсі. Для «пошукових» функцій в межах реляційного застосунку він може бути саме тим, що потрібно:
- Фільтрація + сортування + пагінація по структурованих колонках — його стихія.
- Транзакційна коректність. Коли ви записуєте дані, вони існують. Ніякого асинхронного пайплайна індексації не потрібно.
- Операційна простота. Один сервіс, одна стратегія бекапів, один набір SLO.
- Ефективність витрат на маленькому залізі. MySQL не вимагає великого JVM heap, щоб існувати.
Патерни пошуку в MySQL, які прийнятні
Є три поширені патерни, у зростаючому порядку «це справжній пошук».
-
Пошук за префіксом на нормалізованих полях (імена користувачів, SKU): використовуйте індекси й префіксні пошуки.
Приклад:
WHERE sku LIKE 'ABC%'з індексом наsku. Швидко й передбачувано. -
Токенний пошук з FULLTEXT (поля з натуральною мовою): використовуйте FULLTEXT індекси й BOOLEAN MODE.
Це дає токенізацію й скоринг, але це не сучасна платформа релевантності. Ближче до «краще за LIKE».
-
Гібридний підхід: зберігайте MySQL як авторитет, попередньо обчислюйте пошукову колонку (нормалізовані ключові слова) для простого зіставлення і приймайте обмеження.
Це часто виграшний рух для маленької команди, де uptime важливіший за тонке налаштування релевантності.
Що ламається (або стає болісним)
- Пошук підрядків (contains) у великому масштабі. Користувачі вводять часткові фрагменти; ви тягнетеся до
%term%; CPU вентилятор проходить прослуховування на реактивний двигун. - Очікування щодо релевантності. «Чому цей результат вище за той?» У MySQL відповідь часто «тому що токен співпав більше разів», що не те саме, що «це те, що мав на увазі користувач».
- Помилки, стемінг, синоніми, мультимовні аналайзери. Ви можете апроксимувати деяке з цього, але в підсумку винайдете власний пошуковий двигун у SQL. Не робіть цього.
- Фасети/агрегації з великою кардинальністю. MySQL може групувати й рахувати, але інтерактивний фасетинг з кількома вимірами починає відчуватися як DDoS проти вашої власної бази даних.
MySQL — хороший слуга і поганий месія.
OpenSearch на VPS: потужний, вимогливий, часом драматичний
Що дає OpenSearch, чого MySQL не має
- Справжні інструменти релевантності. Підсилення полів, BM25, фразові запити, близькість та аналайзери, які трактують людську мову не як простий рядок.
- Нечітке зіставлення для помилок і близьких збігів.
- Фасети й агрегації, зроблені для інтерактивного дослідження.
- Підсвічування, підказки, шаблони автозаповнення, яких очікують користувачі сучасного пошуку.
- Денормалізовані документи, оптимізовані для читання, а не для джойнів.
Чого OpenSearch вимагає від вас
OpenSearch — це пошуковий двигун плюс розподілена система. Навіть як однонодовий вузол на VPS, він поводиться як обидва.
- Управління пам’яттю. JVM heap, off-heap, OS page cache і неприємна реальність, що «вільна пам’ять» — це функція продуктивності, а не марнотратство.
- Дисковий I/O. Мерджі сегментів, flush’і, translog, snapshots. Дешеві диски перетворюють «швидке індексування» на «чому ж вантаж 40».
- Рішення щодо життєвого циклу індексів. Кількість шардів, інтервали refresh, репліки (або їх відсутність на одноноді), зберігання й дисципліна мапінгу.
- Операційна гігієна. Знімки, моніторинг, алерти, покриття поновлень (навіть однонодовий вузол потребує обережності) та тестування відновлення.
Короткий жарт (короткий і заслужений): Запуск OpenSearch на VPS з 2 ГБ — як усиновити хаскі в студії. Він буде робити перестановки, голосно.
Реальність однонодового режиму
Один вузол означає:
- Немає резервування. Погане оновлення, пошкоджений диск або випадкове видалення — повний простій, якщо у вас немає знімків.
- Немає балансування шардів. Якщо ваша стратегія шардів неправильна, ви живете з нею, поки не зробите реіндексацію.
- Продуктивність обмежена однією машиною. Це звучить очевидно, поки ви не зрозумієте, що індексування й запити конкурують за той же CPU, пам’ять і I/O.
OpenSearch все ще може бути правильним вибором на VPS. Але треба ставитися до VPS як до продакшн-серверу, а не до хобі-боксу.
Релевантність, аналайзери й «чому це не знаходить?»
Релевантність у MySQL: грубі ручки
MySQL FULLTEXT має скоринг, але він не створювався для ітеративного тонкого налагодження релевантності, яким займаються продуктові команди. Ви можете:
- Запити в природній мові й boolean-режим.
- Зважування по колонках шляхом розподілу й комбінування скорів (незручно, але можливо).
- Списки стоп-слів і мінімальна довжина слова (поведінка залежить від движка/версії).
Найбільша пастка: очікувати від MySQL FULLTEXT поведінки сучасного веб-пошуку. Він так не працює, і ви витратите час, намагаючись змусити його робити вигляд.
Релевантність в OpenSearch: більше можливостей, більше відповідальності
OpenSearch дає аналайзери (токенізація, зниження регістру, стемінг), мапінги на рівні поля, multi-fields (keyword + text) та гнучкість DSL запитів. Можна зробити пошук «розумним». Також можна поламати так, що нічого не буде знаходити, бо аналайзер не токенізує так, як ви очікували.
Операційний вплив: помилки мапінгу — це просто відмова
Помилка мапінгу в OpenSearch часто означає «реіндексувати весь світ». На VPS реіндексація може повалити вузол. Плануйте це:
- Версіоновані індекси й аліаси.
- Контрольовані джоби реіндексації з тротлінгом.
- Знімки перед великими змінами.
Зберігання та I/O: тихий вбивця пошуку на VPS
На папері і MySQL, і OpenSearch — «системи на диску». На практиці:
- MySQL чутливий до випадкових шаблонів I/O (важливий buffer pool hit rate), поведінки fsync журналів і конкуренції з іншими навантаженнями.
- OpenSearch чутливий до стійкої пропускної здатності записів під час індексування та мерджів, а також до затримок читання під час запитного навантаження — крім того, він сильно використовує OS page cache.
На VPS диск часто найменш чесний компонент. «NVMe» у описі плану все одно може означати розділений I/O, кредити на прискорення, шумних сусідів і тротлінг. Мерджі сегментів не зважають на маркетингові тексти.
Другий жарт: Хмара каже, що ваш диск «до 3 000 IOPS». Мій досвід каже: «до 3 000 IOPS, недовго, якщо місяць на ваш бік».
Три корпоративні міні-історії з передової
Інцидент через хибне припущення: «пошук — тільки читання, тож він не зашкодить продакшену»
Середня SaaS-команда додала OpenSearch для клієнтського пошуку. Вони ретельно тестували продуктивність запитів у стенді і були задоволені. Неправильне припущення: трафік пошуку — «тільки читання», отже безпечно колокувати на тій самій VPS-класі інстанції, що й прикладна база під час раннього запуску.
Перший тиждень був нормальний. Потім клієнт імпортував великий набір даних, команда ввімкнула near-real-time індексацію: refresh-и стали частими, індексування працювало безперервно, і мерджі почали накопичуватися. Латентність зросла, але не на endpoint пошуку першою. Вона проявилася у всьому: таймаути API, повільний логін, відстаючі бекграунд-завдання.
Причина не була в CPU. Це була конкуренція на диску. OpenSearch вузол робив стійкі записи плюс мерджі; MySQL на тій самій хості намагався fsync свій redo-журнал. Обидва робили «правильно». Разом вони були катастрофою.
Виправлення було болісно простим: розділити навантаження й додати явний моніторинг диска. Також вони збільшили інтервали refresh під час масового індексування і додали механізм backpressure в пайплайні інгесту.
Урок: «тільки читання» системи все одно пишуть — інколи агресивно. Особливо пошук.
Оптимізація, що дала зворотний ефект: кількість шардів як плацебо продуктивності
Одна інженерна група перейшла з MySQL FULLTEXT на OpenSearch, бо хотіла кращу релевантність і фасети. Ранні бенчмарки були змішаними. Хтось запропонував збільшити кількість шардів, щоб «паралелізувати» запити на одному вузлі. Звучало правдоподібно. Було неправильно для їхнього випадку.
Вони розбили помірний індекс на багато шардів. Пропускна здатність запитів не покращилася. Замість цього тиск на heap зріс: більше структур даних на рівні шардів, більше метаданих сегментів, більше кешів, що конкурують. Паузи GC стали помітні під спайками трафіку. Пропускна здатність індексування впала, бо мерджі відбувалися по більшій кількості маленьких шардів замість кількох ефективних.
Вони спробували «вирішити» це збільшенням heap. Це зменшило частоту GC, але збільшило час пауз при його викликах і обділило OS page cache пам’яттю. Запити знову стали повільними через збільшення читань з диска.
Вони відкотилися, реіндексувавши в меншу кількість шардів (і переключившись через аліас), зберігши помірний heap і залишивши пам’ять для page cache. Поліпшення продуктивності не прийшло від якоїсь магічної цифри; воно з’явилося, коли система перестала сама з собою боротися.
Урок: кількість шардів — не ручка продуктивності. Це рішення про розподіл даних з операційними наслідками.
Нудна, але правильна практика, що врятувала день: знімки й відпрацювання відновлення
Одна компанія тримала OpenSearch як самохостинговану однонодову службу для внутрішнього лог-пошуку та невеликої клієнтської функції пошуку. Ніщо епатажне. Вони мали одну дисципліновану звичку: щотижневі відпрацювання відновлення знімків на тестовій інстанції. Нікого це не надихало. Це був чек-лист, що завжди виглядав менш пріоритетним, ніж «реальна робота».
Одного дня після OS-патчу й ребуту вузол піднявся з проблемою файлової системи, що пошкодила частину сховища індексу. OpenSearch відмовився стартувати чисто. Команда не вчинила тривалих теоретичних дискусій. Вони оголосили вузол нездоровим, підготували нову інстанцію, встановили ту ж версію OpenSearch і відновили останній знімок.
У них усе ще було вікно простою, але воно вимірювалося в передбачуваному відрізку часу, а не у відкритій паніці. Якість пошуку збереглася, бо їхнє відпрацювання відновлення перевірило мапінги, шаблони й повний процес відновлення.
Урок: знімки без практики відновлення — це мрія. Нудна практика перетворює «втрату даних» на «дратівливий післяобідній інцидент».
Швидкий план діагностики
Коли «пошук повільний», треба швидко відповісти на питання: це CPU, пам’ять, диск чи дизайн запиту? Ось послідовність, що працює в реальному житті.
Перше: хост вмирає?
- Перевірте load, CPU steal (VPS!), тиск пам’яті, swap і насичення диска.
- Якщо хост нездоровий, не починайте налаштовувати запити. Вирішіть платформне вузьке місце.
Друге: сервіс стабільний?
- MySQL: з’єднання, потоки, повільні запити, статус InnoDB, поведінка buffer pool.
- OpenSearch: стан кластера (навіть однонодового), тиск JVM heap, паузи GC, thread pools, відхилені запити.
Третє: проблема індексування/refresh/мерджів?
- OpenSearch: перевірте швидкість індексування, інтервал refresh, мерджі і translog.
- MySQL: перевірте write amplification, fsync і співвідношення потраплянь у buffer pool.
Четверте: це запит?
- MySQL: EXPLAIN, індекси, повні перегляди, тимчасові таблиці, filesort.
- OpenSearch: профілюйте запити, аналізуйте токенізацію, перевіряйте мапінги і валідність DSL.
П’яте: вирішуйте, як обмежити шкоду
- Зменшіть радіус ураження: тротлінгуйте індексування, збільшіть інтервал refresh, відключіть дорогі агрегації, обмежте складність запитів або тимчасово погіршіть функції.
- Потім усуньте корінь проблеми.
Практичні завдання: команди, виводи, рішення (12+)
Ось реальні команди, які можна виконати на типовому Linux VPS з MySQL і/або OpenSearch. Кожна містить, що означає вивід і яке рішення далі приймати.
1) Перевірте CPU steal і load (реальність VPS)
cr0x@server:~$ mpstat -P ALL 1 3
Linux 6.5.0 (server) 12/30/2025 _x86_64_ (2 CPU)
12:01:00 PM CPU %usr %nice %sys %iowait %irq %soft %steal %idle
12:01:01 PM all 22.00 0.00 8.00 3.00 0.00 1.00 18.00 48.00
Значення: %steal на рівні 18% означає, що ваш гіпервізор відбирає CPU. Це не «ваш додаток повільний», це «VPS перепроданий».
Рішення: Якщо %steal стабільно >5–10% під час інцидентів — піднімайте/мігруйте хости перед мікрооптимізаціями.
2) Перевірте тиск пам’яті і використання swap
cr0x@server:~$ free -h
total used free shared buff/cache available
Mem: 3.8Gi 3.3Gi 120Mi 42Mi 420Mi 180Mi
Swap: 2.0Gi 1.6Gi 400Mi
Значення: У вас активний swap. OpenSearch + swap — повільна трагедія; MySQL + swap теж погано, тільки тихіше.
Рішення: Зменште heap OpenSearch, зменшіть навантаження, додайте RAM або перенесіть пошук поза вузол. Якщо swap ненульовий у steady state — у вас операційна проблема.
3) Перевірте затримки диска і насичення
cr0x@server:~$ iostat -xz 1 3
Linux 6.5.0 (server) 12/30/2025 _x86_64_ (2 CPU)
Device r/s w/s rMB/s wMB/s await %util
nvme0n1 55.0 220.0 2.1 18.4 42.30 97.00
Значення: 97% util і 42 ms await означають, що диск — вузьке місце. Пошукові вузли цього не люблять; MySQL теж.
Рішення: Тротлінгуйте індексування/мерджі (OpenSearch), зменшіть швидкість запису (обоє), перейдіть на кращий диск або розділіть сервіси.
4) Перевірте місце на файловій системі й тиск inode
cr0x@server:~$ df -h /var/lib
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 80G 74G 2.1G 98% /
Значення: 98% заповнення — це територія інциденту. OpenSearch потребує вільного простору для мерджів; MySQL — для логів і тимчасових таблиць.
Рішення: Вивільніть місце негайно. Для OpenSearch переконайтеся в достатньому запасі диску перед масовим індексуванням або реіндексом.
5) Перевірте події OOM на рівні ядра
cr0x@server:~$ journalctl -k -S -2h | tail -n 8
Dec 30 10:55:12 server kernel: Out of memory: Killed process 1920 (java) total-vm:6123456kB, anon-rss:2567890kB
Dec 30 10:55:12 server kernel: oom_reaper: reaped process 1920 (java), now anon-rss:0kB, file-rss:0kB
Значення: Ядро вбило OpenSearch (Java). Це не баг додатку; це питання потужності/лімітів.
Рішення: Зменшіть heap, вимкніть фічі, що їдять пам’ять, перейдіть на більший вузол або припиніть самохостинг пошуку на цьому VPS.
6) Перевірте, що MySQL використовує індекси (а не надії)
cr0x@server:~$ mysql -e "EXPLAIN SELECT id,title FROM posts WHERE title LIKE '%invoice%' ORDER BY created_at DESC LIMIT 20\G"
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: posts
partitions: NULL
type: ALL
possible_keys: idx_title,idx_created_at
key: NULL
key_len: NULL
ref: NULL
rows: 184233
filtered: 10.00
Extra: Using where; Using filesort
Значення: type: ALL і key: NULL означають повний перегляд таблиці. LIKE з провідним wildcard не використає звичайний індекс.
Рішення: Переробіть запит (пошук за префіксом), додайте FULLTEXT або прийміть, що потрібен пошуковий двигун.
7) Перевірте статус логування повільних запитів MySQL (і чи він не брешуть)
cr0x@server:~$ mysql -e "SHOW VARIABLES LIKE 'slow_query_log%'; SHOW VARIABLES LIKE 'long_query_time';"
+---------------------+-------+
| Variable_name | Value |
+---------------------+-------+
| slow_query_log | ON |
| slow_query_log_file | /var/log/mysql/mysql-slow.log |
+---------------------+-------+
+---------------------+-------+
| Variable_name | Value |
+---------------------+-------+
| long_query_time | 1.000 |
+---------------------+-------+
Значення: Логування повільних увімкнено з порогом 1 с. Добре: ви можете бачити реальність.
Рішення: Якщо slow log відключений у продакшені — увімкніть (обережно) і почніть вимірювати перед переписуванням систем.
8) Інспектуйте тиск InnoDB і поведінку buffer pool
cr0x@server:~$ mysql -e "SHOW ENGINE INNODB STATUS\G" | sed -n '1,40p'
=====================================
2025-12-30 12:05:01 INNODB MONITOR OUTPUT
=====================================
BUFFER POOL AND MEMORY
Total large memory allocated 2147483648
Buffer pool size 131072
Free buffers 8
Database pages 130000
Modified db pages 5200
Значення: Майже немає вільних буферів, багато модифікованих сторінок. Під записним навантаженням флашинг може домінувати над латентністю.
Рішення: Якщо диск насичений, зменшіть швидкість запису, налаштуйте флашинг або виведіть пошук з MySQL.
9) Перевірте стан кластера OpenSearch (навіть однонодовий потребує «зеленості» в розумінні)
cr0x@server:~$ curl -s localhost:9200/_cluster/health?pretty
{
"cluster_name" : "vps-search",
"status" : "yellow",
"timed_out" : false,
"number_of_nodes" : 1,
"number_of_data_nodes" : 1,
"active_primary_shards" : 12,
"active_shards" : 12,
"unassigned_shards" : 12
}
Значення: Yellow на одноноді зазвичай означає, що репліки не призначені (бо нема куди їх розмістити).
Рішення: На одноноді виставте replicas = 0 для індексів, де ви погоджуєтесь на відсутність резервування, і компенсуйте це знімками.
10) Перевірте тиск JVM heap в OpenSearch
cr0x@server:~$ curl -s localhost:9200/_nodes/stats/jvm?pretty | sed -n '1,40p'
{
"nodes" : {
"Q1" : {
"jvm" : {
"mem" : {
"heap_used_in_bytes" : 1638000000,
"heap_max_in_bytes" : 2147483648
}
}
}
}
}
Значення: ~76% використаного heap. Це нормально, поки не дійде до 90% під навантаженням і GC не почне довго сидіти на CPU.
Рішення: Якщо використання heap постійно високе — зменшіть кількість шардів, зменшіть використання fielddata, виправте мапінги (keyword vs text) або додайте пам’ять.
11) Перевірте відхилені запити (насичення thread pool)
cr0x@server:~$ curl -s localhost:9200/_nodes/stats/thread_pool?pretty | grep -E '"rejected"|"search"|"write"' -n | head
42: "search" : {
58: "rejected" : 120
210: "write" : {
226: "rejected" : 45
Значення: Відхилення сигналізують про перевантаження. OpenSearch каже вам, що не встигає.
Рішення: Додайте потужності, зменшіть паралелізм запитів/індексування або реалізуйте backpressure в аплікації. Не «перезапускайте швидше».
12) Інспектуйте мерджі OpenSearch (індикатор дискового шуму)
cr0x@server:~$ curl -s localhost:9200/_nodes/stats/indices/merges?pretty | sed -n '1,80p'
{
"nodes" : {
"Q1" : {
"indices" : {
"merges" : {
"current" : 7,
"current_docs" : 180000,
"current_size_in_bytes" : 2140000000,
"total_throttled_time_in_millis" : 850000
}
}
}
}
}
Значення: Багато поточних мерджів і великий throttled time вказують, що диск обмежує індексування і, можливо, запити теж.
Рішення: Збільште інтервал refresh під час масових завантажень, тротлінгуйте інгест, або перейдіть на швидший диск. Розгляньте меншу кількість шардів.
13) Валідуйте аналіз/токенізацію (чому результати не збігаються)
cr0x@server:~$ curl -s -H 'Content-Type: application/json' localhost:9200/myindex/_analyze -d '{"text":"ACME-Invoice_2021","analyzer":"standard"}'
{"tokens":[{"token":"acme","start_offset":0,"end_offset":4,"type":"<ALPHANUM>","position":0},{"token":"invoice_2021","start_offset":5,"end_offset":17,"type":"<ALPHANUM>","position":1}]}
Значення: Аналайзер залишив invoice_2021 як один токен. Якщо ви очікували «invoice» і «2021» окремо — ваш вибір аналайзера не підходить.
Рішення: Змініть аналайзери (або додайте підполя) перед тим, як налаштовувати запити. Реіндексуйте за потреби.
14) Профілюйте повільний OpenSearch-запит
cr0x@server:~$ curl -s -H 'Content-Type: application/json' localhost:9200/myindex/_search -d '{
"profile": true,
"query": { "match": { "body": "invoice acme" } },
"size": 10
}' | sed -n '1,60p'
{
"took" : 185,
"timed_out" : false,
"hits" : {
"total" : {
"value" : 12034,
"relation" : "eq"
}
}
}
Значення: took: 185 ms не катастрофа, але якщо p95 — секунди, дивіться секції профілю, щоб побачити, яка частина дорога (скоринг, term queries, fetch phase тощо).
Рішення: Якщо дорого fetch — зменшіть розмір stored fields/_source. Якщо дорого скоринг — змініть тип запиту або передфільтруйте ключовими словами.
15) Підтвердіть, що OpenSearch слухає і не флапає
cr0x@server:~$ systemctl status opensearch --no-pager
● opensearch.service - OpenSearch
Loaded: loaded (/lib/systemd/system/opensearch.service; enabled)
Active: active (running) since Mon 2025-12-30 11:40:11 UTC; 25min ago
Docs: man:opensearch
Main PID: 1920 (java)
Tasks: 132 (limit: 4620)
Memory: 2.7G
CPU: 18min 12.345s
Значення: Сервіс запущено, але пам’ять 2.7G на маленькому VPS може бути попереджувальним знаком, а не перемогою.
Рішення: Перевірте налаштування heap і загальну RAM. Якщо процес плюс OS cache перевищують можливості, ви прямуєте до OOM.
16) Перевірте з’єднання MySQL і тиск потоків
cr0x@server:~$ mysql -e "SHOW STATUS LIKE 'Threads_connected'; SHOW STATUS LIKE 'Max_used_connections';"
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| Threads_connected | 182 |
+-------------------+-------+
+----------------------+-------+
| Variable_name | Value |
+----------------------+-------+
| Max_used_connections | 240 |
+----------------------+-------+
Значення: Висока кількість потоків може означати проблеми з пулінгом з’єднань або завислий додаток. Також може вказувати на повільні запити, що накопичуються.
Рішення: Якщо потоки ростуть під час інцидентів — виправте латентність запитів і впровадьте адекватні ліміти пулінгу. Не просто збільшуйте max_connections.
Типові помилки: симптом → корінь → виправлення
1) «Пошук MySQL повільний»
Симптом: Ендпойнт пошуку підскакує CPU, запити тривають секунди, навантаження БД зростає.
Корінь: Використання LIKE '%term%' по великих текстових колонках, що викликає повні скани; або сортування/пагінація без індексів.
Виправлення: Використовуйте пошук за префіксом де можливо; додайте FULLTEXT; нормалізуйте пошукові поля; додайте композитні індекси для фільтрів + порядку сорту; розгляньте OpenSearch, якщо потрібна толерантність до підрядків/помилок.
2) «OpenSearch рандомно повільний»
Симптом: p95 латентності стрибає; іноді багатосекундні запити; CPU виглядає нормально.
Корінь: Паузи GC через високий тиск heap або затримки на диску під час мерджів і пропуски page cache.
Виправлення: Зменшіть кількість шардів; виправте мапінги, щоб уникнути вибуху fielddata; обмежте агрегації; забезпечте достатньо RAM для OS cache; перейдіть на кращий диск.
3) «Стан кластера жовтий і я панікую»
Симптом: OpenSearch однонодовий показує жовтий статус.
Корінь: Репліки налаштовані > 0 при наявності лише одного вузла.
Виправлення: Встановіть replicas = 0 для цих індексів і покладайтеся на знімки для відновлення.
4) «Індексація зробила пошук непридатним»
Симптом: Під час масового імпорту латентність пошуку зросла й відбуваються таймаути.
Корінь: Занадто частий refresh, мерджі домінують на диску, індексування конкурує з запитами на одному вузлі.
Виправлення: Збільшіть інтервал refresh під час масових імпортів; тротлінгуйте інгест; робіть bulk індексування в непіковий час; розгляньте окремі вузли для індексування й обслуговування (навіть якщо це означає «не робіть цього на одному VPS»).
5) «OpenSearch працював добре, поки диск не заповнився»
Симптом: Записи провалюються, кластер стає readonly, дивні помилки.
Корінь: Не враховано накладні витрати мерджів, знімків, росту translog і утримання. Немає планування порогів диску.
Виправлення: Тримайте значний вільний простір; вводьте політики утримання; моніторте диск; тестуйте розміри знімків; уникайте масових реіндексів без запасу простору.
6) «Результати пошуку не містять очевидних збігів»
Симптом: Користувачі наполягають, що дані є; пошук їх не знаходить.
Корінь: Невідповідність аналайзера/мапінгу (keyword vs text), несподівана токенізація, стоп-слова/мінімальна довжина токена або застарілий індекс через збої пайплайну синхронізації.
Виправлення: Перевірте аналіз через _analyze; інспектуйте мапінги; впровадьте надійну індексаційну трубу з ретрайами і dead-letter; побудуйте перевірки консистентності між MySQL і OpenSearch.
7) «Ми збільшили heap і стало гірше»
Симптом: Менше GC, але довші паузи; латентність запитів погіршилася; читання з диска зросло.
Корінь: Занадто великий heap відбирає RAM у OS page cache; Lucene виграє від файлового кешу ОС.
Виправлення: Тримайте heap помірним; залиште RAM для page cache; виправляйте проблеми шардів/мапінгів замість маскування їх підвищенням heap.
Чек-листи / покроковий план
План A: Залишитися на MySQL (і чесно описати вимоги)
- Чітко визначте функції пошуку. Лише ключові слова? Префікс? Фільтри? Налаштування релевантності? Помилки/синоніми? Якщо потрібні помилки і синоніми — припиніть прикидатися.
- Аудит запитів. Замініть патерни
%term%де можливо; переконайтеся, що фільтри і сортування мають індекси. - Спробуйте FULLTEXT там, де підходить. Особливо для «пошуку в заголовку/тексті».
- Вимірюйте за slow query log. Тримайте поріг стабільним (1–2 с) і щотижня переглядайте основні offenders.
- План потужності. Забезпечте DB достатнім buffer pool і запасом диску; ізолюйте від інших шумних навантажень.
- Встановіть очікування. Документуйте обмеження релевантності у продукті: «точні слова, не помилки», «без синонімів», «без стемінгу».
План B: Використовувати OpenSearch, але не робіть з VPS лабораторію
- Почніть з однонодового дев-інстансу. Перевірте аналайзери, мапінги і базові запити.
- Спроєктуйте модель документу. Денормалізуйте. Уникайте джойнів. Визначте, що — «text», а що — «keyword».
- Реалізуйте пайплайн індексування. Використовуйте чергу/outbox, щоб записи в MySQL і оновлення індексу були роз’єднані й повторювані.
- Використовуйте аліаси для плавної реіндексації. Версіонуйте індекси й міняйте аліаси.
- Визначте стратегію знімків. Плануйте знімки і тестуйте відновлення.
- Встановіть захисні обмеження. Обмежуйте складність запитів, розміри агрегацій і додавайте таймаути.
- Спостерігайте за всім. Heap, GC, затримки диска, відхилення thread pool, розподіл латентності запитів.
- Потім лише думайте про самохостинг на маленькому VPS. Якщо не можете зобов’язатися до знімків, моніторингу і відпрацювання відновлення — уникайте самохостингу OpenSearch на маленькому VPS.
План C: Якщо наполягаєте на OpenSearch на VPS, мінімально зробіть це
- Залиште достатньо RAM для OS cache (не виділяйте всю пам’ять під heap).
- Використовуйте швидке, передбачуване сховище. Якщо диск VPS має кредити на прискорення — припускайте, що ви зачепите межу.
- Встановіть replicas = 0 на одноноді і покладайтеся на знімки.
- Тримайте кількість шардів низькою. Ви не кластер; не грайтеся в один.
- Тротлінгуйте масове індексування і збільшуйте refresh під час імпортів.
- Моніторьте й алертьте: диск > 80%, heap > 85%, відхилення thread pool > 0, p95 запитів і події OOM.
- Запустіть відпрацювання відновлення перед тим, як воно знадобиться.
Питання й відповіді (FAQ)
1) Чи може MySQL FULLTEXT замінити OpenSearch для невеликого сайту?
Так, якщо «невеликий» також означає «простий». Пошук по ключовим словам у кількох полях з базовими фільтрами підійде. Як тільки знадобляться толерантність до помилок, синоніми і фасети — ви відчуєте стелю.
2) Чи OpenSearch — надмірність для VPS?
Часто так. Не тому, що він не запуститься, а тому, що без достатньої RAM і дискової продуктивності він не працюватиме надійно. На маленькому VPS ви в один мердж-шторм від поганого дня.
3) Чому OpenSearch потребує так багато пам’яті?
Це Lucene плюс JVM плюс кеші плюс накладні витрати на шарди. Крім того, Lucene сильно залежить від OS page cache для швидких читань. Якщо ви обділяєте OS cache, пошук швидко стає дисково-залежним.
4) Чи ставити heap OpenSearch на 75% від RAM?
Не на VPS, де вам важлива продуктивність. Потрібна пам’ять для OS page cache. Якщо heap занадто великий, ви міняєте «проблеми GC» на «проблеми диска», і ні те, ні інше — приємне.
5) Який найбільший прихований витрат додавання OpenSearch?
Синхронізація і правильність. MySQL — джерело істини; OpenSearch — індекс. Потрібен пайплайн, ретраї, backfills і спосіб виявити дрейф.
6) Чи можу я запускати MySQL і OpenSearch на одному VPS?
Можете, але для продакшену, ймовірно, не слід. Зазвичай це призводить до диск-контенції, а потім — до тиску пам’яті. Якщо все ж робите це, ізолюйте ресурси й моніторьте затримки диска як свою роботу.
7) Чому мій OpenSearch має жовтий стан на одноноді?
Репліки не можуть бути призначені, бо є лише один вузол. Встановіть number_of_replicas на 0 для таких індексів, потім покладайтеся на знімки для відновлення.
8) Коли має сенс залишатися на MySQL, навіть якщо релевантність не ідеальна?
Коли час роботи і простота важливіші за складність пошуку, і користувачі можуть миритися з «пошуком за точними словами». Багато продуктів тихо живуть у цій зоні і почуваються непогано.
9) Який найчистіший шлях міграції від MySQL-пошуку до OpenSearch?
Спочатку побудуйте пайплайн індексації, потім бекап/заповніть версіонований індекс, валідуйте результати і поступово перемикайте читання (або через feature flag). Тримайте можливість відкотитися на MySQL тимчасово.
Наступні кроки, які реально зробити цього тижня
Якщо ви все ще вирішуєте, зробіть це по порядку:
- Запишіть потрібні функції пошуку. Не «було б добре». Саме необхідні.
- Запустіть діагностику. Перевірте затримки диска, swap і CPU steal. Якщо VPS нездоровий — зупиніться й виправте це спочатку.
- Прототипуйте в MySQL. Спробуйте FULLTEXT або структурні префіксні патерни. Вимірюйте через slow logs і EXPLAIN.
- Прототипуйте релевантність OpenSearch на дев-боксі. Перевірте аналайзери/мапінги через
_analyze. Профілюйте найповільніші запити. - Рішення приймайте, виходячи з операційної реальності. Якщо не можете взяти на себе знімки, моніторинг і відпрацювання відновлення — уникайте самохостингу OpenSearch на маленькому VPS.
- Якщо все ж обрали OpenSearch: тримайте кількість шардів низькою, replicas = 0 на одноноді, тротлінгуйте індексування і моніторьте heap + диск як продакшн — тому що це продакшн.
Найкращий самохостингований пошук — той, що пережив поганий день. Вам не потрібна ідеальна модель релевантності, якщо сервіс просто лежить. І вам не потрібен красивий кластер, якщо він з’їдає VPS при реальному трафіку.