
Si dice che nella vita valga la pena provare tutto almeno una volta. E se sei abituato a lavorare con i DB relazionali, sperimentare il NoSQL è fondamentale almeno per una crescita personale. Attualmente, a causa dello sviluppo frenetico di questa tecnologia, ci sono molte opinioni contrastanti e accesi dibattiti su questo tema, che alimentano particolarmente l'interesse.
Se si approfondiscono le ragioni di queste controversie, si può notare che sorgono da un approccio errato. Coloro che utilizzano i database NoSQL esattamente dove sono necessari sono soddisfatti e traggono tutti i benefici da questa soluzione. Al contrario, gli sperimentatori che investono in questa tecnologia come panacea in contesti dove non è applicabile si sentono delusi, perdendo i punti di forza dei database relazionali senza guadagnare vantaggi significativi.
Condividerò la nostra esperienza nell'implementazione di una soluzione basata su Cassandra: le sfide che abbiamo affrontato, come siamo riusciti a superare situazioni difficili, se siamo riusciti a ottenere vantaggi dall'uso del NoSQL e dove abbiamo dovuto investire sforzi/addizionali risorse.
Il compito principale è costruire un sistema che registri le chiamate in un determinato archivio.
Il principio di funzionamento del sistema è il seguente. In ingresso arrivano file con una struttura definita, che descrive la struttura della chiamata. Successivamente, l'applicazione garantisce il salvataggio di questa struttura nelle colonne corrispondenti. Le chiamate salvate vengono poi utilizzate per mostrare informazioni sul consumo di traffico per gli utenti (addebiti, chiamate, storico del saldo).

La scelta di Cassandra è del tutto comprensibile: scrive come una mitragliatrice, è facilmente scalabile e resistente ai guasti.
Ecco cosa ci ha insegnato l'esperienza.
Sì, una node che va offline non è una tragedia. È questo il punto della resilienza di Cassandra. Ma una node può essere attiva e allo stesso tempo iniziare a perdere prestazioni.Come abbiamo scoperto, questo si riflette immediatamente sulle prestazioni dell'intero cluster.
Cassandra non offre la stessa protezione che Oracle garantiva con i suoi vincoli.E se lo sviluppatore dell'applicazione non lo ha capito in tempo, un duplicato in arrivo per Cassandra non è affatto peggiore dell'originale. Se è arrivato, lo inseriamo.
La versione gratuita di Cassandra "out of the box" non è piaciuta affatto agli esperti di sicurezza. non ci sono registri delle attività degli utenti, né limitazioni sui diritti. Le informazioni sulle chiamate riguardano dati personali, il che significa che tutte le tentativi di richiederli/modificarli devono essere registrati con la possibilità di audit successivo. Inoltre, è necessario essere consapevoli della necessità di separare i diritti a diversi livelli per diversi utenti. Un semplice ingegnere operativo e un superamministratore, che può liberamente eliminare tutto il keyspace, hanno ruoli e responsabilità diverse. Senza una tale distinzione dei diritti di accesso, il valore e l'integrità dei dati saranno messi in discussione più rapidamente rispetto a un livello di coerenza ANY.
Non abbiamo considerato che per le chiamate sono necessarie sia analisi approfondite che campionamenti periodici in base a vari criteri. Poiché le registrazioni scelte dovranno poi essere eliminate e riscritte (nell'ambito del compito dobbiamo supportare il processo di aggiornamento dei dati quando inizialmente abbiamo ricevuto dati errati), Cassandra qui non ci sarà d'aiuto. Cassandra, come una cassaforte, è comoda per accumulare dati, ma non è adatta per i calcoli.
Hai riscontrato un problema nel trasferimento dei dati nelle zone di test (5 nodi in test contro 20 in produzione). In questo caso non sarà possibile utilizzare un dump.
Problema con gli aggiornamenti dello schema dei dati dell'applicazione che scrive su Cassandra. Un rollback genererà un numero considerevole di tombstones, il che potrebbe influenzare in modo imprevedibile le nostre prestazioni.. Cassandra è ottimizzata per la scrittura e, prima di scrivere, non pensa molto. Qualsiasi operazione con i dati esistenti è anch'essa una scrittura. Cioè, rimuovendo l'eccesso, generiamo semplicemente più scritture, e solo una parte di esse sarà contrassegnata come tombstones.
Timeout durante l'inserimento. Cassandra è eccellente nella scrittura, ma a volte il flusso in ingresso può metterla seriamente in difficoltà.. Questo accade quando l'applicazione inizia a girare in tondo su diverse scritture che non possono essere inserite per qualche motivo. E avremo bisogno di un vero DBA che monitori gc.log, i log di sistema e di debug in cerca di query lente, metriche su compaction pending.
Diversi datacenter nel cluster. Da dove leggere e dove scrivere?
È possibile separare in lettura e scrittura? E se sì, il DC più vicino all'applicazione dovrebbe essere per scrittura o lettura? E non potremmo avere un vero split brain se scegliamo erroneamente il livello di coerenza? Ci sono tante domande, molte impostazioni esplorative e opportunità che sarebbe bello provare.
Come abbiamo risolto
Per evitare che il nodo crolli, abbiamo disattivato lo SWAP. E ora, in caso di mancanza di memoria, il nodo deve andare giù, invece di generare lunghe pause di gc.
Quindi, non ci aspettiamo più una logica nel DB. Gli sviluppatori dell'app stanno cambiando approccio e iniziano a coprirsi attivamente nel proprio codice. Una separazione perfetta e chiara tra memorizzazione e elaborazione dei dati.
Abbiamo acquistato supporto da DataStax. Lo sviluppo della Cassandra box è stato interrotto (l'ultimo commit è stato effettuato a febbraio 2018). Nel frattempo, Datastax offre un ottimo servizio e molte soluzioni perfezionate e adattate ai sistemi esistenti.
Vorrei anche notare che Cassandra non è molto comoda per le query di estrazione. Certo, CQL rappresenta un grande passo avanti per gli utenti (rispetto a Trift). Ma se hai interi reparti abituati a join facili, a una filtrazione libera su qualsiasi campo e alle possibilità di ottimizzazione delle query, e questi reparti lavorano per risolvere reclami e guasti, allora la scelta di Cassandra sarà vista da loro come ostile e sciocca. Abbiamo iniziato a risolvere come facilitare l'estrazione per i nostri colleghi.
Abbiamo considerato due opzioni. Nella prima opzione registriamo le chiamate non solo in C*, ma anche nel database Oracle archiviato. A differenza di C*, in questo database vengono memorizzate solo le chiamate del mese corrente (un periodo di conservazione sufficiente per i casi di retroattivazione). Qui emergeva subito il seguente problema: se scriviamo in modo sincrono, perdiamo tutti i vantaggi di C* legati all'inserimento veloce; se in modo asincrono, non c'è garanzia che tutte le chiamate necessarie siano arrivate effettivamente in Oracle. Tuttavia, c'era un grande vantaggio: per l'operatività rimane lo stesso familiare PL/SQL Developer, cioè praticamente possiamo implementare il pattern "Facade". L'alternativa: realizziamo un meccanismo che estrae le chiamate da C*, preleva alcuni dati per arricchire dalle tabelle corrispondenti in Oracle, unisce i risultati ottenuti e ci fornisce il risultato finale, che poi utilizziamo in qualche modo (ripristiniamo, ripetiamo, analizziamo, ammiriamo). Svantaggi: il processo risulta piuttosto complesso e, inoltre, manca un'interfaccia per il personale operativo.
Alla fine ci siamo comunque fermati sulla seconda opzione. Per le estrazioni da diverse banche abbiamo utilizzato Apache Spark. Il funzionamento del meccanismo si basa su codice Java che, utilizzando le chiavi specificate (abbonato, ora della chiamata – chiavi della sezione), estrae i dati da C*, insieme ai dati necessari per l'arricchimento da qualsiasi altro database. Successivamente, li unisce in memoria e restituisce il risultato in una tabella finale. Sopra Spark è stata creata un'interfaccia web, rendendola pienamente utilizzabile.

Nella risoluzione del problema di aggiornamento dei dati, il team di prom-test ha nuovamente esaminato diversi metodi. Sia il trasferimento tramite Sstloader, sia la divisione del cluster nella zona di test in due parti, ciascuna delle quali entra alternativamente in un cluster con quello di produzione, alimentandosi da esso. Durante l'aggiornamento del test, era previsto che le parti cambiassero posto: quella che lavorava nel test veniva pulita e introdotta in produzione, mentre l'altra iniziava a lavorare con i dati separatamente. Tuttavia, riflettendo ulteriormente, abbiamo valutato in modo più razionale i dati da trasferire e abbiamo compreso che le stesse chiamate sono un'entità inconsistenti per i test, generate rapidamente in caso di necessità, e che proprio il set di dati di produzione non ha valore per il trasferimento nel test. Ci sono diversi oggetti di accumulo che vale la pena trasferire, ma si tratta di letteralmente un paio di tabelle, e non molto pesanti. Pertanto, ci è venuto nuovamente in aiuto Spark, con il quale abbiamo scritto e iniziato a utilizzare attivamente uno script di trasferimento dei dati tra le tabelle di produzione e test.
La nostra attuale politica di deploy ci consente di operare senza rollback. Prima di ogni promozione, è necessario un test preliminare, dove un errore ha un costo contenuto. In caso di fallimento, è sempre possibile ripristinare il case space e ricominciare da capo con l'intera configurazione.
Per garantire la disponibilità continua di Cassandra, è necessario un DBA e non solo lui. Tutti coloro che lavorano con l'applicazione devono comprendere dove e come monitorare la situazione attuale e diagnosticare tempestivamente eventuali problemi. A tal fine, utilizziamo attivamente DataStax OpsCenter (gestione e monitoraggio dei carichi di lavoro), metriche di sistema del Cassandra Driver (numero di timeout in scrittura in C*, numero di timeout in lettura da C*, latenza massima, ecc.), e monitoriamo il funzionamento stesso dell'applicazione che interagisce con Cassandra.
Quando abbiamo riflettuto sulla domanda precedente, ci siamo resi conto di dove potrebbe trovarsi il principale rischio. Si tratta delle modalità di visualizzazione dei dati che estraggono informazioni da più query indipendenti tra loro. In questo modo, potremmo ottenere informazioni piuttosto incoerenti. Tuttavia, questo problema sarebbe altrettanto pertinente anche se lavorassimo con un solo data center. Pertanto, la cosa più sensata da fare è, ovviamente, implementare una funzione batch per la lettura dei dati in un'applicazione esterna, che garantisca l'ottenimento dei dati in un unico intervallo di tempo. Per quanto riguarda la separazione tra lettura e scrittura in termini di prestazioni, qui ci ha frenato il rischio che, in caso di una certa perdita di connessione tra i data center, potremmo ricevere due cluster completamente incoerenti tra loro.
Di conseguenza, al momento ci siamo fermati a un livello di coerenza per la scrittura di EACH_QUORUM, per la lettura – LOCAL_QUORUM
Impressioni e conclusioni sintetiche
Per valutare la soluzione ottenuta in termini di supporto operativo e prospettive di ulteriore sviluppo, abbiamo deciso di riflettere su dove altro potrebbe essere applicato questo sviluppo.
Se parliamo subito, il punteggio dei dati per programmi come "Paga quando vuoi" (caricando informazioni in S*, calcolo tramite script Spark), la gestione dei reclami con aggregazione per direzioni, la memorizzazione dei ruoli e il calcolo in base alla matrice dei diritti di accesso degli utenti.
Come possiamo notare, il repertorio è ampio e variegato. E se dobbiamo scegliere da che parte schierarci, tra sostenitori e avversari di NoSQL, ci uniremo ai sostenitori, dato che ne abbiamo ricevuto beneficità, precisamente dove ce lo aspettavamo.
Anche la versione di Cassandra out-of-the-box consente la scalabilità orizzontale in tempo reale, risolvendo senza problemi la questione dell'aumento dei dati nel sistema. Siamo riusciti a separare in un contesto distinto un meccanismo ad alta intensità di carico per il calcolo degli aggregati delle chiamate, e a separare lo schema e la logica dell'applicazione, liberandoci dalla cattiva prassi di scrivere job e oggetti personalizzati direttamente nel database. Abbiamo ottenuto la possibilità di scegliere e configurare, per ottimizzare, in quali data center eseguiremo il calcolo e in quali scriveremo i dati, mettendo in protezione i nostri interessi in caso di guasti sia di singoli nodi che dell'intero data center.
Applicando la nostra architettura ai nuovi progetti, e avendo già un'esperienza preliminare, è fondamentale considerare fin da subito le peculiarità descritte sopra, evitando alcuni errori e smussando gli angoli più spigolosi che inizialmente non siamo riusciti a evitare.
Ad esempio, monitorare tempestivamente gli aggiornamenti di Cassandra, poiché molte delle problematiche riscontrate erano già note e sono state corrette.
Evitare di posizionare sia il database che Spark sulle stesse nodi (o separare rigorosamente in base alla capacità massima delle risorse utilizzate), poiché Spark può utilizzare più RAM di quanto previsto, e questo porterà rapidamente al problema numero 1 della nostra lista.
Potenziare il monitoraggio e le competenze operative già nella fase di test del progetto. Considerare sin dall'inizio tutti i potenziali utenti della nostra soluzione, poiché la struttura del database dipenderà in ultima analisi proprio da questo.
Esaminare più volte il diagramma risultante per possibili ottimizzazioni. Identificare quali campi possono essere serializzati. Comprendere quali tabelle aggiuntive dobbiamo creare per tenere conto in modo più accurato ed ottimale e per fornire successivamente le informazioni richieste (ad esempio, tenendo presente che possiamo conservare gli stessi dati in tabelle diverse, a seconda di diversi criteri, possiamo risparmiare notevolmente tempo di CPU durante le query di lettura).
Non male prevedere subito l'assegnazione di TTL e la pulizia dei dati obsoleti.
Durante l'esportazione dei dati da Cassandra la logica dell'applicazione deve funzionare secondo il principio FETCH, per evitare che tutte le righe siano caricate in memoria contemporaneamente, ma selezionate a pacchetti.
È consigliabile verificare la tolleranza ai guasti del sistema, eseguendo una serie di crash test prima di trasferire il progetto alla soluzione descritta. verifica la resilienza del sistema eseguendo una serie di crash test, come la perdita di dati in un datacenter, il ripristino di dati danneggiati in un certo periodo, problemi di rete tra i datacenter. Questi test non solo permetteranno di valutare i pro e i contro dell'architettura proposta, ma daranno anche una buona opportunità di pratica ai tecnici che li eseguiranno, e le competenze acquisite non saranno affatto superflue nel caso di malfunzionamenti del sistema.
Se lavoriamo con informazioni critiche (come i dati per la fatturazione, il calcolo dei debiti degli abbonati), è importante prestare attenzione agli strumenti che possono contribuire a ridurre i rischi derivanti dalle peculiarità del DBMS. Ad esempio, utilizzare l'utilità nodesync (Datastax), sviluppando una strategia ottimale per il suo utilizzo, affinché per garantire coerenza non si crei un carico eccessivo su Cassandra. e utilizzarla solo per determinate tabelle in determinati periodi.
Beh, dopo sei mesi di utilizzo con Cassandra? In generale, non ci sono problemi irrisolti. Non abbiamo nemmeno avuto seri incidenti o perdite di dati. Sì, abbiamo dovuto riflettere su come compensare alcuni problemi che non si erano mai presentati prima, ma alla fine questo non ha particolarmente offuscato la nostra soluzione architettonica. Se siete pronti e non temete di provare qualcosa di nuovo, e al contempo non volete rimanere troppo delusi, preparatevi al fatto che nulla è davvero gratuito. Dovrete impegnarvi, approfondire la documentazione e costruire i vostri specifici ostacoli più del necessario rispetto a una vecchia soluzione legacy, e nessuna teoria vi potrà anticipare quali ostacoli vi attendono.
Fonte: habr.com
