Тестування продуктивності NoSQL БД

  1. Зміст статті Кілька років тому виявилося, що SQL раптово застарів. І почали з'являтися і множитися...
  2. Як проводився тест
  3. INFO
  4. налаштовуємо SSD
  5. результати
  6. Висновки
  7. PS

Зміст статті

Кілька років тому виявилося, що SQL раптово застарів. І почали з'являтися і множитися NoSQL-рішення, відкинули мову SQL і реляційну модель зберігання даних. Основні аргументи на підтримку такого підходу: можливість роботи з великими даними (ті самі Big Data), зберігання даних в самих екзотичних структурах і, найголовніше, можливість все це робити дуже швидко. Давай подивимося, наскільки це виходить у найпопулярніших представників світу NoSQL.

За рахунок чого досягається швидкість в NoSQL? В першу чергу, це наслідок зовсім іншої парадигми зберігання даних. Парсинг і трансляція SQL-запитів, робота оптимізатора, об'єднання таблиць та інше сильно збільшують час відповіді. Якщо взяти і викинути всі ці шари, спростити запити, читати з диска прямо в мережу або зберігати всі дані в оперативній пам'яті, то можна виграти в швидкості. Зменшується як час обробки кожного запиту, так і кількість запитів в секунду. Так з'явилися key-value БД, найтиповішим і широко відомим представником яких є memcached. Так, цей кеш, широко застосовуваний в веб-додатках для прискорення доступу до даних, теж є NoSQL.

типи NoSQL

Можна виділити чотири основні категорії NoSQL-систем:

  • Ключ - значення (key-value). Велика хеш-таблиця, де припустимі тільки операції запису і читання даних по ключу.
  • Колоночного (column). Таблиці, з рядками і колонками. Ось тільки на відміну від SQL кількість колонок від рядка до рядка може бути змінним, а загальне число колонок може вимірюватися мільярдами. Також кожен рядок має унікальний ключ. Можна розглядати таку структуру даних як хеш-таблицю хеш-таблиці, першим ключем є ключ рядки, другим - ім'я колонки. За підтримки вторинних індексів можливі вибірки за значенням в колонці, а не тільки по ключу рядки.
  • Документо-орієнтовані (document-oriented). Колекції структурованих документів. Можлива вибірка по різних полях документа, а також модифікація частин документа. До цієї ж категорії можна віднести пошукові движки, які є індексами, але, як правило, не зберігають самі документи.
  • Графові (graph). Спеціально призначені для зберігання математичних графів: вузлів і зв'язків між ними. Як правило, дозволяють задавати для вузлів і зв'язків ще й набір довільних атрибутів і вибирати вузли та зв'язку за цими атрибутам. Підтримують алгоритми обходу графів і побудови маршрутів.

Для тесту ми взяли представників перших трьох категорій:

  • Aerospike (в дівоцтві Citrusleaf). Дуже швидка key-value БД (а з версії 3.0 - ще й частково документо-орієнтована). В явному вигляді підтримує SSD, використовує їх безпосередньо, без файлової системи, як блокові пристрої. Створювалася для ринку онлайн-реклами (в тому числі для так званого RTB - Real Time Bidding), де потрібні швидкі і великі кеши даних.
  • Couchbase. Виник як симбіоз проектів CouchDB і Membase і є, таким чином, послідовником memcached, тобто key-value БД (хоча знову-таки з версії 2.0 з'явилася підтримка JSON об'єктів-документів). Від memcached в ньому залишилися сумісність на рівні протоколу доступу і прагнення все зберігати в ОЗУ. Відрізняється від memcached персистентного і кластерний, ці нові фічі написані на Erlang.
  • Cassandra. Найстарша СУБД в списку. Виникла в надрах Facebook і була відпущена під крильце Apache в 2008 році. Є стовпчик-орієнтованої БД, ідейним спадкоємцем Google BigTable.
  • MongoDB. Вельми відома документо-орієнтована БД. Мабуть, родоначальник жанру документо-орієнтованих NoSQL БД. Документи являють собою JSON (точніше, BSON) об'єкти. Створювалася для потреб веб-додатків.

Як проводився тест

У розпорядженні у нас було чотири серверних машинки. У кожній: восьміядерний Xeon, 32 Гб ОЗУ, чотири интеловских SSD по 120 Гб кожен.

Тестували ми за допомогою YCSB (Yahoo! Cloud Serving Benchmark). Це спеціальний бенчмарк, випущений командою Yahoo! Research в 2010 році під ліцензією Apache. Бенчмарк спеціально створений для тестування NoSQL баз даних. І зараз він залишається єдиним і досить популярним бенчмарком для NoSQL, фактично стандартом. Написаний, до речі, на Java. Ми додали до оригінального YCSB драйвер для Aerospike, злегка відновили драйвер для MongoDB, а також кілька подшаманили з висновком результатів.

INFO

Крім YCSB, тестувати продуктивність NoSQL БД можна за допомогою, наприклад, JMeter.

Для створення навантаження на наш маленький кластер потрібно вісім клієнтських машин. За чотирьохядерному i5 і 4 Гб ОЗУ на кожній. Одного (і двох, і трьох, і чотирьох ...) клієнтів виявилося недостатньо, щоб завантажити кластер. Може здатися дивним, але факт.

Все це ворушилося в одній гигабитной локальної мережі. Мабуть, було б цікавіше в десятігігабітной мережі, але такого заліза у нас не було. Цікавіше, тому що, коли кількість операцій в секунду починає вимірюватися сотнями тисяч, ми утикаємося в мережу. При пропускної здатності в гігабіт на секунду (10 ^ 9 біт / c) мережа може пропустити кілобайтних пакетів (~ 10 ^ 4 біт) лише близько 100 000 (10 ^ 5) штук. Тобто отримуємо лише 100k операцій в секунду. А нам взагалі-то хотілося отримати мільйон :).

Мережеві карти теж мають значення. Правильні серверні мережевої мають кілька каналів введення-виведення, відповідно, кожен з власним перериванням. Ось тільки за замовчуванням в Лінуксі всі ці переривання призначені на одне ядро ​​процесора. Тільки хлопці з Aerospike перейнялися цією тонкістю, і їх скрипти налаштування БД розкидають переривання мережевих карт по ядрах процесора. Подивитися переривання мережевих карт і то, як вони розподілені по ядрах процесора, можна, наприклад, такою командою: «cat / proc / interrupts | grep eth ».

Окремо варто поговорити про SSD. Ми хотіли протестувати роботу NoSQL БД саме на твердотільних накопичувачах, щоб зрозуміти, чи дійсно ці диски того варті, тобто дають хорошу продуктивність. Тому намагалися налаштувати SSD правильно. Детальніше про це можна прочитати на врізки.

налаштовуємо SSD

Зокрема, SSD вимагають дій, які називаються неперекладним словом overprovisioning. Справа в тому, що в SSD присутній шар трансляції адрес. Адреси блоків, видатні операційній системі, зовсім не відповідають фізичним блокам у флеш-пам'яті. Як ти знаєш, число циклів перезапису у флеш-пам'яті обмежена. До того ж операція запису складається з двох етапів: стирання (часто - відразу декількох блоків) і власне записи. Тому, для забезпечення довговічності накопичувача (рівномірного зносу) і хорошій швидкості запису, контролер диска чергує фізичні блоки пам'яті при запису. Коли операційна система пише блок по якомусь адресою, фізично запис відбувається на якийсь чистий вільний блок пам'яті, а старий блок позначається як доступний для подальшого (фонового) стирання. Для всіх цих маніпуляцій контролера диска потрібні вільні блоки, чим більше, тим краще. Заповнений на 100% SSD може працювати досить повільно.

Вільні блоки можуть вийти декількома способами. Можна за допомогою команди hdparm (з ключем '-N') вказати кількість секторів диска, видимих ​​операційною системою. Решта буде в повному розпорядженні контролера. Однак це працює не на всякому залозі (в AWS EC2, наприклад, не працює). Інший спосіб - залишити не зайняте розділами місце на диску (маються на увазі розділи, створювані, наприклад, fdisk). Контролер досить розумний, щоб задіяти це місце. Третій спосіб - використовувати файлові системи і версії ядра, які вміють повідомляти контролеру про вільних блоках. Це та сама команда TRIM. На нашому залозі вистачило hdparm, ми віддали на розтерзання контролеру 20% від загального обсягу дисків.

Для SSD важливий також планувальник вводу-виводу. Це така підсистема ядра, яка групує і змінювати порядок операції введення-виведення (в основному записи на диск) з метою підвищити ефективність. За замовчуванням линукс використовує CFQ (Completely Fair Queuing), який намагається переставити операції записи так, щоб записати якомога більше блоків послідовно. Це добре для звичайних обертових (так і кажуть - spinning :)) дисків, тому що для них швидкість лінійного доступу помітно вище доступу до випадкових блокам (головки потрібно переміщати). Але для SSD лінійна і випадковий запис - однаково ефективні (теоретично), і робота CFQ тільки вносить зайві затримки. Тому для SSD-дисків потрібно включати інші планувальники, наприклад NOOP, який просто виконує команди введення-виведення в тому порядку, в якому вони надійшли. Переключити планувальник можна, наприклад, такою командою: «echo noop> / sys / block / sda / queue / scheduler», де sda - твій диск. Справедливості заради варто згадати, що свіжі ядра самі вміють визначати SSD-накопичувачі і включати для них правильний планувальник.

Будь-яка СУБД любить інтенсивно писати на диск, а також інтенсивно читати. А Linux дуже любить робити read-ahead, попередній виклик даних, - в надії, що, раз ти прочитав цей блок, ти захочеш прочитати і кілька наступних. Однак з СУБД, і особливо при випадковому читанні (а цей якраз наш варіант), цим надіям не судилося збутися. В результаті маємо нікому не потрібне читання і використання пам'яті. Розробники MongoDB рекомендують по можливості зменшити значення read-ahead. Зробити це можна командою «blockdev --setra 8 / dev / sda», де sda - твій диск.

Будь-яка СУБД любить відкривати багато-багато файлів. Тому необхідно помітно збільшити ліміти nofile (кількість доступних файлових дескрипторів для користувача) в файлі /etc/security/limits.conf на значення сильно більше 4k.

Також виник цікавий питання: як використовувати чотири SSD? Якщо Aerospike просто підключає їх як сховища і якось самостійно чергує доступ до дисків, то інші БД мають на увазі, що у них є лише один каталог з даними. (В деяких випадках можна вказати і кілька каталогів, але це не передбачає чергування даних між ними.) Довелося створювати RAID 0 (з чергуванням) за допомогою утиліти mdadm. Я вважаю, що можна було б пограти з LVM, але виробники СУБД описують тільки використання mdadm.

Природно, на всіх машинах кластера (як серверних, так і клієнтських) годинник повинні бути синхронізовані з допомогою ntpd. Ntpdate тут не годиться, тому що потрібно більша точність синхронізації. Для всіх розподілених систем життєво важливо, щоб час між вузлами було синхронізовано. Наприклад, Cassandra і Aerospike зберігають час зміни запису. І якщо на різних вузлах кластера знайдуться записи з різних таймстамп, то переможе той запис, який новіше.

Самі NoSQL БД налаштовувалися наступним чином. Бралася конфігурація з коробки, і застосовувалися всі рекомендації, описані в документації і стосуються досягнення максимальної продуктивності. У складних випадках ми зв'язувалися з розробниками БД. Найчастіше рекомендації стосувалися підстроювань під кількість ядер і обсяг ОЗУ.

Найпростіше налаштовується Couchbase. У сучасному світі практично-консоль. Досить запустити сервіс на всіх вузлах кластера. Потім на одному з вузлів створити bucket ( «кошик» для ключів-значень) і додати інші вузли в кластер. Все через веб-інтерфейс. Особливо хитрих параметрів настройки у нього немає.

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

Найскладніше з MongoDB. У інших БД всі вузли рівнозначні. У Mongo це не так. Ми хотіли поставити все БД по можливості в однакові умови і виставити у всіх replication factor в 2. Це означає, що в кластері має бути дві копії даних, для надійності та швидкості. В інших БД replication factor - це лише настройка сховища даних (або «кошика», або «сімейства колонок»). У MongoDB кількість копій даних визначається структурою кластера. Грамотно налаштувати кластер MongoDB можна, тільки двічі прочитавши офіційну документацію, присвячену цьому :). Кажучи коротко, нам потрібні shards or replica-sets. Шарден (ну ти напевно чув термін «шардінг») - це підмножини всього набору даних, а також вузли кластера, де кожна підмножина буде зберігається. Репліка-сети - це термін MongoDB, що позначає набір вузлів кластера, що зберігають однакові копії даних. В репліка-сеті є головний вузол, який виконує операції записи, і вторинні вузли, на які здійснюється реплікація даних з головного вузла. У разі збоїв роль головного вузла може бути перенесена на інший вузол репліка-сету. Для нашого випадку (чотири сервера і бажання зберігати дві копії даних) виходить, що нам потрібно два Шардена, кожен з яких представляє собою репліка-сет з двох серверів з даними. Крім того, в кожен репліка-сет потрібно додати так званий арбітр, який не зберігає дані, а потрібен для участі у виборах нового головного вузла. Число вузлів в репліка-сеті для правильних виборів має бути непарною. Ще потрібна маленька конфігураційна БД, в якій буде зберігатися інформація про Шарден і про те, які діапазони даних на якому Шарден зберігаються. Технічно це теж MongoDB, тільки (в порівнянні з основними даними) дуже маленька. Арбітри та конфігураційну БД ми розмістили на клієнтських машинах. І ще на кожному клієнті потрібно запустити демон mongos (mongo switch), який буде звертатися до конфігураційної БД і маршрутизировать запити від кожного клієнта між Шардена.

І ще на кожному клієнті потрібно запустити демон mongos (mongo switch), який буде звертатися до конфігураційної БД і маршрутизировать запити від кожного клієнта між Шардена

розгортання MongoDB

У кожної NoSQL БД свій унікальний спосіб представлення даних і допустимих операцій над ними. Тому YCSB пішов по шляху максимального узагальнення будь-яких БД (включаючи і SQL).

Набір даних, якими оперує YCSB, - це ключ і значення. Ключ - це рядок, в яку входить 64-бітний хеш. Таким чином, сам YCSB, знаючи загальна кількість записів в БД, звертається до них по целочисленному індексу, а для БД безліч ключів виглядає цілком випадковим. Значення - десяток полів випадкових бінарних даних. За замовчуванням YCSB генерує записи кілобайтний розміру, але, як ти пам'ятаєш, в гигабитной мережі це обмежує нас лише в 100k операцій в секунду. Тому в тестах ми зменшили розмір одного запису до 100 байт.

Операції над цими даними YCSB здійснює теж найпростіші: вставка нового запису з ключем і випадковими даними, читання записи по ключу, оновлення записи по ключу. Ніяких об'єднань таблиць (та й взагалі мається на увазі лише одна «таблиця»). Ніяких вибірок по вторинним ключам. Ніяких множинних вибірок за умовою (єдина перевірка - збіг первинного ключа). Це дуже примітивно, але зате може бути вироблено в будь-який БД.

Безпосередньо перед тестуванням БД потрібно наповнити даними. Робиться це самим YCSB. По суті, це навантаження, що складається лише з операцій вставки. Ми експериментували з двома наборами даних. Перший гарантовано поміщається в оперативну пам'ять вузлів кластера, 50 мільйонів записів, приблизно 5 Гб чистих даних. Другий гарантовано не поміщається в ОЗУ, 500 мільйонів записів, приблизно 50 Гб чистих даних.

Сам тест - виконання певного набору операцій - проводиться під навантаженням різного типу. Важливим параметром є співвідношення операцій - скільки повинно бути читань, а скільки оновлень. Ми використовували два типи: інтенсивна запис (Heavy Write, 50% читань і 50% оновлень) і в основному читання (Mostly Read, 95% читань і 5% оновлень). Яку операцію виконати, кожен раз вибирається випадково, відсотки визначають ймовірність вибору операції.

YCSB може використовувати різні алгоритми вибору запису (ключа) для виконання операції. Це може бути рівномірний розподіл (будь-який ключ з усього безлічі даних може бути обраний з однаковою ймовірністю), експоненціальне розподіл (ключі «на початку» набору даних будуть вибиратися значно частіше) і деякі інші. Але типовим розподілом команда Yahoo вибрала так зване zipfian. Це рівномірний розподіл, в якому, однак, окремі ключі (невеликий відсоток від загальної кількості ключів) вибираються значно частіше, ніж інші. Це симулює популярні записи, скажімо в блогах.

Це симулює популярні записи, скажімо в блогах

Як працює YCSB

YCSB стартує з декількома потоками, запускаючи цикл виконання операцій в кожному з них, і все це на одній машині. Маючи лише чотири ядра на одній клієнтській машині, досить сумно намагатися запускати там більше чотирьох потоків. Тому ми запускали YCSB на восьми клієнтських машинах одночасно. Для автоматизації запуску ми використовували fabric і cron (точніше, at). Невеликий скрипт на Python формує необхідні для запуску YCSB команди на кожному клієнті, ці команди поміщаються в чергу at на один і той же час на найближчу хвилину в майбутньому на кожному клієнті. Потім спрацьовує at, і YCSB успішно (або не дуже, якщо помилилися в параметрах) запускається в один і той же час на всіх восьми клієнтів. Щоб зібрати результати (лог файли YCSB), знову використовується fabric.

результати

Отже, вихідні результати - це логи YCSB, з кожного клієнта. Виглядають ці логи приблизно так (показаний фінальний шматочок файлу):

[READ], Operations, 1187363 [READ], Retries, 0 [READ], AverageLatency (us), 3876.5493619053314 [READ], MinLatency (us), 162 [READ], MaxLatency (us), 278190 [READ], 95thPercentileLatency ( ms), 12 [READ], 99thPercentileLatency (ms), 22 [READ], Return = 0, 1187363 [OVERALL], Reconnections, 0.0 [OVERALL], RunTime (ms), 303574.0 [OVERALL], Operations, 1249984.0 [OVERALL] , Throughput (ops / sec), 4117.5594747903315

Як бачиш, тут є кількість операцій певного типу (в даному прикладі - читання), середня, мінімальна і максимальна затримки, затримка, в яку вклалися 95 і 99% операцій, кількість успішних операцій (код повернення 0), загальний час тесту, загальна кількість всіх операцій і середня кількість операцій в секунду. Нас найбільше цікавить середня затримка (AverageLatency) і кількість операцій в секунду (Throughput).

За допомогою чергового скрипта на Python дані з купи балок збирали в табличку, а по табличці будували красиві графіки.

Швидкість вставки на SSD Швидкість вставки на SSD   Швидкість вставки в пам'ять   Продуктивність при інтенсивній записи на SSD   Продуктивність при інтенсивній записи в пам'яті   Продуктивність при 95% читання на SSD   Продуктивність при 95% читання в пам'яті Швидкість вставки в пам'ять Продуктивність при інтенсивній записи на SSD Продуктивність при інтенсивній записи в пам'яті Продуктивність при 95% читання на SSD Продуктивність при 95% читання в пам'яті

Висновки

NoSQL БД розділилися на дві групи: швидкі і повільні. Швидкими, як, власне, і очікувалося, виявилися key-value БД. Aerospike і Couchbase сильно випереджають суперників.

Aerospike дійсно дуже швидка БД. І нам майже вийшло дійти до мільйона операцій в секунду (на даних в пам'яті). Aerospike вельми непогано працює і на SSD, особливо якщо враховувати, що Aerospike в цьому режимі не використовує кешування даних в пам'яті, а на кожен запит звертається до диска. Значить, в Aerospike дійсно можна помістити велику кількість даних (поки вистачить дисків, а не ОЗУ).

Couchbase швидкий, але швидкий тільки на операціях в пам'яті. На графіках з тестами SSD показана швидкість роботи Couchbase на обсязі даних лише трохи більше обсягу ОЗУ - всього 200 мільйонів записів. Це помітно менше 500 мільйонів, з якими тестувалися інші БД. У Couchbase просто не вдалося вставити більше записів, він відмовлявся витісняти кеш даних з пам'яті на диск і припиняв запис (операції записи завершувалися з помилками). Це хороший кеш, але лише для даних, що містяться в ОЗУ.

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

MongoDB досить повільна на запис, але щодо швидка на читання. Якщо дані (а точніше, те, що називають working set - набір актуальних даних, до яких постійно йде звернення) не поміщаються в пам'ять, вона сильно сповільнюється (а це саме те, що відбувається при тестуванні YCSB). Також потрібно пам'ятати, що у MongoDB існує глобальна блокування на читання / запис, що може доставити проблем при дуже високому навантаженні. В цілому ж MongoDB - хороша БД для вебу.

PS

Давай трохи відвернемося від питань продуктивності і подивимося на те, як будуть розвиватися далі SQL- і NoSQL-рішення. Насправді те, що ми бачимо зараз, - це повторення добре знайомої історії. Все це вже було в шістдесятих і сімдесятих роках двадцятого століття: до реляційних БД існували ієрархічні, об'єктні та інші, та інші. Потім захотілося стандартизації, і з'явився SQL. І все серйозні СУБД, кожна з яких підтримувала свою власну мову запитів і API, переключилися на SQL. Мова запитів і реляційна модель стали стандартом. Цікаво, що зараз теж намагаються прищепити SQL до NoSQL, що призводить до створення як обгорток поверх існуючих NoSQL, так і абсолютно нових БД, які називають NewSQL.

Якщо NoSQL вирішили відмовитися від «важкої спадщини» SQL, переглянути підходи до зберігання даних і створили абсолютно нові рішення, то терміном NewSQL називають рух по «відродженню» SQL. Взявши ідеї з NoSQL, хлопці відтворили SQL-бази на новому рівні. Наприклад, в світі NewSQL часто зустрічаються БД зі зберіганням даних в пам'яті, але з повноцінними SQL-запитами, об'єднаннями таблиць та іншими звичними речами. Щоб все ж зберігати багато даних, в ці БД вбудовують механізми шардінга.

До NewSQL зараховують VoltDB, TokuDB, MemDB і інші. Запам'ятай ці імена, можливо, скоро про них теж будуть говорити на кожній ІТ-конференції.

За рахунок чого досягається швидкість в NoSQL?
Також виник цікавий питання: як використовувати чотири SSD?

Дополнительная информация

rss
Карта