Cassandra. Come non morire se conosci solo Oracle

Ciao Habr.

Mi chiamo Misha Butrimov, vorrei parlarvi un po' di Cassandra. La mia storia sarà utile a coloro che non hanno mai incontrato i database NoSQL: contiene molte funzionalità di implementazione e insidie ​​​​che devi conoscere. E se non hai visto altro che Oracle o qualsiasi altro database relazionale, queste cose ti salveranno la vita.

Cosa c'è di così bello in Cassandra? È un database NoSQL progettato senza un singolo punto di errore che si adatta bene. Se hai bisogno di aggiungere un paio di terabyte per qualche database, aggiungi semplicemente dei nodi all'anello. Espanderlo in un altro data center? Aggiungi nodi al cluster. Aumentare gli RPS elaborati? Aggiungi nodi al cluster. Funziona anche nella direzione opposta.

Cassandra. Come non morire se conosci solo Oracle

In cos'altro è brava? Si tratta di gestire molte richieste. Ma quanto è tanto? 10, 20, 30, 40mila richieste al secondo non sono molte. Anche 100mila richieste al secondo per la registrazione. Ci sono aziende che affermano di conservare 2 milioni di richieste al secondo. Probabilmente dovranno crederci.

E in linea di principio, Cassandra ha una grande differenza rispetto ai dati relazionali: non è affatto simile a loro. E questo è molto importante da ricordare.

Non tutto ciò che sembra uguale funziona allo stesso modo

Una volta un collega venne da me e mi chiese: “Ecco un linguaggio di query CQL Cassandra, e ha un'istruzione select, ha dove, ha e. Scrivo lettere e non funziona. Perché?". Trattare Cassandra come un database relazionale è il modo perfetto per commettere un suicidio violento. E non lo sto promuovendo, è proibito in Russia. Progetterai semplicemente qualcosa di sbagliato.

Ad esempio, un cliente viene da noi e dice: “Costruiamo un database per le serie TV, oppure un database per una directory di ricette. Avremo piatti di cibo lì o un elenco di serie TV e attori in esso. Diciamo con gioia: "Andiamo!" Basta inviare due byte, un paio di segni e il gioco è fatto, tutto funzionerà in modo molto rapido e affidabile. E va tutto bene finché non arrivano i clienti e dicono che le casalinghe stanno risolvendo anche il problema opposto: hanno la lista dei prodotti e vogliono sapere che piatto vogliono cucinare. Sei morto.

Questo perché Cassandra è un database ibrido: fornisce contemporaneamente un valore chiave e archivia i dati in colonne ampie. In Java o Kotlin, potrebbe essere descritto in questo modo:

Map<RowKey, SortedMap<ColumnKey, ColumnValue>>

Cioè, una mappa che contiene anche una mappa ordinata. La prima chiave di questa mappa è la chiave Row o Partition key, ovvero la chiave di partizione. La seconda chiave, che è la chiave per una mappa già ordinata, è la chiave Clustering.

Per illustrare la distribuzione del database, disegniamo tre nodi. Ora devi capire come scomporre i dati in nodi. Perché se stimiamo tutto in uno (a proposito, possono essercene mille, duemila, cinque - quanti ne vuoi), non si tratta proprio di distribuzione. Pertanto, abbiamo bisogno di una funzione matematica che restituisca un numero. Solo un numero, un lungo int che rientrerà in un certo intervallo. E avremo un nodo responsabile per un intervallo, il secondo per il secondo, l'ennesimo per l'ennesimo.

Cassandra. Come non morire se conosci solo Oracle

Questo numero viene preso utilizzando una funzione hash, che viene applicata a quella che chiamiamo chiave di partizione. Questa è la colonna specificata nella direttiva della chiave primaria e questa è la colonna che sarà la prima e più fondamentale chiave della mappa. Determina quale nodo riceverà quali dati. Una tabella viene creata in Cassandra con quasi la stessa sintassi di SQL:

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

)

La chiave primaria in questo caso è costituita da una colonna ed è anche la chiave di partizionamento.

Come si comporteranno i nostri utenti? Alcuni andranno su un nodo, altri su un altro e altri su un terzo. Il risultato è una normale tabella hash, conosciuta anche come mappa, conosciuta anche come dizionario in Python, o una semplice struttura di valori chiave da cui possiamo leggere tutti i valori, leggere e scrivere per chiave.

Cassandra. Come non morire se conosci solo Oracle

Seleziona: quando consentire il filtraggio diventa scansione completa o cosa non fare

Scriviamo qualche istruzione select: select * from users where, userid = . Risulta come in Oracle: scriviamo select, specifichiamo le condizioni e tutto funziona, gli utenti lo ottengono. Ma se si seleziona, ad esempio, un utente con un certo anno di nascita, Cassandra lamenta di non poter soddisfare la richiesta. Poiché non sa nulla di come distribuiamo i dati sull'anno di nascita, ha solo una colonna indicata come chiave. Poi dice: “Va bene, posso ancora soddisfare questa richiesta. Aggiungi consenti filtro." Aggiungiamo la direttiva, tutto funziona. E in questo momento accade qualcosa di terribile.

Quando eseguiamo i dati di test, tutto va bene. E quando esegui una query in produzione, dove abbiamo, ad esempio, 4 milioni di record, per noi non va tutto molto bene. Perché consenti il ​​filtraggio è una direttiva che consente a Cassandra di raccogliere tutti i dati di questa tabella da tutti i nodi, tutti i data center (se ce ne sono molti in questo cluster) e solo successivamente filtrarli. Questo è un analogo di Full Scan e quasi nessuno ne è entusiasta.

Se avessimo bisogno solo degli utenti in base all'ID, staremmo bene con questo. Ma a volte dobbiamo scrivere altre query e imporre altre restrizioni alla selezione. Ricordiamo quindi: questa è tutta una mappa che ha una chiave di partizionamento, ma al suo interno c'è una mappa ordinata.

E ha anche una chiave, che chiamiamo Chiave del Clustering. Questa chiave, che a sua volta è composta dalle colonne che selezioniamo, con l'aiuto delle quali Cassandra capisce come i suoi dati saranno ordinati fisicamente e si troveranno su ciascun nodo. Cioè, per alcune chiavi di partizione, la chiave di clustering ti dirà esattamente come inserire i dati in questo albero, quale posto occuperanno lì.

Questo è davvero un albero, lì viene semplicemente chiamato un comparatore, al quale passiamo un certo insieme di colonne sotto forma di oggetto, ed è anche specificato come un elenco di colonne.

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

Presta attenzione alla direttiva Primary key; il suo primo argomento (nel nostro caso, l'anno) è sempre Partition key. Può essere composto da una o più colonne, non importa. Se sono presenti più colonne, è necessario rimuoverle nuovamente tra parentesi in modo che il preprocessore del linguaggio comprenda che questa è la chiave primaria e dietro tutte le altre colonne c'è la chiave di clustering. In questo caso verranno trasmessi nel comparatore nell'ordine in cui compaiono. Cioè la prima colonna è più significativa, la seconda è meno significativa e così via. Il modo in cui scriviamo, ad esempio, è uguale ai campi per le classi di dati: elenchiamo i campi e per essi scriviamo quali sono più grandi e quali sono più piccoli. In Cassandra questi sono, relativamente parlando, i campi della classe dati, a cui verranno applicati gli equal scritti per essa.

Impostiamo l'ordinamento e imponiamo restrizioni

È necessario ricordare che l'ordinamento (discendente, ascendente, qualunque cosa) viene impostato nello stesso momento in cui viene creata la chiave e non può essere modificato successivamente. Determina fisicamente come verranno ordinati i dati e come verranno archiviati. Se è necessario modificare la chiave di clustering o l'ordinamento, sarà necessario creare una nuova tabella e trasferirvi i dati. Questo non funzionerà con uno esistente.

Cassandra. Come non morire se conosci solo Oracle

Abbiamo riempito la nostra tabella con gli utenti e abbiamo visto che cadevano in un anello, prima per anno di nascita, e poi all'interno di ciascun nodo per stipendio e ID utente. Ora possiamo selezionare imponendo restrizioni.

Il nostro lavoro appare di nuovo where, ande otteniamo utenti e tutto va di nuovo bene. Ma se proviamo ad usare solo una parte della chiave Clustering, ed una meno significativa, allora Cassandra si lamenterà subito di non riuscire a trovare il punto nella nostra mappa dove questo oggetto, che ha questi campi per il comparatore nullo, e questo era appena ambientato dove giace. Dovrò recuperare nuovamente tutti i dati da questo nodo e filtrarli. E questo è un analogo della scansione completa all'interno del nodo, questo è negativo.

In qualsiasi situazione poco chiara, crea una nuova tabella

Se vogliamo poter targettizzare gli utenti in base all'ID, all'età o allo stipendio, cosa dovremmo fare? Niente. Usa solo due tabelle. Se devi raggiungere gli utenti in tre modi diversi, ci saranno tre tabelle. Sono finiti i giorni in cui risparmiavamo spazio sulla vite. Questa è la risorsa più economica. Costa molto meno del tempo di risposta, il che può essere dannoso per l'utente. È molto più piacevole per l'utente ricevere qualcosa in un secondo che in 10 minuti.

Scambiamo spazio non necessario e dati denormalizzati con la capacità di scalare bene e operare in modo affidabile. Dopotutto, infatti, un cluster composto da tre data center, ciascuno dei quali ha cinque nodi, con un livello accettabile di conservazione dei dati (quando nulla viene perso), è in grado di sopravvivere completamente alla morte di un data center. E altri due nodi in ciascuno dei restanti due. E solo dopo iniziano i problemi. Questa è una ridondanza abbastanza buona, vale un paio di unità SSD e processori aggiuntivi. Pertanto, per utilizzare Cassandra, che non è mai SQL, in cui non esistono relazioni, chiavi esterne, è necessario conoscere semplici regole.

Progettiamo tutto secondo la tua richiesta. La cosa principale non sono i dati, ma il modo in cui l'applicazione funzionerà con essi. Se necessita di ricevere dati diversi in modi diversi o gli stessi dati in modi diversi, dobbiamo posizionarli in modo conveniente per l'applicazione. Altrimenti falliremo nella scansione completa e Cassandra non ci darà alcun vantaggio.

La denormalizzazione dei dati è la norma. Ci dimentichiamo delle forme normali, non abbiamo più database relazionali. Se mettiamo giù qualcosa 100 volte, si sdraierà 100 volte. È comunque più economico che fermarsi.

Selezioniamo le chiavi per il partizionamento in modo che siano distribuite normalmente. Non vogliamo che l'hash delle nostre chiavi rientri in un intervallo ristretto. Cioè, l'anno di nascita nell'esempio sopra è un cattivo esempio. Più precisamente, è positivo se i nostri utenti sono normalmente distribuiti per anno di nascita, e negativo se parliamo di studenti di 5a elementare: la suddivisione non sarà molto buona.

L'ordinamento viene selezionato una volta nella fase di creazione della chiave di clustering. Se è necessario modificarlo, dovremo aggiornare la nostra tabella con una chiave diversa.

E la cosa più importante: se dobbiamo recuperare gli stessi dati in 100 modi diversi, avremo 100 tabelle diverse.

Fonte: habr.com

Aggiungi un commento