Tarantool: як заощадити мільйон доларів на базі даних на высоконагруженном проекті

Аникин Денис ( danikin, Mail.Ru
Денис Анікін

Сьогодні я розповім, як заощадити на базах даних величезні гроші, наприклад, мільйон доларів, як це зробили ми. Для початку питання: чому частіше використовують саме бази даних, а не файлики?

Бази даних – це сховище, більш структуроване, ніж файл, і має ряд деяких функцій, яких у файлу немає.



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


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



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

Що зазвичай в таких випадках роблять? Ще один сервер. Це реплікація. Тобто, по суті, ви доставляєте репліки рівно в такій кількості, щоб вони тримали вашу навантаження.



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

Далі у нас відбувається навантаження на запис.



І навантаження на запис, знову ж таки, досягає якоїсь межі, коли база даних вже не може її тримати. Допоможе в даному випадку реплікація? Немає. Яка б не була реплікація – майстер-слейв або майстер-майстер – вона не допоможе навантаженні на запису, тому що кожна запис повинна пройти на всі сервери. Навіть якщо там майстер-майстер, значить, скільки у вас йде сумарно запитів на весь ваш кластер, рівно стільки ж піде на кожен з серверів, тобто ви цим нічого не виграєте.

Які є ще ідеї?

Шардінг.



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

Є багато різних способів шардинга – можна різати по базах даних, можна різати по таблицях, можна різати всередині однієї таблиці. Способів – мільйон. Кожен використовує той спосіб, який йому зручніше.

По суті, у вас виходить такий двомірний кластер з баз даних, тобто у вас багато-багато шардов і у кожного декілька реплік. Ви доставляєте шарды, репліки, і варіюєте абсолютно будь-яке навантаження.

Але далі у вас відбувається проблема. Ваша наступна проблема – це ваш бос. Що йому в цьому може не подобатися? Начебто, все працює, все шардируется, реплікується, але що йому не подобається? Гроші. Йому все подобається, крім того, що це коштує дуже дорого, тому що ви доставляєте, доставляєте, доставляєте сервера, і він за це платить.

Ви йому говорите: «Чувак, ти не розумієш, у мене тут технологія, у мене тут шардінг, реплікація… Воно масштабується нескінченно – це дуже крута система».

Він вам каже: «так-Так, але ми втрачаємо гроші, якщо так піде далі, у нас просто не буде грошей, щоб купувати сервера баз даних. Нам доведеться закритися».

Що ж робити?

Насправді, навантаження на базу даних часто буває влаштована так, що якісь елементи даних на читання навантажуються дуже-дуже сильно – вони називаються «гарячі дані».

Кешування – це те, що частково вирішує нашу проблему.



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

Але… яка є проблема? Проблема неузгодженості – одна з найбільших проблем, яка є з кешем.



Це найперша проблема, яка тут же видно.

Дивіться, у вас додаток окремо пише в кеш, наприклад, в Memcached і окремо пише в базу. Тобто кеш не є реплікою бази, кеш є окремою сутністю. Тут намальовані стрілочки між кешем і базою, які насправді віртуальні, тобто ніякої реплікації між ними немає. Все робиться на рівні програми. Відповідно, це призводить до неузгодженості даних.



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



Тут картинка, щоб показати вам, що ви можете писати спочатку в базу, потім в кеш, або спочатку в кеш, потім в базу. В обох випадках у вас буде неузгодженість. Дивіться: ви пишете дані в кеш, після цього ваш додаток благополучно падає і в базу їх записати не встигає. Додаток піднімається і працює з тими даними, які вже в кеші, а в базі даних їх немає, але ніхто про це не знає. Коли кеш перезавантажується (а він коли-небудь перезавантажується), ви отримуєте з бази даних при порожньому кеші застарілі дані.



Як ні дивно, якщо писати в зворотному порядку – спочатку в базу, потім в кеш, буде рівно така ж проблема: записали в базу, додаток впало, в кеші старі дані, все працює зі старими даними, кеш перезавантажився, потягнув з бази нові дані, як би іншу гілку даних, які не цілісні, які не відповідають тим змінам, які були зроблені поверх іншої копії…

Тобто з кешем є як мінімум дві проблеми: немає цілісності даних, і вам все ще потрібен шардінг.

Які є ще проблеми з кешем? Які ви кешем внесли проблеми в той момент, коли ваш бос почав сердитися? Проблема така: кеш – це не база даних.



У вас до кешу була база даних, там були запити, транзакції, і всі ці штуки з кешу майже всі пропадають, у вас програма працює вже з кешем. Індекси і таблиці залишаються, не у всіх кешах є вторинні індекси, не у всіх кешах є таблиці, але якось криво-косо поверх key-value і те і інше можна підтримати, тому ми вважаємо, що ці властивості дотримані, а іншого немає.

Тепер з приводу проблеми нецілісності даних. Що з нею зробити? Як зробити так, щоб дані оновлювалися і в кеші і в базі цілісно? Розумний кеш.



Що таке розумний кеш? Насправді, багато хто з вас, швидше за все, це роблять – це, по суті, кеш, який сам спілкується з базою даних. Тобто це не окремий демон Memcached, а якийсь ваш самописний демон, який всередині себе всі кешує і сам пише в базу. Додаток в базу не пише, вона працює повністю через кеш.



Це вирішує проблему нецілісності, тому що кеш спочатку пише в базу, а потім пише у себе. Якщо він в базу не записав, віддав помилку – все добре. Це, звичайно, не добре, але дані цілісні. Після того, як кеш записав у базу, він у себе запише дані обов'язково, тому що він пише в пам'ять, такого, щоб дані не записалися в пам'ять практично ніколи не буває. Вірніше, буває, коли на сервері б'ється пам'ять і в цей момент все падає, і все одно ви втрачаєте весь кеш, принаймні, цілісність даних не втрачається.

Але є один кейс, коли навіть при такому розумному кеші дані можна втратити.



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

Це рідкісний кейс, але таке теж буває. Тобто розумний кеш не вирішує до кінця проблему нецілісності даних. І вам все ще потрібен шардінг.



Шардінг – це коли у вас є база даних, яка на одному сервері, і ви її просто ріжете. Ви ріжете на шматочки і ці шматочки укладаєте з різних серверів, фізичним серверів. Це зменшує навантаження на запис, тому що навантаження на запис йде на конкретний шматочок. Тобто у вас, по суті, всі CPU, наприклад, всіх серверів беруть участь в обробці цієї навантаження, а не CPU тільки одного сервера.

Шардінг у вас все одно залишається, а ви пам'ятаєте, що ваш бос не любить шардінг, тому що це дуже дорого, тому що багато-багато серверів і у кожного ще репліки.

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

Яка ще дуже неприємна проблема з кешем є? Холодний старт.



Це супер неприємна проблема, коли кеш піднімається з нуля, чистий, голий, без даних… Він марний, також як машина, повністю завалена снігом – у неї спочатку потрібно залізти, а її ще треба завести. Холодний старт повністю вбиває кеш, всі запити безпосередньо летять в базу даних.



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

Чотири проблеми з кешем:



Питання: як прогріти кеш? Кеш завжди повинен бути прогрітий, щоб він мав сенс. Через базу даних його прогрівати якось не акуратненько, тому що багато-багато реплік, і вони всі його гріють-гріють – надто дорого вони його гріють.

Персистенс – правильне слово. Кеш краще просто не охолоджувати, його потрібно персистить, тому що кеш – гарне і швидке рішення, за винятком кількох проблем, включаючи проблему холодного старту, включаючи те, що він іноді недогрет, так нехай він буде завжди зігрітий. Як, наприклад, в Сибіру деякі люди не глушать машини, щоб вони завжди були заведені, тому що інакше ти її потім не заведеш – приблизно з тієї ж опери.

 Який найпростіший спосіб персистента для кешу?



Це просто dump даних. Тобто, насправді, ми кожен раз, може бути, в хвилину, або, наприклад, раз в 5 хвилин дампим весь кеш повністю на диск, прямо цілком. Як вам це рішення? Відстій, тому що втрачається консистентним. Ви раз у 5 хвилин дампите, у вас сервер піднімається, і він втрачає своє зміна за 5 хвилин. Через базу даних їх прогрівати не можна, і навіть незрозуміло, звідки ці зміни взяти. І це не єдина проблема.



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

Як краще персистить кеш?

Лог. Треба просто вести лог.



Тобто навіщо нам дамп, давайте вести лог, давайте кожна зміна кешу буде логироваться на диск, кожне.

Якщо ви думаєте, що це повільно (завжди є думка, що кеш – це щось таке швидке, а коли там з'являється диск, то це стає якось повільно), так от, насправді, це не повільно, тому що навіть самий звичайний обертовий магнітний диск, не SSD, пише зі швидкістю 100 Мб/с, послідовно пише. Якщо розмір транзакції, скажімо, 100 байт, то це 1 млн транзакцій в секунду. Це неймовірна швидкість, яка задовольнить майже всіх присутніх в цій аудиторії, може бути навіть мене. Тому навіть один диск з цим завданням цілком справляється, але є інша проблема, що цей лог розростається дуже сильно, тому що, наприклад, йде 10 инсертов, потім 10 делитов тих же самих даних, вони повинні все луснути, а в балці вони не складаються. Або йде 100 апдейтів одного і того ж елемента даних, знову ж таки потрібен тільки останній, а в балці зберігаються всі. Як цю проблему вирішити? Сшепшоты робити.



Потрібно поєднати два цих методу – Dump і Log воєдино. Тобто ми раз у тиждень дампим, або коли нам цього хочеться, а все інше час тільки пишемо лог. У дамп ми ще запам'ятовуємо id останньої застосованої записи з лода або останнього запису з лода, яка в цьому снепшоте ще є. І коли у нас сервер перезавантажується, ми піднімаємо з диска dump, відновлюємо його в пам'яті і зверху накочується той шматок лода, який після цього запису. Все, кеш відновлений і прогрітий відразу.

До речі, цей прогрів відбувається швидше, ніж з бази даних. Ось такого роду прогрів при перезавантаженні потім повністю потрапить в диск, тому що це лінійне читання файла – 100 Мб/с. Навіть на магнітних дисках це дуже швидко.

Все, проблема холодного старту вирішена, але це тільки одна проблема, на жаль. Хоча кеш прогрітий, є ще 3 проблеми.



Давайте подумаємо, як вирішити перші дві – проблеми неконсистентності і шардинга?

Ідея використовувати кеш як базу даних – це дуже правильний напрямок. Дійсно, навіщо нам у цьому місці наша основна база даних? MySQL або Oracle – навіщо вона нам потрібна? Давайте подумаємо.

Потрібна, напевно, для 2-х речей:

  1. ми вважаємо, що база даних надійно зберігає дані, не як кеш, а надійно, тобто там, напевно, є якась магія;
  2. те, що в базі даних є реплікація. У кешей зазвичай реплікації немає. Відповідно, кеш сервера вийшов з ладу або просто перезавантажується, і поки він не підніме всі з диска, це швидше, ніж прогрів, але все одно буде down time – це теж погано, а реплікації там немає.
По першому пункту – надійне зберігання. Якщо розібратися, то що зберігає база даних? База даних зберігає гарячі і холодні дані – це по суті все, що вона зберігає. Гарячі дані – це зазвичай маленькі-маленькі і дуже-дуже гарячі, яким 10-100 тисяч RPS, а холодні дані – це такі великі і холодні, до них дуже мало звернень.



Завжди виходить так, що холодні дані більші, а гарячі – маленькі. Це закон життя.



По суті, ви реплицируете і шардируете вашу базу даних у великій кількості копій тільки лише для того, щоб обробляти цей маленький шматочок гарячих даних, тому що до залишку холодних даних немає великої кількості запитів, він нормально себе почуває. Але ви все це реплицируете тільки заради гарячих даних.

Навіщо ми робимо все це копіювання? Ми можемо, напевно, копіювати тільки гарячі дані?



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

І ваш бос все ще злий, тому що у вас все ще є шардінг.

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

Реплікації немає. Це, звичайно, погано, але давайте подумаємо, чому кеш не може бути первинним джерелом даних? Тому що немає реплікації? Добре, ми її зробимо.

Чому ще кеш не може бути первинним джерелом даних? Тому що він не володіє властивостями баз даних? Ми теж можемо це зробити, ми можемо всі ці властивості баз даних підтримати, і кеш буде ними володіти. Пам'ятайте картинку «Кеш – це не база даних»?



Кеш може бути базою даних. Він може володіти всіма цими властивостями.

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

Власне, що ми і зробили, і назвали цю базу даних Tarantool.



Ми розробили спеціальну базу даних для гарячих даних, яка є кешем, але при цьому у нього є персистенс, транзакції (такі ж, як у дорослих баз даних), реплікація, у нього навіть є збережені процедури. Тобто всі основні властивості бази даних у Tarantool'а є. І тому ми його використовуємо як первинне джерело для гарячих даних. Ми ці дані не дублюємо ніде. У нас Tarantool, у нього є репліка, ці дані бэкапятся, як і для будь-якої бази даних, але вони не дублюються ніде, ні в яких інших базах даних. Це такий завжди гарячий кеш з персистентом і з властивостями баз даних, тобто він всі ці проблеми вирішує.



По суті, нам не потрібні зараз всі ці сотні серверів з шардингом і з репліками, у нас просто наше завдання розділилася,



Ми просто використовуємо правильний інструмент для правильної завдання, тобто холодні дані зберігаються у сховищі. Наприклад, в SQL сховищах, які були створені десятки років тому саме для холодних даних, тому що тоді не було такої кількості запитів в секунду на дані, ніхто про це не думав. А гарячі дані зберігаються в тім сховищі, які спеціально задизайнено для гарячих даних в Tarantool.



Тут, в принципі, на слайді все написано – наш шлях, через який ми пройшли, але факт в тому, що для більшості завдань були достатні 2 инстанса всього Tarantool – один майстер, другий – репліка, тому що навантаження, яка йде на одну з баз даних, на один інстанси, вона, швидше за все, забезпечить всю вашу смугу навантаження, яка йшла раніше на весь ваш кластер SQL серверів.



І тут ще один психологічний момент – не дуже хочеться йти з затишного світу баз даних у незатишний новий світ кешей. В базі даних транзакції і т. д. Тоді, коли ваш бос злий, ви доставили кеш і якось відразу стало незатишно. І як раз Tarantool повертає цей затишок, тобто він, мало того, що вирішує проблеми неконсистанси і холодного старту. Він, як би, повертає вас в світ баз даних для гарячих даних.

Тепер кейс поштою Mail.ru.



Кейс був такий: нам потрібно було зберігати профілі користувачів. Профілі користувачів – це такі маленькі шматочки інформації – від 500 байт до 1 Кб на користувача. Ми спочатку стали для цієї справи використовувати MySQL. І почали дублювати всю навантаження на профілі, які у нас до цього лежали в старому сховище, на читання і запис на ферму з MySQL. Ми поставили ферму з 16-ти MySQL, всі пошардили заздалегідь і туди пустили навантаження. І виявилося, що на 1/8 від всієї нашої навантаження ці 16 серверів вперлися в полки. В основному, вони вперлися в полку по процесору.



Ми намагалися їх тюнить так і сяк, але по факту, все, що ми досягли – це 16 серверів на 1/8 навантаження, тобто на весь кластер, на всю навантаження знадобилося б 128 MySQL серверів. Ми подумали, що це дорогувато – це більше 1 млн. доларів. І ми просто поставили кілька серверів з Tarantool і туди пустили все навантаження. В тестових цілях, дублювали. І виявилося, що вони без проблем її тягнуть цілком. Навіть одного сервера було достатньо. Поставили просто 4, тому що майстер, репліка + ще пара, на всякий випадок. Ми зазвичай перезакладываемся завжди по навантаженні.

Власне, ось вона, економія мільйона доларів – просто ми в 60 разів знизили кількість серверів, яке нам потрібно. При цьому навіть користувачу стало краще, тому що кеші зазвичай працюють з кращим лэйтенси.



У нас сумарно в хмарі і пошті більше 120 инстансов Tarantool, серверів прямо з Tarantool, які використовуються для різних фіч, для дуже великої кількості функцій. Якщо б ми все це зберігали в MySQL або в будь-якому іншому SQL, то це були б сотні мільйонів доларів, просто, якщо екстраполювати наявні цифри.

Мораль всього мого виступу: потрібно використовувати правильний інструмент, для правильної роботи, тобто потрібно використовувати бази даних для холодних даних і кеш з персистентом, як Tarantool, для гарячих даних. І заощадити на цьому 1 млн. доларів.



Тут короткий Summary про те, що ми зробили. Ми шардировали, реплицировали, вперлися в гроші, стали кешувати, втратили консистенси і ще дещо… Зробили персистент кеш, це не база даних, але він може бути базою даних, відокремили гарячі від холодних даних, холодні – в MySQL, гарячі – в Tarantool, врятували 1 млн. доларів і, як бонус, отримали кращий юзер експириенс, тому що все стало швидше.

Контакти
» anikin@corp.mail.ru
» danikin
» Блог компанії Mail.ru

Ця доповідь — розшифровка одного з кращих виступів на конференції розробників високонавантажених систем HighLoad++. Зараз ми активно готуємо конференцію 2016 року — у цьому році HighLoad++ пройде в Сколково, 7 і 8 листопада.

В цьому році Денис подав заявку під назвою "Чому Tarantool такий оптимальний?". Обіцяється зрив покривів :) Костянтин Осипов так прокоментував цей доповідь — «він такий швидкий, тому що не робить НІЧОГО!» :) Послухаємо, поборемося!

Весь Програмний комітет HighLoad++ продовжує дружно рекомендувати платформу Tarantool. Це одна з чудових наших (російських) розробок і, як мінімум, заслуговує уваги.

Також деякі з цих матеріалів використовуються нами в навчальному онлайн-курс по розробці високонавантажених систем HighLoad.Guide — це ланцюжок спеціально підібраних листів, статей, матеріалів, відео. Вже зараз у нашому підручнику понад 30 унікальних матеріалів. Підключайтеся!
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.