Cassandra. Hur man inte dör om man bara känner till Oracle

Hej Habr.

Jag heter Misha Butrimov, jag skulle vilja berätta lite om Cassandra. Min historia kommer att vara användbar för dem som aldrig har stött på NoSQL-databaser - den har många implementeringsfunktioner och fallgropar som du behöver veta om. Och om du inte har sett något annat än Oracle eller någon annan relationsdatabas, kommer dessa saker att rädda ditt liv.

Vad är det som är så bra med Cassandra? Det är en NoSQL-databas designad utan en enda felpunkt som skalas väl. Om du behöver lägga till ett par terabyte för någon databas lägger du helt enkelt till noder i ringen. Vill du utöka det till ett annat datacenter? Lägg till noder i klustret. Öka bearbetad RPS? Lägg till noder i klustret. Det fungerar i motsatt riktning också.

Cassandra. Hur man inte dör om man bara känner till Oracle

Vad mer är hon bra på? Det handlar om att hantera många förfrågningar. Men hur mycket är mycket? 10, 20, 30, 40 tusen förfrågningar per sekund är inte mycket. 100 tusen förfrågningar per sekund för inspelning - också. Det finns företag som sa att de behåller 2 miljoner förfrågningar per sekund. De måste nog tro det.

Och i princip har Cassandra en stor skillnad mot relationsdata – den liknar dem inte alls. Och detta är mycket viktigt att komma ihåg.

Allt som ser likadant ut fungerar inte likadant

En gång kom en kollega till mig och frågade: "Här är ett CQL Cassandra-frågespråk, och det har en select-sats, den har var, den har och. Jag skriver brev och det fungerar inte. Varför?". Att behandla Cassandra som en relationsdatabas är det perfekta sättet att begå våldsamt självmord. Och jag marknadsför det inte, det är förbjudet i Ryssland. Du kommer bara att designa något fel.

Till exempel kommer en kund till oss och säger: ”Låt oss bygga en databas för tv-serier, eller en databas för en receptkatalog. Vi kommer att ha maträtter där eller en lista över tv-serier och skådespelare i den.” Vi säger glatt: "Låt oss gå!" Skicka bara två byte, ett par tecken och du är klar, allt kommer att fungera mycket snabbt och tillförlitligt. Och allt är bra tills kunderna kommer och säger att hemmafruar också löser det motsatta problemet: de har en lista med produkter och de vill veta vilken maträtt de vill laga. Du är död.

Detta beror på att Cassandra är en hybriddatabas: den ger samtidigt ett nyckelvärde och lagrar data i breda kolumner. I Java eller Kotlin kan det beskrivas så här:

Map<RowKey, SortedMap<ColumnKey, ColumnValue>>

Det vill säga en karta som också innehåller en sorterad karta. Den första nyckeln till denna karta är rad- eller partitionsnyckeln - partitioneringsnyckeln. Den andra nyckeln, som är nyckeln till en redan sorterad karta, är Clustering-nyckeln.

För att illustrera distributionen av databasen, låt oss rita tre noder. Nu måste du förstå hur man bryter ner data till noder. För om vi klämmer ihop allt i ett (förresten, det kan bli tusen, tvåtusen, fem – hur många som helst), så handlar det egentligen inte om distribution. Därför behöver vi en matematisk funktion som returnerar ett tal. Bara en siffra, en lång int som kommer att falla inom något område. Och vi kommer att ha en nod ansvarig för ett område, den andra för det andra, den n:te för det n:te.

Cassandra. Hur man inte dör om man bara känner till Oracle

Detta nummer tas med hjälp av en hash-funktion, som tillämpas på vad vi kallar partitionsnyckeln. Det här är den kolumn som anges i direktivet om primärnyckel, och det här är den kolumn som kommer att vara den första och mest grundläggande nyckeln på kartan. Den bestämmer vilken nod som ska ta emot vilken data. En tabell skapas i Cassandra med nästan samma syntax som i SQL:

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

)

Den primära nyckeln i det här fallet består av en kolumn, och det är också partitioneringsnyckeln.

Hur kommer våra användare att prestera? Vissa kommer att gå till en nod, några till en annan och några till en tredje. Resultatet är en vanlig hashtabell, även känd som en karta, även känd som en ordbok i Python, eller en enkel nyckelvärdesstruktur från vilken vi kan läsa alla värden, läsa och skriva för nyckel.

Cassandra. Hur man inte dör om man bara känner till Oracle

Välj: när tillåt filtrering övergår i full scan, eller vad du inte ska göra

Låt oss skriva ett utvalt uttalande: select * from users where, userid = . Det blir som i Oracle: vi skriver select, specificerar villkoren och allt fungerar, användarna får det. Men om du väljer till exempel en användare med ett visst födelseår klagar Cassandra på att den inte kan uppfylla förfrågan. Eftersom hon inte vet någonting alls om hur vi fördelar data om födelseåret - har hon bara en kolumn angiven som nyckel. Sedan säger hon: "Okej, jag kan fortfarande uppfylla denna begäran. Lägg till tillåt filtrering." Vi lägger till direktivet, allt fungerar. Och i detta ögonblick händer något hemskt.

När vi kör på testdata är allt bra. Och när man kör en förfrågan i produktionen, där vi till exempel har 4 miljoner poster, då är allt inte särskilt bra för oss. Eftersom tillåt filtrering är ett direktiv som tillåter Cassandra att samla in all data från den här tabellen från alla noder, alla datacenter (om det finns många av dem i det här klustret), och först därefter filtrera det. Detta är en analog till Full Scan, och knappast någon är nöjd med den.

Om vi ​​bara behövde användare med ID skulle vi klara det bra. Men ibland behöver vi skriva andra frågor och införa andra begränsningar för urvalet. Därför kommer vi ihåg: allt detta är en karta som har en partitioneringsnyckel, men inuti den är en sorterad karta.

Och hon har också en nyckel, som vi kallar Clustering Key. Denna nyckel, som i sin tur består av de kolumner som vi väljer, med hjälp av vilken Cassandra förstår hur dess data är fysiskt sorterad och kommer att finnas på varje nod. Det vill säga, för en partitionsnyckel kommer Clustering-nyckeln att berätta exakt hur du skjuter in data i detta träd, vilken plats den kommer att ta där.

Detta är verkligen ett träd, en komparator kallas helt enkelt där, till vilken vi skickar en viss uppsättning kolumner i form av ett objekt, och den anges också som en lista med kolumner.

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

Var uppmärksam på Primärnyckeldirektivet; dess första argument (i vårt fall årtalet) är alltid Partitionsnyckel. Det kan bestå av en eller flera kolumner, det spelar ingen roll. Om det finns flera kolumner måste den tas bort inom parentes igen så att språkförprocessorn förstår att detta är den primära nyckeln, och bakom den finns alla andra kolumner Clustering-nyckeln. I det här fallet kommer de att sändas i komparatorn i den ordning som de visas. Det vill säga, den första kolumnen är mer signifikant, den andra är mindre signifikant och så vidare. Hur vi skriver är till exempel lika med fält för dataklasser: vi listar fälten, och för dem skriver vi vilka som är större och vilka som är mindre. I Cassandra är dessa, relativt sett, fälten i dataklassen, på vilka de lika skrivna för den kommer att tillämpas.

Vi sätter sortering och inför restriktioner

Du måste komma ihåg att sorteringsordningen (fallande, stigande, vad som helst) ställs in i samma ögonblick som nyckeln skapas, och den kan inte ändras senare. Det avgör fysiskt hur data kommer att sorteras och hur den kommer att lagras. Om du behöver ändra klustringsnyckeln eller sorteringsordningen måste du skapa en ny tabell och överföra data till den. Detta kommer inte att fungera med en befintlig.

Cassandra. Hur man inte dör om man bara känner till Oracle

Vi fyllde vårt bord med användare och såg att de ramlade i en ring, först efter födelseår, och sedan inne på varje nod efter lön och användar-ID. Nu kan vi välja genom att införa begränsningar.

Vårt arbetande dyker upp igen where, and, och vi får användare, och allt är bra igen. Men om vi försöker använda bara en del av klustringsnyckeln, och en mindre betydelsefull sådan, så kommer Cassandra omedelbart att klaga på att den inte kan hitta platsen på vår karta där detta objekt, som har dessa fält för nolljämföraren, och det här. det var precis inställt, - där han ligger. Jag måste hämta all data från denna nod igen och filtrera den. Och det här är en analog av Full Scan inom en nod, det här är dåligt.

I alla oklara situationer, skapa en ny tabell

Om vi ​​vill kunna rikta in oss på användare efter ID, eller efter ålder eller efter lön, vad ska vi göra? Ingenting. Använd bara två bord. Om du behöver nå användare på tre olika sätt kommer det att finnas tre tabeller. De dagar då vi sparade plats på skruven är förbi. Detta är den billigaste resursen. Det kostar mycket mindre än svarstid, vilket kan vara skadligt för användaren. Det är mycket trevligare för användaren att få något på en sekund än på 10 minuter.

Vi byter onödigt utrymme och denormaliserad data för förmågan att skala bra och fungera tillförlitligt. När allt kommer omkring kan faktiskt ett kluster som består av tre datacenter, som var och en har fem noder, med en acceptabel nivå av databevarande (när inget går förlorat), överleva döden av ett datacenter helt. Och ytterligare två noder i var och en av de återstående två. Och först efter detta börjar problemen. Detta är en ganska bra redundans, det är värt ett par extra SSD-enheter och processorer. Därför, för att använda Cassandra, som aldrig är SQL, där det inte finns några relationer, främmande nycklar, måste du känna till enkla regler.

Vi designar allt efter dina önskemål. Huvudsaken är inte data, utan hur applikationen kommer att fungera med den. Om den behöver ta emot olika data på olika sätt eller samma data på olika sätt måste vi lägga den på ett sätt som passar applikationen. Annars kommer vi att misslyckas i Full Scan och Cassandra kommer inte att ge oss någon fördel.

Denormalisering av data är normen. Vi glömmer normala former, vi har inte längre relationsdatabaser. Om vi ​​lägger ner något 100 gånger så kommer det att ligga ner 100 gånger. Det är fortfarande billigare än att sluta.

Vi väljer nycklarna för partitionering så att de fördelas normalt. Vi vill inte att hashen på våra nycklar ska hamna i ett smalt område. Det vill säga födelseåret i exemplet ovan är ett dåligt exempel. Närmare bestämt är det bra om våra användare är normalfördelade efter födelseår, och dåligt om vi pratar om elever i 5:e klass – uppdelningen där blir inte särskilt bra.

Sortering väljs en gång när klustringsnyckeln skapas. Om den behöver ändras måste vi uppdatera vår tabell med en annan nyckel.

Och det viktigaste: om vi behöver hämta samma data på 100 olika sätt, då kommer vi att ha 100 olika tabeller.

Källa: will.com

Lägg en kommentar