Касандра. Як не памерці, калі ведаеш толькі Oracle

Прывітанне, Хабр.

Мяне клічуць Міша Бутрымаў, я хацеў бы хацеў крыху расказаць пра Cassandra. Мой аповяд будзе карысны тым, хто ніколі не сутыкаўся з NoSQL-базамі, - у яе ёсць вельмі шмат асаблівасцяў рэалізацыі і падводных камянёў, пра якія трэба ведаць. І калі акрамя Oracle ці любой іншай рэляцыйнай базы вы нічога не бачылі, гэтыя рэчы выратуюць вам жыццё.

Чым добрая Cassandra? Гэта NoSQL-база дадзеных, cпраектаваная без адзінай кропкі адмовы, якая добра маштабуецца. Калі вам трэба дадаць пару тэрабайт для якой-небудзь базы, вы проста дадаеце ноды ў кольца. Пашырыць яе на яшчэ адзін дата-цэнтр? Дадаеце ноды ў кластар. Павялічыць апрацоўваны RPS? Дадаеце ноды ў кластар. У адваротны бок таксама працуе.

Касандра. Як не памерці, калі ведаеш толькі Oracle

У чым яшчэ яна добрая? У тым, каб апрацоўваць шмат запытаў. Але шмат - гэта колькі? 10, 20, 30, 40 тысяч запытаў у секунду - гэта няшмат. 100 тысяч запытаў у секунду на запіс - таксама. Ёсць кампаніі, якія казалі, што яны трымаюць 2 млн. запытаў за секунду. Вось ім, мусіць, прыйдзецца паверыць.

І ў прынцыпе ў Cassandra ёсць адно вялікае адрозненне ад рэляцыйных дадзеных яна наогул на іх не падобная. І пра гэта вельмі важна памятаць.

Не ўсё, што выглядае аднолькава, працуе аднолькава

Неяк да мяне прыйшоў калега і спытаў: «Вось СQL Cassandra query language, і ў ім ёсць select statement, у ім ёсць where, у ім ёсць and. Я пішу літары, і не працуе. Чаму?». Калі ставіцца да Cassandra як да рэляцыйнай базы дадзеных, тое гэта ідэальны спосаб скончыць жыццё жорсткім самагубствам. І я не прапагандую, гэта забаронена ў Расіі. Вы проста спраектуеце што-небудзь няправільна.

Напрыклад, да нас прыходзіць заказчык і кажа: «Давайце пабудуем базу даных для серыялаў, або базу даных для даведніка рэцэптаў. У нас там будуць стравы з прадуктамі ці спіс серыялаў і акцёраў у ім». Мы гаворым радасна: "Давайце!". Гэта два байта пераслаць, пара таблічак і ўсё гатова, усё будзе працаваць вельмі хутка, надзейна. І ўсё выдатна, пакуль замоўцы не прыходзяць і не кажуць, што хатнія гаспадыні вырашаюць яшчэ і зваротную задачу: у іх ёсць спіс прадуктаў, і яны жадаюць пазнаць, якую страву яны жадаюць прыгатаваць. Вы мёртвыя.

Усё таму, што Cassandra -гібрыдная база дадзеных: яна адначасова і key value, і захоўвае дадзеныя ў шырокіх слупках. Калі казаць на мове Java ці Kotlin, гэта можна было б апісаць вось так:

Map<RowKey, SortedMap<ColumnKey, ColumnValue>>

Гэта значыць карта, усярэдзіне якой ляжыць яшчэ і адсартаваная карта. Першым ключом да гэтай карты з'яўляецца Row key або Partition key - ключ партыцыянавання. Другі ключ, які з'яўляецца ключом да ўжо адсартаванай карты, гэта Clustering key.

Для ілюстрацыі размеркаванасці базы дадзеных намалюем тры ноды. Цяпер трэба зразумець, як раскласці дадзеныя на ноды. Таму што калі мы будзем пхаць усё ў адну (іх, дарэчы, можа быць тысяча, дзве тысячы, пяць - колькі заўгодна), гэта не вельмі-то пра размеркаванасць. Таму нам патрэбна матэматычная функцыя, якая будзе вяртаць лік. Проста лік, доўгі int, які будзе трапляць у нейкі дыяпазон. І ў нас адна нода будзе адказваць за адзін дыяпазон, другая - за другі, n-ная - за n-ы.

Касандра. Як не памерці, калі ведаеш толькі Oracle

Гэты лік бярэцца з дапамогай хэш-функцыі, якая прымяняецца як раз да таго, што мы называем Partition key. Гэта той слупок, які ўказваецца ў дырэктыве Primary key, і гэта той слупок, які будзе першым і самым асноўным ключом карты. Ён вызначае, на якую ноду якія дадзеныя патрапяць. Табліца ствараецца ў Cassandra амаль з такім жа сінтаксісам, як у SQL:

CREATE TABLE users (
	user_id uu id,
	name text,
	year int,
	salary float,
	PRIMARY KEY(user_id)

)

Primary key у дадзеным выпадку складаецца з адной калонкі, і яна ж з'яўляецца ключом партыцыянавання.

Як у нас лягуць карыстальнікі? Частка трапіць на адну ноду, частка - на іншую, і частка - на трэцюю. Атрымліваецца звычайная хэш-табліца, яна ж map, яна ж у Python - слоўнік, яна ж - простая Key value-структура, з якой мы можам чытаць усе значэнні, чытаць і пісаць па ключы.

Касандра. Як не памерці, калі ведаеш толькі Oracle

Select: калі allow filtering ператвараецца ў full scan, ці як не трэба рабіць

Давайце напішам які-небудзь select statement: select * from users where, userid = . Атрымліваецца быццам бы як у Oracle: пішам select, паказваем умовы і ўсё працуе, карыстачы дастаюцца. Але калі абраць, напрыклад, карыстача з вызначаным годам нараджэння, Cassandra лаецца, што яна не можа выканаць запыт. Таму што яна ўвогуле нічога не ведае пра тое, як у нас размяркоўваюцца дадзеныя аб годзе нараджэння — у яе ў якасці ключа пазначана толькі адна калонка. Тады яна кажа: «Добра, я магу па-ранейшаму выканаць гэты запыт. Дадайце allow filtering». Мы дадаем дырэктыву, усё працуе. І ў гэты момант адбываецца страшнае.

Калі мы ганяем на тэставых дадзеных, тое ўсё выдатна. А калі вы выконваем запыт у прадакшэне, дзе ў нас, да прыкладу, 4 мільёны запісаў, то ў нас усё не вельмі добра. Таму што allow filtering - гэта дырэктыва, якая дазваляе Cassandra сабраць усе дадзеныя з гэтай табліцы з усіх нод, усіх дата-цэнтраў (калі іх шмат у гэтым кластары), і толькі потым ужо адфільтраваць. Гэта аналаг Full Scan, і наўрад ці ад яго нехта ў захапленні.

Калі б нам патрэбны былі карыстальнікі толькі па ідэнтыфікатарах, нас бы гэта задаволіла. Але часам нам трэба пісаць іншыя запыты і накладаць іншыя абмежаванні на выбарку. Таму ўспамінаем: гэта ўсё ў нас карта, у якой ёсць ключ партыцыянавання, але ўсярэдзіне яе - адсартаваная карта.

І ў яе таксама ёсць ключ, які мы завем Сlustering Key. Гэты ключ, які, у сваю чаргу, складаецца з калонак, якія мы абярэм, з дапамогай якога Cassandra разумее, як у яе дадзеныя фізічна адсартуюцца і будуць ляжаць на кожнай надзе. Гэта значыць, для нейкага Partition key Clustering key раскажа, як менавіта дадзеныя запхнуць у гэтае дрэва, якое месца яны там зоймуць.

Гэта рэальна дрэва, тамака проста выклікаецца кампаратар, у які мы перадаем нейкі набор калонак у выглядзе аб'екта, і задаецца ён таксама ў выглядзе пераліку калонак.

CREATE TABLE users_by_year_salary_id (
	user_id uuid,
	name text,
	year int,
	salary float,
	PRIMARY KEY((year), salary, user_id)

Звярніце ўвагу на дырэктыву Primary key, у яе першы аргумент (у нашым выпадку год) заўсёды ідзе Partition key. Ён можа складацца з адной ці некалькіх калонак, гэта не важна. Калі калонак некалькі, яго трэба яшчэ раз у дужкі прыбраць, каб прэпрацэсар мовы зразумеў, што гэта менавіта Primary key, а за ім усе астатнія калонкі - Clustering key. Пры гэтым яны будуць у кампаратары перадавацца ў тым парадку, у якім яны ідуць. Гэта значыць, першая калонка больш значная, другая - менш значная і гэтак далей. Як мы для data classes пішам, напрыклад, палі equals: пералічваем палі, і для іх пішам, якія большыя, а якія меншыя. У Cassandra гэта, умоўна кажучы, палі data class, да якога будзе прымяняцца напісаны для яго equals.

Задаем сартаванне, накладваем абмежаванні

Трэба памятаць, што парадак сартавання (змяншальная, нарастальная, не важна) задаецца ў той жа момант, калі ствараецца ключ, і памяняць яго потым будзе нельга. Ён фізічна вызначае, як будуць пасартаваны дадзеныя і як яны будуць ляжаць. Калі трэба будзе змяніць Clustering key або парадак сартавання, давядзецца ствараць новую табліцу і пераліваць у яе дадзеныя. З ужо існуючай так не атрымаецца.

Касандра. Як не памерці, калі ведаеш толькі Oracle

Мы запоўнілі нашу табліцу карыстальнікамі і ўбачылі, што яны леглі ў кола спачатку па годзе нараджэння, а потым усярэдзіне на кожнай нодзе па зарплаце і па user ID. Цяпер мы можам селектаваць, накладваючы абмежаванні.

З'яўляецца зноў наш які працуе where, and, і карыстальнікі нам дастаюцца, і ўсё зноў добра. Але калі мы паспрабуем выкарыстаць толькі частка Clustering key, прычым меней значную, то Cassandra тут жа лаецца, што не можа ў нашай мапе знайсці месца, дзе гэты аб'ект, у якога вось гэтыя палі для кампаратара null, а вось гэты, які толькі што задалі , - Дзе ён ляжыць. Мне давядзецца зноў падняць усе дадзеныя з гэтай ноды і адфільтраваць іх. І гэта аналаг Full Scan у рамках ноды, гэта дрэнна.

У любой незразумелай сітуацыі стварай новую табліцу

Калі мы жадаем мець магчымасць даставаць карыстачоў па ID ці па ўзросце, ці па зарплаце, што рабіць? Нічога. Проста выкарыстоўваць дзве табліцы. Калі трэба будзе даставаць карыстальнікаў трыма рознымі спосабамі - табліц будзе тры. Мінулі тыя часы, калі мы эканомілі месца на шрубе. Гэта самы танны рэсурс. Ён каштуе значна танней, чым час адказу, які можа быць згубным для карыстальніка. Карыстальніку значна прыемней атрымаць нешта за секунду, чым за 10 хвілін.

Мы абменьваем залішняе месца, дэнармалізаваныя дадзеныя на магчымасць добра маштабавацца, надзейна працаваць. Бо насамрэч кластар, які складаецца з трох дата-цэнтраў, у кожным з якіх па пяць нод, пры прымальным узроўні захавання дадзеных (калі сапраўды нічога не згубіцца), здольны перажыць згубу аднаго дата-цэнтра цалкам. І яшчэ па дзве ноды ў кожным з двух астатніх. І вось толькі пасля гэтага пачнуцца праблемы. Гэта даволі добрае рэзерваванне, яно варта пары-тройкі лішніх sSD-назапашвальнікаў і працэсараў. Таму для таго, каб выкарыстоўваць Cassandra, якая ні разу не SQL, у якой няма адносін, вонкавых ключоў, трэба ведаць простыя правілы.

Праектуем усё, што ад запыту. Галоўнымі становяцца не дадзеныя, а тое, як дадатак збіраецца з імі працаваць. Калі яму трэба атрымліваць розныя дадзеныя рознымі спосабамі ці адны і тыя ж дадзеныя рознымі спосабамі, мы павінны пакласці іх так, як будзе зручна з дадаткам. Інакш мы будзем правальвацца ў Full Scan і ніякай перавагі Cassandra нам не дасць.

Дэнармалізаваць дадзеныя - гэта норма. Забываем пра нармальныя формы, у нас больш не рэляцыйныя базы. Пакладзем што-небудзь 100 разоў, будзе ляжаць 100 разоў. Гэта ўсё роўна танней, чым спыніць.

Выбіраемы ключы для партыцыянавання так, каб яны звычайна размяркоўваліся. Нам не трэба, каб хэш ад нашых ключоў пападаў у адзін вузкі дыяпазон. Гэта значыць, год нараджэння ў прыкладзе вышэй - дрэнны прыклад. Дакладней, ён добры, калі ў нас карыстальнікі па годзе нараджэння нармальна размеркаваны, і дрэнны, калі гаворка ідзе пра вучняў 5-га класа - там не вельмі добра будзе партыцыянавацца.

Сартаванне выбіраецца адзін раз на этапе стварэння Clustering Key. Калі яе трэба будзе змяніць, то давядзецца пераліваць нашу табліцу з іншым ключом.

І найважнейшае: калі нам трэба 100 рознымі спосабамі забраць адны і тыя ж дадзеныя, значыць у нас будзе 100 розных табліц.

Крыніца: habr.com

Дадаць каментар