Оптимізація навантаження на Highload-проект за допомогою ElasticSearch

Привіт, Хабре! Мене звуть Максим Васильєв, я працюю аналітиком та менеджером проектів у FINCH. Сьогодні я хотів би розповісти, як за допомогою ElasticSearch ми змогли обробити 15 млн запитів за 6 хвилин і оптимізувати щоденні навантаження на сайті одного з наших клієнтів. На жаль, доведеться обійтися без імен, оскільки у нас NDA сподіваємося, що зміст статті від цього не постраждає. Let`s go.

Як влаштований проект

На нашому бекенді ми створюємо сервіси, які забезпечують працездатність сайтів та мобільного застосування нашого клієнта. Загальну структуру можна побачити на схемі:

Оптимізація навантаження на Highload-проект за допомогою ElasticSearch

У процесі роботи ми обробляємо велику кількість транзакцій: покупок, виплат, операцій з балансами користувачів, за якими зберігаємо багато логів, а також імпортуємо та експортуємо ці дані у зовнішні системи.

Також йдуть і зворотні процеси, коли ми отримуємо дані від клієнта та передаємо їх користувачем. Крім цього ще існують процеси роботи з платежами та бонусними програмами.

Коротка передісторія

Спочатку як єдине сховище даних ми використовували PostgreSQL. Його стандартні переваги для СУБД: наявність транзакцій, розвинена мова вибірки даних, широкий інструментарій для інтеграції; у поєднанні з гарною продуктивністю досить довго задовольняли наші потреби.

Ми зберігали в Postgres всі дані: від транзакцій до новин. Але кількість користувачів зростала, а разом із ним і кількість запитів.

Для розуміння, річна кількість сеансів у 2017 році тільки на десктопному сайті – 131 млн. За 2018 – 125 млн. 2019 знову 130 млн. Додайте туди ще 100-200 млн від мобільної версії сайту та мобільного додатку, і ви отримаєте колосальну кількість запитів.

Зі зростанням проекту Postgres перестав справлятися з навантаженням, ми не встигали — з'явилася велика кількість різноманітних запитів, під які ми не змогли створити достатню кількість індексів.

Ми розуміли, що є необхідність в інших сховищах даних, які забезпечили б наші потреби і зняли навантаження з PostgreSQL. Як можливі варіанти розглядали Elasticsearch і MongoDB. Останній програвав за наступними пунктами:

  1. Повільна швидкість індексації зі зростанням обсягу даних індексах. У Elastic швидкість залежить від обсягу даних.
  2. Немає повнотекстового пошуку

Так ми вибрали собі Elastic і приготувалися до переходу.

Перехід на Elastic

1. Ми почали перехід із сервісу пошуку точок продажів. У нашого клієнта сумарно є близько 70 000 точок продажу, і при цьому потрібно кілька типів пошуку на сайті та у додатку:

  • Текстовий пошук за назвою населеного пункту
  • Геопошук у заданому радіусі від якоїсь точки. Наприклад, якщо користувач хоче побачити якісь точки продажу найближче до його будинку.
  • Пошук за заданим квадратом – користувач окреслює квадрат на карті, і йому показуються всі точки у цьому радіусі.
  • Пошук за додатковими фільтрами. Точки продажу відрізняються одна від одної за асортиментом

Якщо говорити про організацію, то у Postgres у нас лежить джерело даних як по карті, так і по новинах, а в Elastic робляться Snapshot'и від оригінальних даних. Справа в тому, що спочатку Postgres не справлявся з пошуком за всіма критеріями. Мало того, що було багато індексів, вони могли ще й перетинатися, тому планувальник Postgres губився і не розумів, який індекс йому використовувати.

2. Наступним на черзі був розділ новин. На сайті щодня з'являються публікації, щоб користувач не загубився в потоці інформації, потрібно сортувати дані перед видачею. Для цього і потрібен пошук: на сайті можна шукати за текстовим збігом, а заразом підключати додаткові фільтри, оскільки вони також зроблені через Elastic.

3. Потім ми перенесли обробку транзакцій. Користувачі можуть купувати певний товар на сайті та брати участь у розіграші призів. Після таких покупок, ми обробляємо велику кількість даних, особливо у вихідні та свята. Для порівняння, якщо у звичайні дні кількість покупок становить десь 1,5-2 млн., то у свята цифра може досягати 53 млн. грн.

При цьому дані потрібно обробити за мінімальний час – користувачі не люблять чекати на результат кілька днів. Через Postgres таких термінів ніяк не досягнеш - ми часто отримували блокування, і поки ми обробляли всі запити, користувачі не могли перевірити, чи отримали вони призи чи ні. Це не дуже приємно для бізнесу, тому ми перенесли обробку до Elasticsearch.

Періодичність

Наразі оновлення налаштовані подієво, за такими умовами:

  1. Точки продажу. Як тільки до нас надходять дані із зовнішнього джерела, ми одразу ж запускаємо оновлення.
  2. Новини. Як тільки на сайті редагують якусь новину, вона автоматично вирушає до Elastic.

Тут ще раз варто сказати про плюси Elastic. У Postgres під час відправки запиту, потрібно чекати, поки він чесно обробить всі записи. У Elastic можна відправити 10 тис. записів, і відразу почати працювати, не чекаючи, поки записи розійдуться по Shards. Звичайно, якийсь Shard або Replica можуть не побачити дані відразу, але незабаром все буде доступно.

Способи інтеграції

Є 2 способи інтеграції з Elastic:

  1. Через нативний клієнт з TCP. Нативний драйвер поступово вимирає: його припиняють підтримувати, на ньому дуже незручний синтаксис. Тому ми його практично не використовуємо і намагаємось повністю відмовитися від нього.
  2. Через HTTP інтерфейс, в якому можна використовувати як запити JSON, так і синтаксис Lucene. Останнє - текстовий двигун, який використовує Elastic. У такому варіанті ми отримуємо можливість Batch через JSON-запити з HTTP. Саме цей варіант ми намагаємось використати.

Завдяки інтерфейсу HTTP ми можемо використовувати бібліотеки, які дають асинхронну реалізацію HTTP клієнта. Ми можемо використовувати перевагу Batch та асинхронного API, що в результаті дає високу продуктивність, яка дуже допомогла у дні великої акції (про це нижче)

Небагато цифр для порівняння:

  • Збереження користувачів, які отримали призи в Postgres у 20 потоків без угруповань: 460713 записів за 42 секунди
  • Elastic + реактивний клієнт на 10 потоків + batch на 1000 елементів: 596749 записів за 11 секунд
  • Elastic + реактивний клієнт на 10 потоків + batch на 1000 елементів: 23801684 записів за 4 хвилини

Зараз ми написали менеджер запитів з HTTP, який будує JSON як Batch/не Batch і відправляє через будь-який HTTP клієнт незалежно від бібліотеки. Також можна вибирати синхронно або асинхронно надсилати запити.

У деяких інтеграціях ми досі використовуємо офіційний transport client, але це лише питання найближчого рефакторингу. При цьому для обробки використовують власний клієнт, побудований на базі Spring WebClient.

Оптимізація навантаження на Highload-проект за допомогою ElasticSearch

Велика акція

Щороку на проекті проходить велика акція для користувачів - це той самий Highload, тому що в цей час ми працюємо з десятками мільйонів користувачів одночасно.

Зазвичай піки навантажень бувають у святкові дні, але ця акція зовсім інший рівень. Позаторік у день акції ми продали 27 580 890 одиниць товару. Дані оброблялися понад півгодини, що викликало незручність у користувачів. Користувачі отримали призи за участь, але зрозуміло, що процес треба прискорювати.

На початку 2019 року ми вирішили, що потрібний ElasticSearch. Цілий рік ми організовували обробку отриманих даних в Elastic та їх видачу в api мобільного додатка та сайту. У результаті наступного року під час акції ми обробили 15 131 783 записів за 6 хвилин.

Оскільки бажаючих купити товар і брати участь у розіграші призів в акціях у нас дуже багато, то це є тимчасовий захід. Зараз ми надсилаємо актуальну інформацію до Elastic, але надалі плануємо архівну інформацію за минулі місяці переносити до Postgres як перманентне сховище. Щоб не засмічувати індекс Elastic, який також має свої обмеження.

Висновок/висновки

На даний момент ми перенесли на Elastic усі сервіси, які хотіли і на цьому поки що зробили паузу. Зараз ми поверх основного персистентного сховища в Postgres будуємо індекс в Elastic, який приймає на себе навантаження користувача.

Надалі ми плануємо переносити сервіси, якщо ми розуміємо, що запит даних стає занадто різноманітним і шукається за необмеженою кількістю колонок. Це вже завдання не для Postgres.

Якщо нам буде потрібен повнотекстовий пошук у функціоналі або якщо у нас з'явиться багато різноманітних критеріїв пошуку, то ми вже знаємо, що це потрібно перекладати в Elastic.

⌘⌘⌘

Дякую, що прочитали. Якщо у вас в компанії також використовуються ElasticSearch і є власні кейси продажу, то розкажіть. Цікаво буде дізнатися як у інших 🙂

Джерело: habr.com

Додати коментар або відгук