6.1 Battaglia delle abbreviazioni: BASE vs. ACIDO
"In chimica, il pH misura l'acidità relativa di una soluzione acquosa. La scala del pH va da 0 (sostanze fortemente acide) a 14 (sostanze fortemente alcaline); l'acqua pura a 25°C ha un pH di 7 ed è neutra. Gli ingegneri dei dati hanno preso questa metafora per confrontare i database per quanto riguarda l'affidabilità delle transazioni." Probabilmente, l'idea era questa: più alto è il pH, cioè più il database è vicino a "alcalino" ("BASE"), meno affidabili sono le transazioni. |
I database relazionali popolari, come MySQL, sono apparsi proprio sulla base di ACID. Ma negli ultimi dieci anni, i cosiddetti database NoSQL, che combinano diversi tipi di database molto diversi sotto questo nome, hanno funzionato abbastanza bene senza ACID. In effetti, esiste un gran numero di sviluppatori che lavorano con database NoSQL e non si preoccupano affatto delle transazioni e della loro affidabilità. Vediamo se hanno ragione.
Non puoi parlare in generale del database NoSQL, perché è solo una buona astrazione. I database NoSQL differiscono l'uno dall'altro nella progettazione dei sottosistemi di archiviazione dei dati e persino nei modelli di dati: NoSQL è sia CouchDB orientato ai documenti che Neo4J grafico. Ma se ne parliamo nel contesto delle transazioni, tendono tutte ad essere simili in una cosa: forniscono versioni limitate di atomicità e isolamento, e quindi non forniscono garanzie ACID. Per capire cosa significa, rispondiamo alla domanda: cosa offrono, se non ACID? Niente?
Non proprio. Dopotutto, anche loro, come i database relazionali, devono vendersi in un bel pacchetto. E hanno inventato la loro abbreviazione "chimica" - BASE.
6.2 BASE come antagonista
E anche qui non andrò in ordine di lettere, ma comincerò dal termine fondamentale: coerenza. Dovrò livellare il tuo effetto di riconoscimento, perché questa coerenza ha poco a che fare con la coerenza di ACID. Il problema con il termine coerenza è che viene utilizzato in troppi contesti. Ma questa coerenza ha un contesto di utilizzo molto più ampio, e in effetti questa è esattamente la coerenza che viene discussa quando si parla di sistemi distribuiti.
I database relazionali di cui abbiamo parlato in precedenza forniscono diversi livelli di isolamento delle transazioni e il più rigoroso di essi garantisce che una transazione non possa vedere le modifiche non valide apportate da un'altra transazione. Se ti trovi alla cassa di un negozio e in quel momento il denaro per l'affitto viene prelevato dal tuo conto, ma la transazione con il trasferimento del denaro per l'affitto fallisce e il tuo conto torna al valore precedente (il denaro viene non addebitato), la tua transazione di pagamento alla cassa non noterà tutti questi gesti - dopotutto, quella transazione non è mai andata a buon fine e, in base al requisito dell'isolamento della transazione, le sue modifiche temporanee non possono essere notate da altre transazioni.
Molti database NoSQL rinunciano alla garanzia di isolamento e offrono "coerenza finale" per cui alla fine vedrai dati validi, ma c'è la possibilità che la tua transazione legga valori non validi, ovvero temporanei, parzialmente aggiornati o obsoleti. È possibile che i dati diventino coerenti in modalità "pigra" durante la lettura ("pigramente al momento della lettura").
NoSQL è stato concepito come un database per l'analisi in tempo reale e, per ottenere una maggiore velocità, ha sacrificato la coerenza. Ed Eric Brewer, lo stesso che ha coniato il termine BASE, ha formulato il cosiddetto "teorema CAP", secondo il quale:
Per qualsiasi implementazione del calcolo distribuito, è possibile fornire non più di due delle seguenti tre proprietà:
- coerenza dei dati ( coerenza ) - i dati su diversi nodi (istanze) non si contraddicono a vicenda;
- disponibilità ( disponibilità ) - qualsiasi richiesta a un sistema distribuito termina con una risposta corretta, ma senza la garanzia che le risposte di tutti i nodi del sistema siano le stesse;
- tolleranza della partizione (tolleranza della partizione ) - Anche se non esiste alcuna connessione tra i nodi, continuano a funzionare indipendentemente l'uno dall'altro.
Se vuoi una spiegazione molto semplice della PAC, allora ecco qua.
Ci sono opinioni secondo cui il teorema CAP non funziona ed è generalmente formulato in modo troppo astratto. In un modo o nell'altro, i database NoSQL spesso rifiutano la coerenza nel contesto del teorema CAP, che descrive la seguente situazione: i dati sono stati aggiornati in un cluster con diverse istanze, ma le modifiche non sono state ancora sincronizzate su tutte le istanze. Ricorda, ho menzionato l'esempio DynamoDB sopra, che mi ha detto: le tue modifiche sono diventate durevoli - ecco un HTTP 200 per te - ma ho visto le modifiche solo dopo 10 secondi? Un altro esempio della vita quotidiana di uno sviluppatore è il DNS, il sistema dei nomi di dominio. Se qualcuno non lo sa, allora questo è esattamente il "dizionario" che traduce gli indirizzi http (s) in indirizzi IP.
Il record DNS aggiornato viene propagato ai server in base alle impostazioni dell'intervallo di memorizzazione nella cache, quindi gli aggiornamenti non sono immediatamente visibili. Bene, una simile incoerenza temporale (cioè, alla fine coerenza) può accadere a un cluster di database relazionale (diciamo, MySQL) - dopotutto, questa coerenza non ha nulla a che fare con la coerenza di ACID. Pertanto, è importante capire che in questo senso, è improbabile che i database SQL e NoSQL siano molto diversi quando si tratta di diverse istanze in un cluster.
Inoltre, la coerenza end-to-end può significare che le richieste di scrittura verranno effettuate in modo non ordinato: ovvero verranno scritti tutti i dati, ma il valore che verrà eventualmente ricevuto non sarà l'ultimo nella coda di scrittura.
I database NoSQL non ACID hanno un cosiddetto "stato morbido" dovuto al modello di coerenza end-to-end, il che significa che lo stato del sistema può cambiare nel tempo, anche senza input. Ma tali sistemi si sforzano di fornire una maggiore accessibilità. Fornire una disponibilità al 100% non è un compito banale, quindi stiamo parlando di "disponibilità di base". E insieme questi tre concetti: “basically available”, “soft state” (“soft state”) e “eventual consistent” formano l'acronimo BASE.
Ad essere sincero, il concetto di BASE mi sembra un involucro di marketing più vuoto di ACID, perché non dà nulla di nuovo e non caratterizza in alcun modo il database. E l'applicazione di etichette (ACID, BASE, CAP) a determinati database può solo confondere gli sviluppatori. Ho deciso di presentarti comunque questo termine, perché è difficile aggirarlo quando si studia il database, ma ora che sai cos'è, voglio che te ne dimentichi il prima possibile. E torniamo al concetto di isolamento.
6.3 Quindi i database BASE non soddisfano affatto i criteri ACID?
In sostanza, dove i database ACID differiscono dai non-ACID è che i non-ACID rinunciano effettivamente all'isolamento. Questo è importante da capire. Ma è ancora più importante leggere la documentazione del database e testarla come fanno i ragazzi del progetto Hermitage. Non è così importante come esattamente i creatori di questo o quel database chiamino la loro idea: ACID o BASE, CAP o non CAP. L'importante è cosa fornisce esattamente questo o quel database.
Se i creatori del database affermano che fornisce garanzie ACID, allora probabilmente c'è una ragione per questo, ma è consigliabile testarlo tu stesso per capire se è così e fino a che punto. Se dichiarano che il loro database non fornisce tali garanzie, ciò può significare le seguenti cose:
-
Il DB non fornisce alcuna garanzia di atomicità. Mentre alcuni database NoSQL offrono un'API separata per le operazioni atomiche (ad es. DynamoDB);
- Il DB non fornisce alcuna garanzia di isolamento. Ciò può significare, ad esempio, che il database non scriverà i dati nell'ordine in cui sono stati scritti.
Per quanto riguarda la garanzia di durabilità, molti database scendono a compromessi su questo punto per motivi di prestazioni. Scrivere su disco è un'operazione troppo lunga e ci sono diversi modi per risolvere questo problema. Non voglio entrare molto nella teoria dei database, ma in modo che tu capisca approssimativamente da che parte guardare, descriverò in termini generali come diversi database risolvono il problema con la durabilità.
Per confrontare diversi database, tra le altre cose, è necessario sapere quali strutture di dati sono alla base del sottosistema di archiviazione e recupero dei dati di un particolare database. In breve: diversi database hanno diverse implementazioni dell'indicizzazione, ovvero l'organizzazione dell'accesso ai dati. Alcuni di essi ti consentono di scrivere i dati più velocemente, altri di leggerli più velocemente. Ma non si può dire in generale che alcune strutture di dati aumentino o diminuiscano la durabilità.
6.4 come i diversi database indicizzano i dati e come ciò influisce sulla durabilità e altro ancora
Esistono due approcci principali per l'archiviazione e il recupero dei dati.
Il modo più semplice per salvare i dati è aggiungere operazioni alla fine del file in modo simile a un registro (ovvero, si verifica sempre un'operazione di accodamento): non importa se vogliamo aggiungere, modificare o eliminare dati - tutto Le operazioni CRUD vengono semplicemente scritte nel log. La ricerca nel registro è inefficiente ed è qui che entra in gioco l'indice, una struttura di dati speciale che memorizza i metadati su esattamente dove sono archiviati i dati. La strategia di indicizzazione più semplice per i log è una mappa hash che tiene traccia di chiavi e valori. I valori saranno riferimenti al byte offset per i dati scritti all'interno del file, che è il log (log) ed è memorizzato su disco. Questa struttura dati è archiviata interamente in memoria, mentre i dati stessi sono su disco, ed è chiamata albero LSM (unione strutturata log).
Probabilmente ti sei chiesto: se scriviamo continuamente le nostre operazioni sul diario, allora crescerà in modo esorbitante? Sì, e quindi è stata inventata la tecnica di compattazione, che "ripulisce" i dati con una certa periodicità, ovvero lascia solo il valore più rilevante per ogni chiave, oppure lo cancella. E se abbiamo più di un registro su disco, ma diversi, e sono tutti ordinati, otterremo una nuova struttura dati chiamata SSTable ("tabella di stringhe ordinate"), e questo migliorerà senza dubbio le nostre prestazioni. Se vogliamo ordinare in memoria, otterremo una struttura simile, la cosiddetta MemTable, ma con essa il problema è che se si verifica un arresto anomalo del database, i dati scritti per ultimi (situati in MemTable, ma non ancora scritti in disco) sono persi. In realtà,
Un altro approccio all'indicizzazione si basa sui B-tree ("B-tree"). In un albero B, i dati vengono scritti su disco in pagine di dimensioni fisse. Questi blocchi di dati hanno spesso una dimensione di circa 4 KB e hanno coppie chiave-valore ordinate per chiave. Un nodo B-tree è come un array con collegamenti a un intervallo di pagine. Massimo. il numero di collegamenti in un array è chiamato fattore di diramazione. Ogni intervallo di pagine è un altro nodo B-tree con collegamenti ad altri intervalli di pagine.
Alla fine, a livello di foglio, troverai singole pagine. Questa idea è simile ai puntatori nei linguaggi di programmazione di basso livello, tranne per il fatto che questi riferimenti di pagina sono archiviati su disco anziché in memoria. Quando si verificano INSERT e DELETE nel database, alcuni nodi possono essere suddivisi in due sottoalberi per corrispondere al fattore di ramificazione. Se il database non riesce per qualsiasi motivo nel mezzo del processo, l'integrità dei dati potrebbe essere compromessa. Per evitare che ciò accada, i database che utilizzano alberi B mantengono un "log write-ahead", o WAL, in cui viene registrata ogni singola transazione. Questo WAL viene utilizzato per ripristinare lo stato del B-tree se è danneggiato. E sembra che questo sia ciò che rende i database che utilizzano alberi B migliori in termini di durabilità. Ma i database basati su LSM possono anche mantenere un file che svolge essenzialmente la stessa funzione di WAL. Pertanto, ripeterò quanto ho già detto, e forse più di una volta: capisci i meccanismi di funzionamento del database che hai scelto.
Ciò che è certo dei B-tree, tuttavia, è che sono utili per la transazionalità: ogni chiave si trova in un solo posto nell'indice, mentre i sottosistemi di archiviazione su giornale possono avere più copie della stessa chiave in diversi shard (ad esempio, fino a quando il viene eseguita la successiva compattazione).
Tuttavia, la progettazione dell'indice influisce direttamente sulle prestazioni del database. Con un albero LSM, le scritture su disco sono sequenziali e gli alberi B causano più accessi casuali al disco, quindi le operazioni di scrittura sono più veloci con LSM che con gli alberi B. La differenza è particolarmente significativa per i dischi rigidi magnetici (HDD), dove le scritture sequenziali sono molto più veloci delle scritture casuali. La lettura è più lenta sugli alberi LSM perché devi esaminare diverse strutture di dati e tabelle SS che si trovano in diverse fasi di compattazione. Più in dettaglio sembra così. Se eseguiamo una semplice query sul database con LSM, cercheremo prima la chiave nella MemTable. Se non è presente, esaminiamo la SSTable più recente; in caso contrario, esaminiamo la penultima SSTable e così via. Se la chiave richiesta non esiste, allora con LSM sapremo quest'ultima. Gli alberi LSM sono utilizzati, ad esempio, in: LevelDB, RocksDB, Cassandra e HBase.
Descrivo tutto in modo così dettagliato in modo che tu capisca che quando si sceglie un database, è necessario considerare molte cose diverse: ad esempio, ti aspetti di scrivere o leggere di più i dati. E non ho ancora menzionato la differenza nei modelli di dati (hai bisogno di attraversare i dati, come consente il modello grafico? Ci sono relazioni tra diverse unità nei tuoi dati - quindi i database relazionali verranno in soccorso?), e 2 tipi di schemi di dati: durante la scrittura (come in molti NoSQL) e la lettura (come in relazionale).
Se torniamo all'aspetto della durabilità, la conclusione sarà la seguente: qualsiasi database che scrive su disco, indipendentemente dai meccanismi di indicizzazione, può fornire buone garanzie per la durabilità dei tuoi dati, ma devi occuparti di ogni database specifico , cosa offre esattamente.
6.5 Come funzionano i DB in memoria
A proposito, oltre ai database che scrivono su disco, esistono anche i cosiddetti database "in memoria" che funzionano principalmente con la RAM. In breve, i database in memoria in genere offrono una durata inferiore per motivi di velocità di scrittura e lettura più elevate, ma ciò potrebbe essere appropriato per alcune applicazioni.
Il fatto è che la memoria RAM è stata a lungo più costosa dei dischi, ma recentemente ha iniziato a diventare rapidamente più economica, il che ha dato origine a un nuovo tipo di database, il che è logico, vista la velocità di lettura e scrittura dei dati dalla RAM. Ma giustamente chiederai: che dire della sicurezza dei dati di questi database? Anche in questo caso, è necessario esaminare i dettagli dell'implementazione. In generale, gli sviluppatori di tali database offrono i seguenti meccanismi:
- Puoi usare RAM alimentata da batterie;
- È possibile scrivere i registri delle modifiche su disco (qualcosa come i WAL sopra menzionati), ma non i dati stessi;
- È possibile scrivere periodicamente copie dello stato del database su disco (che, senza utilizzare altre opzioni, non fornisce una garanzia, ma migliora solo la durabilità);
- Puoi replicare lo stato della RAM su altre macchine.
Ad esempio, il database Redis in memoria, utilizzato principalmente come coda di messaggi o cache, manca di durabilità da ACID: non garantisce che un comando eseguito correttamente venga archiviato su disco, poiché Redis scarica i dati su disco (se si avere la persistenza abilitata) solo in modo asincrono, a intervalli regolari.
Tuttavia, questo non è fondamentale per tutte le applicazioni: ho trovato un esempio dell'editor online cooperativo EtherPad, che si scaricava ogni 1-2 secondi, e potenzialmente l'utente poteva perdere un paio di lettere o una parola, il che non era certo critico. Altrimenti, poiché i database in memoria sono utili in quanto forniscono modelli di dati che sarebbero difficili da implementare con gli indici del disco, Redis può essere utilizzato per implementare le transazioni: la sua coda di priorità ti consente di farlo.
GO TO FULL VERSION