Sistemi di database distribuiti 101
Oppure, Database distribuiti - cosa significa in realtà l'FK " scala web "?
I sistemi di database distribuiti sono creature complesse e sono disponibili in diversi gusti. Se approfondirò i miei studi vagamente ricordati su questo all'università, proverò a spiegare alcuni dei principali problemi di ingegneria nella costruzione di un sistema di database distribuito.
Innanzitutto, un po 'di terminologia
Proprietà ACID (Atomicità, Coerenza, Isolamento e Durabilità): questi sono gli invarianti chiave che devono essere applicati per implementare una transazione in modo affidabile senza causare effetti collaterali indesiderati.
Atomicity richiede il completamento o il rollback completo della transazione. Le transazioni parzialmente completate non dovrebbero mai essere visibili e il sistema deve essere costruito in modo da impedire che ciò accada.
La coerenza richiede che una transazione non debba mai violare invarianti (come l'integrità referenziale dichiarativa) garantiti dallo schema del database. Ad esempio, se esiste una chiave esterna, dovrebbe essere impossibile inserire un record figlio con riverenza a un genitore inesistente.
L'isolamento richiede che le transazioni non interferiscano tra loro. Il sistema dovrebbe garantire gli stessi risultati se le transazioni vengono eseguite in parallelo o in sequenza. In pratica, la maggior parte dei prodotti RDBMS consente modalità che compromettono l'isolamento rispetto alle prestazioni.
La durabilità richiede che, una volta impegnata, la transazione rimanga in archivio persistente in modo robusto per guasti hardware o software.
Spiegherò alcuni degli ostacoli tecnici che questi requisiti presentano sui sistemi distribuiti di seguito.
Architettura del disco condivisa: un'architettura in cui tutti i nodi di elaborazione in un cluster hanno accesso a tutto l'archiviazione. Ciò può presentare un collo di bottiglia centrale per l'accesso ai dati. Un esempio di un sistema a disco condiviso è Oracle RAC o Exadata .
Shared Nothing Architecture: un'architettura in cui i nodi di elaborazione in un cluster dispongono di memoria locale non visibile ad altri nodi del cluster. Esempi di sistemi a nulla condiviso sono Teradata e Netezza .
Architettura della memoria condivisa: un'architettura in cui più CPU (o nodi) possono accedere a un pool condiviso di memoria. I server più moderni hanno un tipo di memoria condivisa. La memoria condivisa facilita alcune operazioni come cache o primitive di sincronizzazione atomica che sono molto più difficili da eseguire su sistemi distribuiti.
Sincronizzazione: un termine generico che descrive vari metodi per garantire l'accesso coerente a una risorsa condivisa da più processi o thread. Questo è molto più difficile da fare su sistemi distribuiti che su sistemi di memoria condivisa, sebbene alcune architetture di rete (ad esempio BYNET di Teradata) presentassero primitive di sincronizzazione nel protocollo di rete. La sincronizzazione può anche comportare una notevole quantità di costi generali.
Semi-Join: una primitiva utilizzata per unire i dati contenuti in due diversi nodi di un sistema distribuito. Fondamentalmente è costituito da informazioni sufficienti sulle righe per unire il raggruppamento e il passaggio da un nodo all'altro al fine di risolvere il join. In una query di grandi dimensioni ciò potrebbe comportare un notevole traffico di rete.
Coerenza finale: un termine usato per descrivere la semantica delle transazioni che compensano l'aggiornamento immediato (coerenza sulle letture) su tutti i nodi di un sistema distribuito per le prestazioni (e quindi un throughput delle transazioni più elevato) nelle scritture. L'eventuale coerenza è un effetto collaterale dell'utilizzo della replica quorum come ottimizzazione delle prestazioni per accelerare gli commit delle transazioni in database distribuiti in cui più copie dei dati sono conservate su nodi separati.
Algoritmo di Lamport: un algoritmo per implementare l'esclusione reciproca (sincronizzazione) tra sistemi senza memoria condivisa. Normalmente l'esclusione reciproca all'interno di un sistema richiede un'istruzione atomica read-compare-write o simile di un tipo normalmente pratica solo su un sistema di memoria condivisa. Esistono altri algoritmi di sincronizzazione distribuiti, ma Lamport è stato uno dei primi ed è il più noto. Come la maggior parte dei meccanismi di sincronizzazione distribuiti, l'algoritmo di Lamport è fortemente dipendente da tempistiche accurate e sincronizzazione dei nodi del nodo di sincronizzazione.
Two Phase Commit (2PC): una famiglia di protocolli che garantisce che gli aggiornamenti del database che coinvolgono più sistemi fisici eseguano il commit o il rollback in modo coerente. Se 2PC viene utilizzato all'interno di un sistema o tra più sistemi tramite un gestore delle transazioni, comporta un notevole sovraccarico.
In un protocollo di commit in due fasi, il gestore delle transazioni chiede ai nodi partecipanti di persistere nella transazione in modo tale da garantire che si impegnerà, quindi segnalare questo stato. Quando tutti i nodi hanno restituito uno stato "felice", segnala ai nodi di eseguire il commit. La transazione è ancora considerata aperta fino a quando tutti i nodi non inviano una risposta indicando che il commit è completo. Se un nodo si arresta prima di segnalare che il commit è completo, il gestore delle transazioni interrogherà nuovamente il nodo quando torna su fino a quando non ottiene una risposta positiva indicando che la transazione è stata impegnata.
Controllo di concorrenza multi-versione (MVCC): gestione della contesa scrivendo nuove versioni dei dati in una posizione diversa e consentendo ad altre transazioni di vedere la vecchia versione dei dati fino al commit della nuova versione. Ciò riduce la contesa del database a spese di un ulteriore traffico di scrittura per scrivere la nuova versione e quindi contrassegnare la vecchia versione come obsoleta.
Algoritmo elettorale: i sistemi distribuiti che coinvolgono più nodi sono intrinsecamente meno affidabili di un singolo sistema in quanto vi sono più modalità di errore. In molti casi è necessario un meccanismo per i sistemi cluster per far fronte al fallimento di un nodo. Gli algoritmi elettorali sono una classe di algoritmi utilizzati per selezionare un leader per coordinare un calcolo distribuito in situazioni in cui il nodo "leader" non è determinato o affidabile al 100%.
Partizionamento orizzontale: una tabella può essere suddivisa su più nodi o volumi di archiviazione tramite la sua chiave. Ciò consente a un grande volume di dati di essere suddiviso in blocchi più piccoli e distribuito su nodi di archiviazione.
Frammentazione: un set di dati può essere partizionato orizzontalmente su più nodi fisici in un'architettura a nulla condiviso. Laddove questo partizionamento non è trasparente (ovvero il client deve essere consapevole dello schema di partizione e capire quale nodo interrogare esplicitamente), questo è noto come sharding. Alcuni sistemi (ad esempio Teradata) suddividono i dati tra nodi ma la posizione è trasparente per il client; il termine non viene normalmente utilizzato insieme a questo tipo di sistema.
Hashing coerente: un algoritmo utilizzato per allocare i dati alle partizioni in base alla chiave. È caratterizzato dalla distribuzione uniforme delle chiavi hash e dalla possibilità di espandere o ridurre in modo elastico il numero di bucket in modo efficiente. Questi attributi lo rendono utile per il partizionamento dei dati o il caricamento su un cluster di nodi in cui la dimensione può cambiare dinamicamente con l'aggiunta o la caduta di nodi dal cluster (forse a causa di un errore).
Replica multi-master: una tecnica che consente di replicare negli altri nodi le scritture su più nodi in un cluster. Questa tecnica facilita il ridimensionamento consentendo a alcune tabelle di essere partizionate o suddivise tra server e altre per essere sincronizzate attraverso il cluster. Le scritture devono essere replicate su tutti i nodi rispetto a un quorum, quindi i commit delle transazioni sono più costosi su un'architettura replicata multi-master che su un sistema replicato quorum.
Switch non bloccante: switch di rete che utilizza il parallelismo hardware interno per ottenere un throughput proporzionale al numero di porte senza colli di bottiglia interni. Un'implementazione ingenua può usare un meccanismo a barra, ma questo ha una complessità O (N ^ 2) per le porte N, limitandola a switch più piccoli. Gli switch più grandi possono utilizzare una topologia interna più complessa denominata switch di spanning minimo non bloccante per ottenere il ridimensionamento lineare della velocità effettiva senza la necessità di hardware O (N ^ 2).
Realizzare un DBMS distribuito: quanto può essere difficile?
Diverse sfide tecniche rendono questo abbastanza difficile da fare in pratica. Oltre alla complessità aggiunta della costruzione di un sistema distribuito, l'architetto di un DBMS distribuito deve superare alcuni complicati problemi di ingegneria.
Atomicità su sistemi distribuiti: se i dati aggiornati da una transazione sono distribuiti su più nodi, è necessario coordinare il commit / rollback dei nodi. Ciò aggiunge un notevole sovraccarico sui sistemi a nulla condiviso. Sui sistemi a disco condiviso questo è meno un problema in quanto tutta la memoria può essere vista da tutti i nodi in modo che un singolo nodo possa coordinare il commit.
Coerenza su sistemi distribuiti: per prendere l'esempio di chiave esterna sopra citato, il sistema deve essere in grado di valutare uno stato coerente. Ad esempio, se il padre e il figlio di una relazione di chiave esterna potrebbero risiedere su nodi diversi, è necessario una sorta di meccanismo di blocco distribuito per garantire che le informazioni obsolete non vengano utilizzate per convalidare la transazione. Se ciò non viene applicato, si potrebbe avere (ad esempio) una condizione di competizione in cui il genitore viene eliminato dopo che la sua presenza è stata verificata prima di consentire l'inserimento del figlio.
L'applicazione ritardata dei vincoli (ovvero l'attesa fino al commit per convalidare il DRI) richiede che il blocco venga mantenuto per la durata della transazione. Questo tipo di blocco distribuito comporta un notevole sovraccarico.
Se vengono conservate più copie dei dati (ciò potrebbe essere necessario sui sistemi a condivisione nulla per evitare il traffico di rete non necessario dai semi-join), tutte le copie dei dati devono essere aggiornate.
Isolamento su sistemi distribuiti: laddove i dati interessati su una transazione risiedano su più nodi di sistema, i blocchi e la versione (se MVCC è in uso) devono essere sincronizzati tra i nodi. Garantire la serializzazione delle operazioni, in particolare su architetture condivise, dove è possibile archiviare copie ridondanti di dati richiede un meccanismo di sincronizzazione distribuito come l'algoritmo di Lamport, che comporta anche un notevole sovraccarico nel traffico di rete.
Durabilità sui sistemi distribuiti: su un sistema a dischi condivisi il problema della durabilità è essenzialmente lo stesso di un sistema a memoria condivisa, con l'eccezione che i protocolli di sincronizzazione distribuita sono ancora richiesti tra i nodi. Il DBMS deve scrivere nel diario nel registro e scrivere i dati in modo coerente. Su un sistema a nulla condiviso potrebbero esserci più copie dei dati o parti dei dati memorizzati su nodi diversi. È necessario un protocollo di commit in due fasi per garantire che il commit avvenga correttamente su tutti i nodi. Ciò comporta anche notevoli spese generali.
In un sistema a nulla condiviso la perdita di un nodo può significare che i dati non sono disponibili per il sistema. Per mitigare questi dati può essere replicato su più di un nodo. Coerenza in questa situazione significa che i dati devono essere replicati in tutti i nodi in cui risiedono normalmente. Ciò può comportare notevoli spese generali per le scritture.
Un'ottimizzazione comune realizzata nei sistemi NoSQL è l'uso della replica del quorum e dell'eventuale coerenza per consentire ai dati di essere replicati pigramente garantendo al contempo un certo livello di resilienza dei dati scrivendo a un quorum prima di riportare la transazione come impegnata. I dati vengono quindi replicati pigramente sugli altri nodi in cui risiedono copie dei dati.
Si noti che l '"eventuale coerenza" è un importante compromesso sulla coerenza che potrebbe non essere accettabile se i dati devono essere visualizzati in modo coerente non appena la transazione viene impegnata. Ad esempio, in un'applicazione finanziaria un saldo aggiornato dovrebbe essere immediatamente disponibile.
Sistemi a dischi condivisi
Un sistema a disco condiviso è quello in cui tutti i nodi hanno accesso a tutto l'archiviazione. Pertanto, il calcolo è indipendente dalla posizione. Molte piattaforme DBMS possono funzionare anche in questa modalità: Oracle RAC è un esempio di tale architettura.
I sistemi di dischi condivisi possono ridimensionarsi in modo sostanziale in quanto possono supportare una relazione M: M tra nodi di archiviazione e nodi di elaborazione. Una SAN può avere più controller e più server possono eseguire il database. Queste architetture hanno un interruttore come collo di bottiglia centrale ma gli interruttori della barra trasversale consentono a questo interruttore di avere molta larghezza di banda. Alcune elaborazioni possono essere scaricate sui nodi di archiviazione (come nel caso degli Exadata di Oracle) che possono ridurre il traffico sulla larghezza di banda di archiviazione.
Sebbene lo switch sia teoricamente un collo di bottiglia, la larghezza di banda disponibile significa che le architetture dei dischi condivisi si ridimensioneranno in modo abbastanza efficace per grandi volumi di transazioni. La maggior parte delle architetture DBMS tradizionali adottano questo approccio perché offre scalabilità "abbastanza buona" e alta affidabilità. Con un'architettura di archiviazione ridondante come il canale in fibra ottica non esiste un singolo punto di errore in quanto vi sono almeno due percorsi tra qualsiasi nodo di elaborazione e qualsiasi nodo di archiviazione.
Sistemi condivisi-niente
I sistemi a nulla condiviso sono sistemi in cui almeno alcuni dei dati sono conservati localmente su un nodo e non sono direttamente visibili ad altri nodi. Ciò elimina il collo di bottiglia di un interruttore centrale, consentendo al database di ridimensionare (almeno in teoria) con il numero di nodi. Il partizionamento orizzontale consente di suddividere i dati tra nodi; questo può essere trasparente per il cliente o meno (vedi Sharding sopra).
Poiché i dati sono intrinsecamente distribuiti, una query può richiedere dati da più di un nodo. Se un join richiede dati da nodi diversi, viene utilizzata un'operazione di semi-join per trasferire dati sufficienti per supportare il join da un nodo a un altro. Ciò può comportare una grande quantità di traffico di rete, quindi l'ottimizzazione della distribuzione dei dati può fare una grande differenza per le prestazioni delle query.
Spesso, i dati vengono replicati su nodi di un sistema a nulla condiviso per ridurre la necessità di semi-join. Funziona abbastanza bene su dispositivi di data warehouse in quanto le dimensioni sono generalmente di molti ordini di grandezza inferiori rispetto alle tabelle dei fatti e possono essere facilmente replicate su nodi. Inoltre, vengono generalmente caricati in batch, quindi l'overhead della replica è meno problematico di quanto non sarebbe su un'applicazione transazionale.
Il parallelismo intrinseco di un'architettura a nulla condiviso li rende adatti al tipo di query table scan / aggregate caratteristiche di un data warehouse. Questo tipo di operazione può ridimensionarsi in modo quasi lineare con il numero di nodi di elaborazione. I join di grandi dimensioni tra i nodi tendono a comportare un sovraccarico in quanto le operazioni di semi-join possono generare molto traffico di rete.
Lo spostamento di grandi volumi di dati è meno utile per le applicazioni di elaborazione delle transazioni, in cui l'overhead di più aggiornamenti rende questo tipo di architettura meno attraente di un disco condiviso. Pertanto, questo tipo di architettura tende a non essere ampiamente utilizzato dalle applicazioni di data warehouse.
Cocci, replica del quorum ed eventuale coerenza
La replica quorum è una funzione in cui un DBMS replica i dati per l'alta disponibilità. Ciò è utile per i sistemi destinati a funzionare su hardware di prodotti più economici che non dispone di funzionalità integrate ad alta disponibilità come una SAN. In questo tipo di sistema i dati vengono replicati su più nodi di archiviazione per prestazioni di lettura e archiviazione ridondante per rendere il sistema resiliente ai guasti hardware di un nodo.
Tuttavia, la replica delle scritture su tutti i nodi è O (M x N) per i nodi M e N. Ciò rende le scritture costose se la scrittura deve essere replicata su tutti i nodi prima che una transazione sia autorizzata a eseguire il commit. La replica del quorum è un compromesso che consente di replicare immediatamente le scritture in un sottoinsieme dei nodi e di scriverle pigramente negli altri nodi da un'attività in background. Le scritture possono essere impegnate più rapidamente, fornendo al contempo un certo grado di ridondanza garantendo che vengano replicate in un sottoinsieme minimo (quorum) di nodi prima che la transazione venga segnalata come impegnata per il client.
Ciò significa che la lettura dei nodi al di fuori del quorum può visualizzare versioni obsolete dei dati fino a quando il processo in background non ha terminato la scrittura dei dati nel resto dei nodi. La semantica è nota come "eventuale coerenza" e può essere o non essere accettabile a seconda dei requisiti dell'applicazione, ma significa che gli commit delle transazioni sono più vicini a O (1) rispetto a O (n) nell'uso delle risorse.
La frammentazione richiede che il client sia consapevole del partizionamento dei dati all'interno dei database, spesso usando un tipo di algoritmo noto come "hashing coerente". In un database frammentato il client esegue l'hashing della chiave per determinare a quale server nel cluster emettere la query. Poiché le richieste sono distribuite tra i nodi del cluster, non vi sono colli di bottiglia con un singolo nodo coordinatore di query.
Queste tecniche consentono a un database di ridimensionarsi a una velocità quasi lineare aggiungendo nodi al cluster. Teoricamente, la replica del quorum è necessaria solo se il supporto di memorizzazione sottostante deve essere considerato inaffidabile. Ciò è utile se devono essere utilizzati server di prodotti di base, ma ha un valore inferiore se il meccanismo di archiviazione sottostante ha il proprio schema di disponibilità elevata (ad esempio una SAN con controller con mirroring e connettività multi-percorso agli host).
Ad esempio, BigTable di Google non implementa la replica quorum da sola, sebbene si trovi su GFS, un file system cluster che utilizza la replica quorum. BigTable (o qualsiasi sistema a nulla condiviso) potrebbe utilizzare un sistema di archiviazione affidabile con più controller e partizionare i dati tra i controller. L'accesso parallelo sarebbe quindi ottenuto attraverso il partizionamento dei dati.
Torna alle piattaforme RDBMS
Non vi è alcuna ragione intrinseca che queste tecniche non possano essere utilizzate con un RDBMS. Tuttavia, la gestione dei blocchi e delle versioni sarebbe piuttosto complessa su un tale sistema e ogni mercato per tale sistema è probabilmente piuttosto specializzato. Nessuna delle piattaforme RDBMS tradizionali utilizza la replica del quorum e non sono specificamente a conoscenza di alcun prodotto RDBMS (almeno non uno con un assorbimento significativo) che lo fa.
I sistemi a disco condiviso e nulla condiviso possono scalare fino a carichi di lavoro molto grandi. Ad esempio, Oracle RAC può supportare 63 nodi di elaborazione (che potrebbero essere macchine SMP di grandi dimensioni a sé stanti) e un numero arbitrario di controller di archiviazione sulla SAN. Un IBM Sysplex (un cluster di mainframe zSeries) può supportare più mainframe (ciascuno con una notevole potenza di elaborazione e larghezza di banda I / O propria) e più controller SAN. Queste architetture possono supportare volumi di transazioni molto grandi con la semantica ACID, sebbene presuppongano una memorizzazione affidabile. Teradata, Netezza e altri fornitori realizzano piattaforme analitiche ad alte prestazioni basate su progetti a nulla condiviso che si adattano a volumi di dati estremamente grandi.
Finora, il mercato delle piattaforme RDBMS completamente ACID a basso volume ma ad altissimo volume è dominato da MySQL, che supporta lo sharding e la replica multi-master. MySQL non utilizza la replica quorum per ottimizzare la velocità di scrittura, quindi i commit delle transazioni sono più costosi rispetto a un sistema NoSQL. La frammentazione consente velocità di lettura molto elevate (ad esempio Facebook utilizza MySQL in modo estensivo), quindi questo tipo di architettura si adatta bene ai carichi di lavoro pesanti.
Un dibattito interessante
BigTable è un'architettura a nulla condiviso (essenzialmente una coppia chiave-valore distribuita) come sottolineato da Michael Hausenblas di seguito . La mia valutazione originale includeva il motore MapReduce, che non fa parte di BigTable ma che verrebbe normalmente utilizzato in combinazione con esso nelle sue implementazioni più comuni (ad esempio Hadoop / HBase e il framework MapReduce di Google).
Confrontando questa architettura con Teradata, che ha affinità fisica tra archiviazione ed elaborazione (ovvero i nodi hanno memoria locale piuttosto che una SAN condivisa) si potrebbe sostenere che BigTable / MapReduce è un'architettura disco condivisa attraverso il sistema di archiviazione parallela visibile globalmente.
La velocità di elaborazione di un sistema in stile MapReduce come Hadoop è limitata dalla larghezza di banda di uno switch di rete non bloccante. 1 Gli switch non bloccanti possono, tuttavia, gestire aggregati di larghezza di banda elevati a causa del parallelismo insito nella progettazione, quindi raramente rappresentano un vincolo pratico significativo per le prestazioni. Ciò significa che un'architettura di disco condivisa (forse meglio definita come sistema di archiviazione condiviso) può ridimensionare a grandi carichi di lavoro anche se lo switch di rete è teoricamente un collo di bottiglia centrale.
Il punto originale era notare che sebbene questo collo di bottiglia centrale esista nei sistemi a disco condiviso, un sottosistema di archiviazione partizionato con più nodi di archiviazione (ad esempio server tablet BigTable o controller SAN) può comunque scalare fino a grandi carichi di lavoro. Un'architettura switch non bloccante può (in teoria) gestire tutte le connessioni correnti quante sono le porte.
1 Naturalmente anche la velocità di elaborazione e I / O disponibile costituisce un limite alle prestazioni, ma lo switch di rete è un punto centrale attraverso il quale passa tutto il traffico.