Cassandra. Hoe je niet moet sterven als je alleen Oracle kent

Hé Habr.

Mijn naam is Misha Butrimov, ik wil je graag iets over Cassandra vertellen. Mijn verhaal zal nuttig zijn voor degenen die nog nooit NoSQL-databases zijn tegengekomen - het bevat veel implementatiefuncties en valkuilen waar je over moet weten. En als je niets anders hebt gezien dan Oracle of een andere relationele database, zullen deze dingen je leven redden.

Wat is er zo goed aan Cassandra? Het is een NoSQL-database die is ontworpen zonder een enkel storingspunt en goed schaalbaar is. Als u voor een bepaalde database een paar terabytes moet toevoegen, voegt u eenvoudigweg knooppunten aan de ring toe. Uitbreiden naar een ander datacenter? Voeg knooppunten toe aan het cluster. Verwerkte RPS verhogen? Voeg knooppunten toe aan het cluster. Het werkt ook in de tegenovergestelde richting.

Cassandra. Hoe je niet moet sterven als je alleen Oracle kent

Waar is ze nog meer goed in? Het gaat om het afhandelen van veel verzoeken. Maar hoeveel is veel? 10, 20, 30, 40 duizend verzoeken per seconde is niet veel. Ook 100 verzoeken per seconde voor opname. Er zijn bedrijven die zeggen dat ze 2 miljoen verzoeken per seconde bijhouden. Ze zullen het waarschijnlijk moeten geloven.

En in principe heeft Cassandra één groot verschil met relationele gegevens: ze lijken er helemaal niet op. En dit is heel belangrijk om te onthouden.

Niet alles wat er hetzelfde uitziet, werkt hetzelfde

Een collega kwam eens naar mij toe en vroeg: “Hier is een CQL Cassandra-querytaal, en deze heeft een selecte verklaring, deze heeft waar, deze heeft en. Ik schrijf brieven en het werkt niet. Waarom?". Cassandra behandelen als een relationele database is de perfecte manier om gewelddadige zelfmoord te plegen. En ik maak er geen reclame voor, het is verboden in Rusland. Je ontwerpt gewoon iets verkeerd.

Een klant komt bijvoorbeeld naar ons toe en zegt: “Laten we een database bouwen voor tv-series, of een database voor een receptenmap. We zullen daar eetgerechten hebben of een lijst met tv-series en acteurs erin. We zeggen vreugdevol: "Laten we gaan!" Stuur gewoon twee bytes, een paar tekens en je bent klaar, alles werkt heel snel en betrouwbaar. En alles gaat goed totdat de klanten komen en zeggen dat huisvrouwen ook het tegenovergestelde probleem oplossen: ze hebben een lijst met producten en willen weten welk gerecht ze willen koken. Je bent dood.

Dit komt omdat Cassandra een hybride database is: deze biedt tegelijkertijd een sleutelwaarde en slaat gegevens op in brede kolommen. In Java of Kotlin zou het als volgt kunnen worden beschreven:

Map<RowKey, SortedMap<ColumnKey, ColumnValue>>

Dat wil zeggen, een kaart die ook een gesorteerde kaart bevat. De eerste sleutel tot deze kaart is de rijsleutel of partitiesleutel: de partitiesleutel. De tweede sleutel, die de sleutel is tot een reeds gesorteerde kaart, is de Clustering-sleutel.

Laten we, om de distributie van de database te illustreren, drie knooppunten tekenen. Nu moet u begrijpen hoe u de gegevens in knooppunten kunt ontleden. Want als we alles in één proppen (het kunnen er trouwens duizend, tweeduizend, vijf zijn – zoveel als je wilt), gaat het niet echt om distributie. Daarom hebben we een wiskundige functie nodig die een getal retourneert. Gewoon een getal, een lange int die binnen een bepaald bereik valt. En we zullen één knooppunt hebben dat verantwoordelijk is voor één bereik, de tweede voor de tweede, de n-de voor de n-de.

Cassandra. Hoe je niet moet sterven als je alleen Oracle kent

Dit nummer wordt bepaald met behulp van een hash-functie, die wordt toegepast op wat we de partitiesleutel noemen. Dit is de kolom die is gespecificeerd in de Primaire sleutelrichtlijn, en dit is de kolom die de eerste en meest elementaire sleutel van de kaart zal zijn. Het bepaalt welk knooppunt welke gegevens ontvangt. Er wordt in Cassandra een tabel gemaakt met bijna dezelfde syntaxis als in SQL:

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

)

De primaire sleutel bestaat in dit geval uit één kolom en is tevens de partitiesleutel.

Hoe zullen onze gebruikers presteren? Sommige gaan naar het ene knooppunt, sommige naar een ander knooppunt, en sommige naar een derde. Het resultaat is een gewone hashtabel, ook wel een kaart genoemd, ook wel een woordenboek genoemd in Python, of een eenvoudige sleutelwaardestructuur waaruit we alle waarden kunnen lezen, lezen en schrijven per sleutel.

Cassandra. Hoe je niet moet sterven als je alleen Oracle kent

Selecteer: wanneer toestaan ​​dat filteren verandert in volledige scan, of wat u niet moet doen

Laten we een selecte verklaring schrijven: select * from users where, userid = . Het blijkt zoals in Oracle: we schrijven select, specificeren de voorwaarden en alles werkt, gebruikers krijgen het. Maar als je bijvoorbeeld een gebruiker selecteert met een bepaald geboortejaar, klaagt Cassandra dat ze niet aan het verzoek kan voldoen. Omdat ze helemaal niets weet over hoe we gegevens over het geboortejaar verspreiden, heeft ze slechts één kolom aangegeven als sleutel. Dan zegt ze: 'Oké, ik kan nog steeds aan dit verzoek voldoen. Voeg filtering toe." We voegen de richtlijn toe, alles werkt. En op dit moment gebeurt er iets vreselijks.

Als we op testgegevens draaien, is alles in orde. En als je een query uitvoert in productie, waar we bijvoorbeeld 4 miljoen records hebben, dan is alles niet zo goed voor ons. Omdat filteren een richtlijn is waarmee Cassandra alle gegevens uit deze tabel van alle knooppunten en alle datacenters (als er veel in dit cluster zijn) kan verzamelen en deze pas daarna kan filteren. Dit is een analoog van Full Scan en bijna niemand is er blij mee.

Als we alleen gebruikers op ID nodig hadden, zouden we dit prima vinden. Maar soms moeten we andere vragen stellen en andere beperkingen opleggen aan de selectie. Daarom onthouden we: dit is allemaal een kaart met een partitiesleutel, maar daarbinnen zit een gesorteerde kaart.

En ze heeft ook een sleutel, die we de Clustering Key noemen. Deze sleutel, die op zijn beurt bestaat uit de kolommen die we selecteren, met behulp waarvan Cassandra begrijpt hoe de gegevens fysiek zijn gesorteerd en zich op elk knooppunt zullen bevinden. Dat wil zeggen dat voor sommige partitiesleutels de Clustering-sleutel u precies vertelt hoe u de gegevens in deze boomstructuur moet plaatsen en welke plaats ze daar zullen innemen.

Dit is echt een boom, daar wordt eenvoudigweg een comparator genoemd, waaraan we een bepaalde reeks kolommen doorgeven in de vorm van een object, en deze wordt ook gespecificeerd als een lijst met kolommen.

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

Let op de richtlijn Primaire sleutel; het eerste argument (in ons geval het jaartal) is altijd Partitiesleutel. Het kan uit één of meer kolommen bestaan, het maakt niet uit. Als er meerdere kolommen zijn, moet deze weer tussen haakjes worden verwijderd, zodat de taalpreprocessor begrijpt dat dit de primaire sleutel is en dat daarachter alle andere kolommen de clustersleutel zijn. In dit geval worden ze in de comparator verzonden in de volgorde waarin ze verschijnen. Dat wil zeggen: de eerste kolom is belangrijker, de tweede minder belangrijk, enzovoort. Hoe we bijvoorbeeld schrijven, is gelijk aan velden voor gegevensklassen: we zetten de velden op een rij, en voor hen schrijven we welke groter en welke kleiner zijn. In Cassandra zijn dit relatief gezien de velden van de dataklasse, waarop de gelijkheden die ervoor zijn geschreven, zullen worden toegepast.

We stellen sorteringen in en leggen beperkingen op

U moet niet vergeten dat de sorteervolgorde (aflopend, oplopend, wat dan ook) wordt ingesteld op hetzelfde moment waarop de sleutel wordt gemaakt, en dat deze later niet kan worden gewijzigd. Het bepaalt fysiek hoe de gegevens worden gesorteerd en hoe deze worden opgeslagen. Als u de clustersleutel of de sorteervolgorde moet wijzigen, moet u een nieuwe tabel maken en de gegevens daarin overbrengen. Dit werkt niet met een bestaande.

Cassandra. Hoe je niet moet sterven als je alleen Oracle kent

We vulden onze tabel met gebruikers en zagen dat ze in een ring vielen, eerst op geboortejaar, en vervolgens op elk knooppunt op basis van salaris en gebruikers-ID. Nu kunnen we selecteren door beperkingen op te leggen.

Onze werkende verschijnt weer where, and, en we krijgen gebruikers, en alles is weer in orde. Maar als we slechts een deel van de Clustering-sleutel proberen te gebruiken, en een minder belangrijke, dan zal Cassandra onmiddellijk klagen dat het de plaats op onze kaart niet kan vinden waar dit object, dat deze velden heeft voor de nulvergelijker, en dit dat was gewoon ingesteld, - waar hij ligt. Ik zal alle gegevens van dit knooppunt opnieuw moeten ophalen en filteren. En dit is een analoog van Volledige scan binnen een knooppunt, dit is slecht.

Maak in elke onduidelijke situatie een nieuwe tabel

Wat moeten we doen als we gebruikers willen targeten op basis van ID, leeftijd of salaris? Niets. Gebruik gewoon twee tabellen. Als u gebruikers op drie verschillende manieren wilt bereiken, zijn er drie tabellen. Voorbij zijn de dagen dat we ruimte op de schroef bespaarden. Dit is de goedkoopste bron. Het kost veel minder dan de responstijd, wat schadelijk kan zijn voor de gebruiker. Het is voor de gebruiker veel prettiger om in een seconde iets te ontvangen dan in 10 minuten.

We ruilen onnodige ruimte en gedenormaliseerde gegevens in voor de mogelijkheid om goed te schalen en betrouwbaar te werken. Een cluster dat bestaat uit drie datacenters met elk vijf knooppunten, met een acceptabel niveau van databehoud (wanneer er niets verloren gaat), kan immers de dood van één datacenter volledig overleven. En nog twee knooppunten in elk van de overige twee. En pas daarna beginnen de problemen. Dit is een redelijk goede redundantie, het is een paar extra SSD-schijven en processors waard. Om Cassandra te kunnen gebruiken, wat nooit SQL is, waarin er geen relaties of externe sleutels zijn, moet je daarom eenvoudige regels kennen.

Wij ontwerpen alles volgens uw verzoek. Het belangrijkste zijn niet de gegevens, maar hoe de applicatie ermee gaat werken. Als het verschillende gegevens op verschillende manieren of dezelfde gegevens op verschillende manieren moet ontvangen, moeten we dit op een manier plaatsen die handig is voor de toepassing. Anders zullen we falen in de Volledige Scan en zal Cassandra ons geen enkel voordeel opleveren.

Het denormaliseren van data is de norm. We vergeten de normale formulieren, we hebben geen relationele databases meer. Als we iets 100 keer neerleggen, zal het ook 100 keer gaan liggen. Het is nog steeds goedkoper dan stoppen.

We selecteren de sleutels voor partitionering zodat ze normaal worden verdeeld. We willen niet dat de hash van onze sleutels in één smal bereik valt. Dat wil zeggen, het geboortejaar in het bovenstaande voorbeeld is een slecht voorbeeld. Om precies te zijn: het is goed als onze gebruikers normaal verdeeld zijn naar geboortejaar, en slecht als we het hebben over leerlingen van groep 5 - de verdeling daar zal niet erg goed zijn.

Sorteren wordt één keer geselecteerd tijdens het maken van de clustersleutel. Als het moet worden gewijzigd, zullen we onze tabel moeten bijwerken met een andere sleutel.

En het allerbelangrijkste: als we dezelfde gegevens op 100 verschillende manieren moeten ophalen, dan hebben we 100 verschillende tabellen.

Bron: www.habr.com

Voeg een reactie