Статьи

Apache Cassandra - ще одне NoSQL-сховище

Володимир Климонтович Співзасновник і технічний директор компанії GetIntent - стартапа в області інтернет-реклами

Володимир Климонтович: Доброго дня, мене звати Володимир Климонтович. Сьогодні я буду розповідати про Apache Cassandra. Це ще одне NoSQL сховище, яких зараз дуже багато. Є й ті, хто його використовує. Зокрема, його використовуємо ми.

Про що я буду розповідати?

Спочатку я скажу пару вступних слів про NoSQL: що це таке, і навіщо це потрібно або не потрібно використовувати.

Потім я розповім про таку систему зберігання даних, як Amazon Dynamo. Це пропріетарна система зберігання даних від Amazon, яку вони досить докладно описали в статті і яка лягла в основу архітектури Cassandra.

Після цього я трохи розповім про Google Big Table. Це теж пропріетарна система, тільки вже від Google, яка теж стала основою для архітектури Cassandra.

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

Також я згадаю про наш досвід використання Cassandra.

про NoSQL

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

Всі вважають, що його модно використовувати, а навіщо і чому - не особливо розуміють

Чому це погане і неприємне слово?

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

В даному випадку під SQL розуміється досить проста річ. Це MySQL зі сховищем InnoDB, Microsoft SQL і Oracle, які влаштовані більш-менш однаково. Це такі строкові бази даних з індексами на b-деревах. Зазвичай, коли в побуті говорять "NoSQL", розуміється, що це не MySQL.

Чомусь багато хто хоче використовувати якісь NoSQL-рішення замість MySQL. Якщо добре подивитися, то в багатьох випадках добре налаштований MySQL-кластер буде працювати, ніж Cassandra, HBase і так далі.

Але з MySQL-кластером є деякі проблеми. По-перше, щоб добре його налаштувати, потрібно добре розбиратися в MySQL, а з Cassandra, HBase та іншими мати справу набагато простіше. Відразу все працює, і не потрібно думати про налаштування. Загалом, тому всі їх використовують. Хоча це не завжди правильно.

Хоча це не завжди правильно

теорема CAP

Зовсім трохи теорії. Існує CAP-теорема (CAP: consistency, availability, partition tolerance). Напевно, багато хто про неї чули. Будемо розглядати систему з точки зору трьох параметрів.

• Перший - це несуперечливість (англ. Consistency). Це означає, що всі клієнти бачать один і той же набір даних в один і той же момент часу.

• Другий - це доступність (англ. Availability). На кожен запит буде дано якусь відповідь.

• Третій - стійкість до поділу (англ. Partition tolerance). Система буде продовжувати працювати, якщо ми розділимо мережеве підключення між будь-якими двома нодамі в системі. Можливо, це не дуже сувора формулювання. Але, я думаю, в цілому суть зрозуміла.

Стверджується, що не можна побудувати систему, яка задовольняє всім трьом параметрам. Можна вибрати тільки два. Зокрема, Cassandra - це стійка до поділу і доступна система. Немає несуперечності. В цьому є деякі плюси, пов'язані зі швидкістю і взагалі з тим, що вона трохи більш стійка до різного роду мережевим проблем. Немає несуперечності, хоча це так чи інакше вирішується в Cassandra. На рівні клієнта є налаштування, які дозволяють вирішити частину проблем, пов'язаних з відсутністю несуперечності. Про це я розповім.

NoSQL-сховища

На слайді перераховані NoSQL-сховища. Є MongoDB. Насправді, архітектурно це той же MySQL-кластер, в деякому сенсі, тільки з JSON-інтерфейсом.

Є Apache Hadoop. Це не зовсім NoSQL, оскільки це офлайн-сховище з можливістю написання логіки за допомогою MapReduce.

Є сімейство Google BigTable. Тут все буквально скопійовано з BigTable безпосередньо. Наприклад, HBase.

Є те, що скопійовано з Amazon Dynamo, рішення називається Riak. Є таке сховище. З ним я не працював.

Є Cassandra. Це Google BigTable + Amazon Dynamo. Взяли щось з одного, щось з іншого - отримали Cassandra.

конкретика

Трохи відволіклися, тепер давайте повернемося до конкретики. Щоб розповісти, як працює Cassandra, я перерахую основні архітектурні ідеї Amazon Dynamo і Google BigTable.

Amazon Dynamo, як я вже сказав раніше, компанія Amazon представила в 2007-му році в статті. Архітектура описана, але сама система платна - завантажити і запустити її не можна. Але можна прочитати статтю і написати те ж саме.

Основні ідеї Amazon Dynamo

Це не база даних. Це розподілений HashMap. В Amazon Dynamo є всього 2 операції - get (отримати щось по ключу) і отримати якесь значення (англ. Value) по ключу. Судячи за статтею, в Amazon це рішення використовується для таких речей, як управління сесіями, кошиком користувача, і так далі. Amazon Dynamo підходить для цілком нагальних речей, які трапляються під час роботи користувача з сайтом. Рішення Amazon Dynamo не призначене для того, щоб зберігати якісь історичні дані.

Що ще особливого в Amazon Dynamo? Це абсолютно розподілене рішення. У багатьох системах існує якась головна нода, яка є координатором, і якісь ведені Ноди, на яких зберігаються дані. В Amazon Dynamo все Ноди рівноправні, і немає ніякого координатора. Як це працює, докладніше розповім пізніше.

Власне, те, що називається Distributed Hash Table. Дуже схожа технологія Distributed Hash Table використовується в BitTorrent, вона всім знайома на практиці, по крайней мере.

Дуже схожа технологія Distributed Hash Table використовується в BitTorrent, вона всім знайома на практиці, по крайней мере

архітектура Cassandra

Основна ідея - це "кільце токенов" (англ. Token Ring). Що це таке? У нас є певна кількість серверів в кластері. Наприклад, їх 4, як на картинці. Ми кожного серверу призначимо токен. Це, грубо кажучи, певна кількість. Але спочатку ми визначаємо, які у нас взагалі, в принципі, бувають ключі.

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

Тут по картинці все досить зрозуміло. Наприклад, сервер T1 відповідає за токени від T1 включно до T2 і так далі. Це основна ідея архітектури в Cassandra і Dynamo.

Як влаштована реплікація?

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

Насправді, не знаю, як в Dynamo, але в Cassandra політика реплікації настроюється. Можна зробити дещо складніше.

Як відбувається запис в Cassandra?

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

Після того як довільний сервер отримав команду на запис в Amazon Dynamo, він стає координатором цієї операції. Він відповідає за те, щоб все було добре. Оскільки самі сервери вже "знають" про топології кластера, про те, як вони збудовані в кільці токенов, сервер сам вирішує, куди відправити команду на запис. Здійснюється запис на потрібні сервери: на один сервер реплікації, на два - в залежності від налаштувань реплікації кластера.

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

Він може вказати: «Я хочу вважати запис успішної, коли дані потрапили на один сервер реплікації, а з другим вже сам сервер розбереться (потрапили, не потрапили - неважливо)». Або він може сказати: «Запис успішна, коли дані потрапили на всі сервери реплікації». Відповідно, від цього залежить швидкість роботи. Це досить зручно. Якщо ми, наприклад, пишемо не надто цінне, можна, в принципі, писати швидко і не турбуватися про втрати.

Як відбувається читання?

Приблизно так само, як і запис. Знову відправляється команда безпідставного сервера в нашому кільці, в нашому кластері. Сервер стає координатором даної конкретної операції читання. Він, знаючи про те, де і як розташовані дані, починає опитувати сусідні сервери і віддає дані клієнта.

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

Google BigTable

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

BigTable - це рішення, трохи більше схоже на бази даних. Там є таблиці, які, з першого погляду, такі ж, як в MySQL. Але є і суттєва відмінність.

У кожного рядка може бути довільний набір колонок. Це не регламентується схемою. Я можу в будь-який момент до будь-якому рядку додати колонку з будь-яким ім'ям, з будь-яким значенням.

Я можу в будь-який момент до будь-якому рядку додати колонку з будь-яким ім'ям, з будь-яким значенням

Розглянемо приклад ...

Наприклад, ми будемо писати соціальну мережу (зараз все це роблять) і зберігати відносини між людьми (друг, не друг, з якої категорії друг та інше). Як ми будемо це робити в BigTable? Дуже просто. У нас ключем буде ім'я користувача. Припустимо, у нас є 3 користувача. Колонка у нас теж буде зберігати ім'я користувача. Відповідно, пара «ключ + певне значення» у конкретної колонки позначає відношення дружби між цими людьми (взаємно, навзамін і так далі).

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

Які операції виконуються на таких таблицях?

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

Або навпаки - значення колонки по всім ключам

Як зберігає дані Google BigTable?

Трохи інакше, ніж Amazon Dynamo. Власне кажучи, тут на зображенні все більш-менш зрозуміло намальовано. У нас є ведучий (англ. Master) вузол, який "знає" про структуру мереж і є координатором кожної з операцій на читання і запис.

Є сервери, зазначені на картинці як "Tablet servers". Це такі відомі (англ. Slave) сервери, на яких зберігаються дані. Як відбуваються читання та запис? Клієнт спочатку посилає ведучому вузлу запит про те, що він хоче отримати, які дані по яким ключам. Ведучий вузол "знає", де зберігаються дані і відповідає клієнтові, які конкретно ведені сервери опитати. Після цього клієнт звертається за даними вже безпосередньо до цих серверів.

Досить хороша система, оскільки весь трафік, пов'язаний з даними, не йде через провідний вузол, а розподілений між цими серверами.

Як зберігаються дані всередині ведених серверів?

Є таке поняття, як Memtable. Це просто таблиця даних в пам'яті. Туди потрапляють най-най "свіжі" записи. Якщо ми записали 100 ключів на якийсь ведений сервер, вони потраплять в пам'ять. На диск вони не скинуться. Крім того, що вони потрапили в Memtable, вони дублюються журналом оновлень (англ. Commit log) на диску. Навіщо це потрібно? Якщо раптом у нас сервер "впаде", ми можемо його перезапустити, і Memtable заповниться назад.

Припустимо, наша таблиця Memtable заповнена (зазвичай це якось налаштовується). У неї у нас потрапив мільйон ключів або тисяча ключів. Дані пора скидати на диск. Дані скидаються в таку структуру даних, яка називається SSTable. Це такий файл, який дозволяє знайти дані по конкретному ключу за лінійний час. Але це незмінний файл. Ми його один раз записали, всередину вже вставити щось не вийде.

Дані приходять, заповнюють Memtable. Кожен раз Memtable з якоюсь періодичністю скидається на диск. Ми отримуємо багато-багато SSTable-файлів.

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

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

Коли SSTable-файлів виявляється дуже багато, відбувається процес ущільнення (англ. Compaction). У фоновому режимі маленькі SSTable-файли складаються в один. Їх виходить мало, але вони великі за розміром.

Приблизно так все це влаштовано.

Причому тут Cassandra?

Зараз розкажу. Розробники рішення Cassandra взяли ідеї з архітектур BigTable і Dynamo. Я розповім, які саме, а також трохи більш докладно поясню, як влаштовано читання та інші операції в Cassandra.

Я розповім, які саме, а також трохи більш докладно поясню, як влаштовано читання та інші операції в Cassandra

Що взято з Dynamo?

З Dynamo взята структура кільця токенов. Повна распределенность, у нас немає ніякого ведучого вузла. Клієнт "спілкується" з усіма даними. Також реалізований алгоритм читання і запису.

Що взято з BigTable?

З BigTable розробники рішення Cassandra взяли модель даних. На відміну від Amazon Dynamo, Cassandra не просто розподілений хеш. Там є колонки, ключі і значення у колонок. Взяли локальну структуру даних на серверах. У Dynamo, наскільки я пам'ятаю, все влаштовано трохи не так, як в BigTable. Проте, для Cassandra використовували локальну структуру на серверах, як у BigTable.

Тут є та ж таблиця Memtable, куди тимчасово потрапляють дані, той же журнал оновлень, де вони дублюються на випадок падіння Ноди. Є абсолютно такий же файл SSTable, взятий у Google, з тим же алгоритмом.

Абсолютно таке ж фонове ущільнення SSTable, "схлопування" маленьких файлів SSTable в один. Така ж модель даних. Замість put і get можна задавати складні запити.

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

Як Cassandra виглядає зовні?

Почну з термінології. Є таке поняття, як кластер. Це установка рішення Cassandra, це все наші Ноди. Для цього кластера налаштовуються різні параметри: як розподілені дані, яка у нас є структура і так далі.

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

Сімейство колонки (англ. Column family). Це те ж саме, що таблиця в термінах MySQL та інших стандартних баз даних.

Ще є суперколонкі (англ. Super columns). Не дуже хотів про них розповідати, але, мабуть, треба, оскільки в Cassandra їх позиціонують як важливу опцію.

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

приклад

Ми зберігаємо відносини користувачів соціальної мережі, які дружать або не дружать. Вони можуть або дружити, або можуть бути підписані на оновлення сторінок один одного. Ми маємо такі суперколонкі: "Друзі" і "Передплатники". Усередині них є колонки, в яких ми зберігаємо дані.

Які операції підтримуються в Cassandra?

• Є операція "mutation", яка здійснюється, коли ми вказуємо, що з даного ключу в дану колонку ми повинні отримати дане значення. Чому операція називається "mutation"? Тому що Cassandra "не розрізняє" операції insert, update і інші. У нас є тільки така операція. Немає даних - значить, вони з'являться. Були старі дані - значить, вони перезапишуть.

• Є операція "get". Ця операція дозволяє отримати значення з даного ключу і по даній колонці.

• Є операція "multi_get". Те ж саме з багатьох ключів.

• Є операція "get_slice". Те ж саме, тільки можна відфільтрувати колонки, наприклад, по інтервалу або за належністю до якогось безлічі.

• Є операція "get_range_slices", коли ми отримуємо щось не по конкретному ключу, а по інтервалу ключів.

Це практично всі операції. Їх небагато, але трохи більше, ніж у Amazon Dynamo.

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

На жаль, на відміну від MySQL і якихось стандартних речей, це потрібно знати прикладного розробнику. Без цього розробка не йде, і незрозуміло, чому. Доводиться знати.

Ідея така ж, як і в Amazon Dynamo. Команда йде на довільний сервер в кластері, і цей сервер стає координатором даної конкретної операції. Сервер знаходить потрібні репліки в залежності від настройки, яка називається "ReplicationStrategy". Про неї буде трохи пізніше.

Якщо у нас "ReplicationFactor" дорівнює одиниці, тобто ми зберігаємо всі на одному сервері. Ми знаємо ключ і знаємо сервер, який відповідає за даний інтервал токенов.

Відразу после того як прийшла команда записи на сервер, дані зберігаються локально в деякій табліці. Цей сигнал назівається "Hinted handoff". ВІН теж є в Amazon Dynamo. Навіщо це потрібно? На випадок, если у нас якісь сервери недоступні. Нам необов'язково з помилкою завершувати операцію запису, тому що клієнтові може бути не дуже важливо, щоб дані розклалися по всіх серверів відразу. Тоді сервер збереже дані локально, скаже, що все окей. Коли сервери знову почнуть функціонувати, дані будуть розподілені по всіх серверів реплікації.

Критерій успішності записи визначається клієнтом. Таких критеріїв 4.

Перший критерій називається "ANY". Це означає, що запис визнається успішною, як тільки дані потрапляють на початковий сервер. Неважливо, розподілилися вони по серверам реплікації чи ні. Головне, що вони потрапили на наш довільний сервер, який є координатором.

Є критерій "ONE". Як тільки дані потрапили хоча б на 1 сервер реплікації, вважається, що все записано успішно.

Є критерій "QUORUM". Це "ReplicationFactor" поділити навпіл + 1.

Є критерій "ALL". Запис визнається успішною після "розкладання" даних по всіх серверів реплікації, які відповідальні за даний ключ.

Запис визнається успішною після розкладання даних по всіх серверів реплікації, які відповідальні за даний ключ

Як влаштована реплікація?

Розглянемо простий випадок. У нас є 4 сервери, кожен відповідає за свій токен. У нас є сервер, який відповідає за нульовою. Припустимо, що у нас ключі від одного до ста. Сервер відповідає за нульовою, 25-й, 50-й, 75-й.

Точно так же, як в Amazon Dynamo, кожен сервер відповідає за два інтервалу ключів. Перший сервер відповідає як за інтервал ключів від 0 до 25 (за свій інтервал), так і за інтервал ключів попереднього сервера. Думаю, по картинці все більш-менш зрозуміло.

Все це працює дуже добре, і не тільки всередині одного дата-центру. Одна з відмінних рис Cassandra - то, що це рішення може добре реплицировать дані між дата-центрами. Чому це важливо? Це може стати в нагоді тим, хто використовує Amazon, Esety та інші схожі сервіси. Наприклад, у того ж Amazon є така властивість, що цілий дата-центр може впасти. Якщо ми хочемо, щоб система працювала добре, непогано було б, щоб вона продовжувала працювати з другого дата-центру.

Як влаштована реплікація між дата-центрами?

Це свого роду хак до структури Cassandra. Ідея така. У Cassandra є така вимога: у кожного сервера повинен бути свій унікальний токен.

Добре, давайте зробимо так. Призначимо серверів такі маркери. Виглядає це досить штучно. Перший сервер отримує 0, другий отримує 1. "Розфарбуємо" ці сервери в різні кольори. Припустимо, "білі" сервери у нас знаходяться в одному дата-центрі, а "сірі" - в іншому.

Налаштуємо реплікацію наступним чином: копія потрапляє на якийсь сервер, а наступна копія йде на сервер через один. Наприклад, згідно з таким налаштуванням, ключ із значенням 42 потрапить на 25-й і на 26-й сервер.

Після цього можна "білі" ноди "відправити" в один дата-центр, "сірі" - в інший. Так буде працювати реплікація. Команда буде йти на довільний сервер. Оскільки копії "знають", де вони знаходяться, вони всі будуть здійснювати реплікацію між собою.

Як працює запис?

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

Всередині це працює так. Використана ідея з Memtable, сховищем нових записів. Туди потрапляють всі записи відразу. Є настроюється критерій "flush". Налаштувати його можна по таймаут, за розміром і якось ще (не пам'ятаю).

Memtable буде періодично "скидатися" на диск і перетворюватися в SSTable. SSTable - це файл з даними, такий же, як у Google. Насправді, це 3 файлу: дані, index + bloomfilter. Періодично буде відбуватися ущільнення SSTable. Всі маленькі файли, які поступово скидалися на диск, будуть у фоновому режимі збиратися у великі файли.

Всі маленькі файли, які поступово скидалися на диск, будуть у фоновому режимі збиратися у великі файли

Як влаштовані команди читання?

Команду читання, як і у Amazon Dynamo, в Cassandra обробляє довільний сервер в кластері. Який - вирішує клієнт. Клієнти бувають різні. Зазвичай сервер визначається на основі завантаженості і близькості по мережі.

Сервер визначає найближчий сервер реплікації на основі настройки, яка називається "snitch". Є різні опції. Перша - "Simple", класти просто на випадковий сервер реплікації, найближча в кільці токенов. Є настройка, яка заснована на топології мережі. Сервер буде класти не на довільний сервер реплікації, а на той, який ближче по мережі. Є динамічний snitch. Це означає, що сервер буде запам'ятовувати статистику часу відповіді сервера реплікації.

Так само, як і в випадку із записом, клієнт визначає критерій успішності читання. Є 3 настройки. Перша: дані є хоча б на одному сервері реплікації. Другий рівень - QUORUM: дані є на кількості серверів реплікації навпіл + 1. ALL: дані є на всіх серверах реплікації.

Навіщо це потрібно? Чому не вважати успішним відповідь, коли дані є на одному сервері реплікації? Здавалося б, яка нам різниця. Насправді, різниця є. Тільки переконавшись, що ми отримали дані з усіх серверів реплікації, ми досягаємо несуперечності. Якщо ми отримали дані з усіх серверів реплікації, відповідно, вони всюди однакові. Відповідно, ми отримали те, що клієнт туди записав. Якщо ми задовольнилися одним сервером реплікації, може бути, все сталося по-іншому. Припустимо, якийсь інший клієнт записав дані, вони потрапили на сервер реплікації № 1, а ми прочитали дані з сервера реплікації № 2 і отримали щось суперечливе.

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

Як вирішуються конфлікти в Cassandra?

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

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

Що ще є в Cassandra?

Є кешування ключів (рівень "key cache"). Це означає, що дані, насправді, будуть читатися ні з диска. Дані про положення в ключі файлу будуть читатися з пам'яті. Після цього буде читатися файл.

Є кешування рядків (англ. Row cache). Рядки в таблицях будуть кешуватися в пам'яті. Читання буде відбуватися не з диска. Принцип простий. Самі "гарячі" ключі будуть зберігатися в кеші. Але розробник Cassandra не рекомендує використовувати цю можливість (навіть, скоріше, не розробники, а якісь великі користувачі).

Кажуть, що є mmap, такий же кеш операційної системи, яку використовує Cassandra. Краще не використовувати ніяке кешування і покладатися на нього.

індекси

Ще одна марна можливість в Cassandra - це індекси. Можна вказати, що дана колонка індексована, і отримати, припустимо, все рядки в сімействі колонки, у яких дана колонка дорівнює даним значенням. Є така можливість, при цьому очевидно, що її досить складно реалізувати в такий розподіленої системі. Потрібно опитати всі сервери в системі, щоб щось знайти, оскільки дані секціонованими по ключам.

У Cassandra є досить дивне обмеження (не знаю, чому воно таке). Можна шукати колонки тільки за чітким значенням. Припустимо, знайти всі ключі, у яких значення колонки більше, ніж заданий, неможливо.

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

Тобто досить марна можливість

Масштабування в Cassandra

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

Як було сказано, у кожного сервера є певний токен, за який він відповідає. Є операція "move token". Можна перепризначити токен. Вказати, що цей сервер відповідає тепер за іншою діапазон. Це можна зробити прямо "на ходу". Відповідно, топологія, картинка з розподілом токенов і діапазонів зміниться. При цьому міграція відбуватиметься у фоновому режимі. Для клієнтів це все буде досить прозоро.

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

Ця картинка ілюструє ідею масштабування. Найпростіше масштабироваться в 2 рази, як і всюди. Наприклад, у нас є 4 сервери, кожен з яких відповідає за токен 0, 25, 50 і 75 (кожен за свій діапазон). Між ними дуже легко вставити сервери, які позначені на зображенні помаранчевим, кожному призначити токен, який знаходиться рівно посередині між сусідами.

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

Все переноситься в фоновому режимі і продовжує працювати, як раніше

Що добре в Cassandra, а що погано?

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

Cassandra забезпечує легкість адміністрування, по крайней мере, для початку. Ми просто купуємо або орендуємо сервери (Cassandra - це рівно один сервер), і "розповідаємо" сервера для кожного його сусіда, призначаємо йому цей токен. Сервера самі "дізнаються" один про одного через механізм під назвою "gossip", і все починає працювати.

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

недоліки Cassandra

Їх дуже багато, і вони досить неприємні.

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

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

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

Ще проблема. Ущільнення файлів SSTable, про який я розповідав ( "схлопування" маленьких таблиць в великі), хоча і відбувається у фоновому режимі, все-таки істотно витрачає ресурси серверів і уповільнює роботу. Це видно. Запити починають працювати повільно. Це означає, що десь відбувається ущільнення.

Власне кажучи до недоліків варто віднести і протокол комунікації, який не вміє передавати дані поточно. Це такий протокол "free" від "Facebook". Він дуже неприємний з технічної точки зору, і з ним важко працювати.

Наш досвід використання Cassandra я опишу буквально в двох словах.

Що ми робимо?

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

Який у нас обсяг даних?

Один з наших клієнтів: десь 1 мільярд подій в місяць і 100 мільйонів унікальних користувачів.

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

Які запити ми обробляємо в Cassandra?

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

Питання № 3 - стандартне запитання для Cassandra. Питання № 1 і 2 Cassandra не вміє вирішувати, тому що це агрегаційні запити. Нам не просто треба отримати невеликі дані. Нам буде потрібно отримати дуже-дуже багато даних, їх якось стиснути, щось з ними зробити і дати маленький відповідь.

Чим нам подобається Cassandra?

Дуже швидка запис, автоматично все групується по user id. Немає ніяких проблем з дублікатами, на відміну від якого-небудь Hadoop. Коли ми записуємо там логи, ми завжди боїмося записати одну і ту ж сходинку два рази. У Cassandra таких проблем немає. Завантажили один і той же файл два рази - дані перезаписати.

Наші проблеми

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

Не можна винести логіку на свої Ноди, ЦП яких зазвичай простоює

Як ми вирішуємо проблеми?

Проблему агрегації ми думали вирішити за допомогою Hadoop, MapReduce. Hadoop вміє запускати "MapReduce jobs" на даних Cassandra. Також думали про власні рішення.

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

У нас написано своє рішення. Це такі демони, які є на кожному з серверів Cassandra і отримують запити: наприклад, сагрегіровать дані по такому-то критерієм. Демони агрегує їх і видають клієнту вже сагрегірованние дані.

трохи цифр

У нас поки досить тестовий проект. Даних мало - всього 600 гігабайт. Тому кластер теж дуже маленький - 3 сервера конфігурації m1.large в Amazon, 2 ЦП і 7,5 гігабайт оперативної пам'яті, хоча оперативна пам'ять в Cassandra не дуже добре використовується.

Запис - десь 12 тисяч подій в секунду, читання - десь 9 тисяч. Знову ж таки, це дивно для сховищ, тому що зазвичай все буває навпаки.

Напевно, все. Задавайте питання, якщо що цікаво.

Питання та відповіді

Питання із залу: Чи могли б ви детальніше розповісти про конфігурацію нод в кластерах? Що вони "знають" один про одного - кожен знає тільки про сусіда? Як відбувається маршрутизація запиту?

Володимир Климонтович: Так, про одне тільки. У Ноди є сусіди, про яких вона знає. У параметр конфігурації можна написати рівно одного сусіда. Нода "запитає" сусіда про його сусідів, і так вони один про одного "знають". Це механізм "auto discovery".

Питання із залу: Тобто реально можлива ситуація, коли доведеться опитати всі сервера, щоб зрозуміти, хто в підсумку повинен обробити запит?

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

Питання із залу: Я правильно зрозумів, що цей прикордонний токен (нульовий, 25-й) зберігається тільки на одному кластері завжди? Він не реплицируется?

Володимир Климонтович: Ні, реплицируется.

Питання із залу: Там був 0, 1, 26 ...

Володимир Климонтович: Токен визначає виключно те, за який інтервал відповідає даний сервер, до якого цей токен приставлений.

Репліка із залу: Я зрозумів, що "сірі" відповідали з 1-го по 25-й.

Володимир Климонтович: Да-да-да, саме так.

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

Володимир Климонтович: Нульовий токен?

Репліка із залу: Так.

Володимир Климонтович: Або на одному, якщо ReplicationFactor = 1, або на ньому ж і на наступному, якщо ReplicationFactor = 2. Або на ньому ж і через один, якщо у нас такі настройки.

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

Володимир Климонтович: Чому? Будуть.

Репліка із залу: Я так зрозумів, що нульовий, 25-й - в одному дата-центрі, 1-й, 26-й - в іншому дата-центрі.

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

Про що я буду розповідати?
Чому це погане і неприємне слово?
Що ще особливого в Amazon Dynamo?
Що це таке?
Як влаштована реплікація?
Як відбувається запис в Cassandra?
Як відбувається читання?
Як ми будемо це робити в BigTable?
Які операції виконуються на таких таблицях?
Як зберігає дані Google BigTable?

Новости