SQL distribuito: quando usare Spanner, CockroachDB e YugabyteDB
Scopri quando lo SQL distribuito giustifica il suo costo, come si confrontano Spanner, CockroachDB e YugabyteDB e come pianificare in sicurezza carichi multi-regione.

Cosa significa SQL distribuito
Lo SQL distribuito è un'architettura di database relazionale che distribuisce dati ed elaborazione delle transazioni su più macchine, presentando alle applicazioni un unico database SQL logico. Mantiene tabelle, join, indici, vincoli e transazioni ACID, aggiungendo partizionamento automatico, replica e recupero dai guasti.
Un sistema rientra in questa categoria quando combina in genere queste proprietà:
- Uno schema relazionale e un'interfaccia di query SQL
- Scalabilità orizzontale tra nodi del database
- Coerenza transazionale tra partizioni
- Replica e failover automatici
- Funzionamento coordinato come unico database logico
La definizione conta perché un database non diventa SQL distribuito limitandosi ad aggiungere repliche di lettura a PostgreSQL o MySQL. Una primaria con repliche invia comunque le scritture a un solo server principale. Lo sharding gestito dall'applicazione distribuisce le scritture, ma costringe l'applicazione a decidere dove risiedono i record e come gestire le operazioni tra shard. Lo SQL distribuito trasferisce al database gran parte di questa responsabilità.
La posizione tra RDBMS convenzionale e NoSQL
Lo SQL distribuito unisce il modello di programmazione relazionale di un RDBMS convenzionale al design scale-out associato agli archivi dati distribuiti. Le installazioni tradizionali di PostgreSQL e MySQL funzionano bene quando un'istanza primaria può gestire il carico di scrittura e un guasto regionale non richiede di continuare a scrivere altrove. Repliche di lettura, caching, pool di connessioni e indici migliori possono estendere questo modello per anni.
Molti database NoSQL hanno scelto una distribuzione più semplice limitando join, transazioni o garanzie di coerenza. Queste scelte restano sensate per carichi come grandi flussi di eventi, cache sacrificabili e record che raramente partecipano a transazioni su più righe. Un cluster relazionale richiede più coordinamento perché le applicazioni si aspettano che vincoli e transazioni restino validi dopo che i dati sono stati divisi tra nodi.
La distinzione pratica riguarda chi possiede la complessità. Con lo sharding manuale, i team applicativi implementano il routing, ribilanciano i dati, coordinano le modifiche dello schema e gestiscono operazioni che toccano più shard. Con lo SQL distribuito, il database fornisce questi meccanismi, anche se gli ingegneri devono comunque progettare schemi e query per un sistema in rete.
I problemi che è progettato per risolvere
Lo SQL distribuito è pensato per applicazioni la cui disponibilità, collocazione geografica o crescita delle scritture ha superato un'architettura con una sola primaria. Esempi comuni sono un servizio SaaS globale, un sistema di prenotazione che non può vendere oltre la disponibilità e un libro mastro finanziario i cui invarianti devono resistere ai guasti dei nodi.
Può eliminare la necessità di sharding a livello applicativo e ridurre la dipendenza da un'unica posizione di scrittura. Può anche collocare i dati vicino agli utenti o all'interno di giurisdizioni approvate. Questi vantaggi hanno un costo: più repliche, più traffico di rete, più coordinamento e modalità di guasto che non esistono su un solo server.
Un database relazionale gestito convenzionale resta l'impostazione predefinita migliore quando il carico rientra comodamente in una regione. Lo SQL distribuito giustifica il proprio costo quando sharding personalizzato, failover regionale o controlli geografici sui dati altrimenti diventerebbero un grande sistema ingegneristico a sé stante.
Come funziona internamente lo SQL distribuito
Lo SQL distribuito funziona dividendo i dati in partizioni replicate e coordinando le modifiche tramite consenso e protocolli di transazione distribuita. Il database nasconde gran parte di questi meccanismi dietro SQL, ma il loro comportamento determina comunque latenza, throughput, progettazione dello schema e risposta agli incidenti.
Le partizioni determinano dove risiedono i record
Un cluster divide le proprie tabelle logiche in unità più piccole che possono spostarsi indipendentemente tra nodi. Spanner descrive comunemente queste unità come split, CockroachDB usa range e YugabyteDB usa tablet. Ogni unità copre una parte dello spazio delle chiavi di una tabella o di un indice.
I confini delle partizioni possono seguire intervalli, hash o regole geografiche esplicite. Un intervallo ordinato per identificatore cliente rende facili da scansionare i record correlati, ma un identificatore monotonicamente crescente può indirizzare le nuove scritture verso una partizione. La distribuzione hash distribuisce le scritture in modo più uniforme, ma può rendere più difficili le scansioni ordinate o il posizionamento per tenant. Molti schemi di produzione combinano un identificatore del tenant con un altro valore, così i dati correlati restano accessibili senza concentrare ogni scrittura in un unico punto.
Gli indici secondari richiedono il proprio storage distribuito. Una scrittura su una riga può quindi aggiornare la tabella di base e più voci di indice su partizioni differenti. Un indice economico su un singolo server può generare ulteriore lavoro di consenso e traffico di rete in un cluster.
Replica e consenso proteggono ogni partizione
Ogni partizione dispone normalmente di più repliche e un gruppo di consenso decide la sequenza accettata delle modifiche. CockroachDB e YugabyteDB usano replica basata su Raft. Spanner usa replica basata su Paxos insieme alla propria infrastruttura temporale.
Un leader o leaseholder coordina le scritture per un gruppo di repliche. Il sistema registra una modifica su un numero sufficiente di repliche da formare un quorum prima di considerarla confermata. Se un nodo scompare, i membri rimanenti possono eleggere o designare un altro coordinatore, purché il quorum resti disponibile.
Il quorum è un requisito matematico, non la promessa che ogni guasto sia innocuo. Un gruppo con tre repliche può generalmente tollerare una replica non disponibile. Se perde due membri, la copia rimasta non può accettare scritture in sicurezza perché non può dimostrare che un'altra maggioranza non abbia progredito altrove. Il posizionamento tra domini di guasto conta quanto il numero di repliche.
Le transazioni distribuite coordinano più partizioni
Una transazione che tocca una sola partizione può spesso terminare con un coordinamento relativamente ridotto. Una transazione che coinvolge più partizioni richiede una decisione di commit comune, affinché ogni partecipante applichi le proprie scritture oppure le annulli.
Il protocollo esatto varia in base al prodotto, ma il lavoro include di solito lettura o lock delle versioni rilevanti, convalida delle modifiche concorrenti, replica di intenti o record provvisori e finalizzazione del commit. Le transazioni lunghe allargano la finestra dei conflitti. Batch di grandi dimensioni possono coinvolgere molti gruppi di consenso e causare picchi di latenza anche quando ogni istruzione appare semplice.
Per questo è importante progettare le transazioni pensando alla rete. Raggruppa le righe correlate sotto prefissi di partizione compatibili quando il database supporta questa strategia. Mantieni brevi le transazioni, evita di attendere servizi esterni mentre una transazione è aperta e non caricare migliaia di record non correlati in un'unica unità atomica senza misurarne l'effetto.
Tempo e ordinamento richiedono meccanismi espliciti
I nodi distribuiti non condividono un orologio perfettamente sincronizzato, quindi ogni prodotto ha bisogno di un modo per ordinare le transazioni. Spanner usa i limiti d'incertezza di TrueTime e l'attesa di commit per fornire coerenza esterna. Altri sistemi possono combinare orologi fisici con componenti logiche, tracciamento delle dipendenze e protocolli transazionali.
Il coordinamento degli orologi influisce su operazioni come l'esecuzione serializzabile, le letture follower e gli snapshot. Le applicazioni dovrebbero usare i timestamp delle transazioni del database invece di presumere che timestamp generati da server applicativi separati stabiliscano un ordine globale affidabile.
La località controlla il percorso di rete
La configurazione della località decide dove si trovano le repliche e quale regione coordina le scritture di un record. Le letture possono essere rapide quando una replica adatta è vicina al chiamante. Una scrittura con ordinamento forte deve comunque raggiungere le repliche necessarie per il quorum, quindi la sua latenza riflette la topologia scelta.
Un buon posizionamento segue il carico di lavoro, non un diagramma aziendale. Se la maggior parte delle scritture di un tenant UE parte dall'Europa, collocare lì il coordinatore delle scritture evita un viaggio intercontinentale all'inizio di ogni transazione. Un record condiviso globalmente, come un contatore aggiornato da tutte le regioni, non può essere locale per ogni writer e può diventare un punto di contesa.
Quando lo SQL distribuito è la scelta giusta
Lo SQL distribuito è la scelta giusta quando resilienza geografica, capacità di scrittura orizzontale o correttezza tra partizioni contano abbastanza da giustificare costi continui di coordinamento. Una grande azienda non ne ha automaticamente bisogno, e un prodotto piccolo può averne bisogno se la promessa commerciale include disponibilità regionale rigorosa.
Condizioni che giustificano una valutazione
Una valutazione seria è opportuna quando si applicano più condizioni tra queste:
- Il servizio deve continuare a funzionare durante un'interruzione di zona o regione
- La domanda di scritture si avvicina al limite pratico di un solo database primario
- Lo sharding manuale richiederebbe molto tempo di sviluppo applicativo
- Le transazioni devono restare corrette tra nodi o posizioni
- I record richiedono un posizionamento geografico applicabile
Queste condizioni devono essere supportate da numeri. Definisci recovery time objective, recovery point objective, latenza delle transazioni, velocità di scrittura di picco e domini di guasto richiesti. Una richiesta vaga di scalabilità globale non basta per scegliere un'architettura.
La sola presenza di utenti regionali non è un motivo decisivo. Un'applicazione ricca di contenuti può collocare server web e cache vicino agli utenti mantenendo una sola regione di database. Le repliche di lettura possono supportare la navigazione regionale se sono accettabili risultati leggermente datati. Il caso diventa più forte quando utenti in più località devono eseguire scritture a bassa latenza su dati correlati.
Condizioni che favoriscono un database più semplice
Un servizio relazionale convenzionale è di solito preferibile quando il traffico è moderato, le scritture partono da una regione e il recupero può includere una promozione pianificata del database. Offre strumenti maturi, ampia compatibilità con estensioni, debug familiare e una fattura infrastrutturale inferiore.
Requisiti di latenza molto stretti possono favorire anch'essi una primaria regionale. Una scrittura durevole locale può completarsi molto più rapidamente di una scrittura di quorum che attraversa regioni lontane. I sistemi ricchi di analisi dovrebbero di norma separare le transazioni operative dalle scansioni lunghe, invece di aspettarsi che lo stesso cluster eccella in entrambi gli ambiti.
Conta anche la capacità del team. I servizi gestiti riducono il lavoro su hardware, patch e funzionamento del piano di controllo, ma non eliminano contesa nello schema, retry transazionali, pianificazione delle query, gestione della capacità o gestione degli incidenti lato applicazione. Se un team non ha tempo per testare il comportamento in caso di guasto, adottare un database distribuito può aumentare il rischio.
Una soglia decisionale basata sulle alternative
La giustificazione più forte emerge quando l'alternativa è già complicata. Se gli ingegneri stanno per costruire routing dei tenant, mappe degli shard, regole per transazioni tra shard, procedure di promozione regionale e strumenti di migrazione separati, un database che offre queste funzioni merita un'attenta valutazione.
Se l'alternativa è un'istanza PostgreSQL gestita con una replica di lettura e backup testati, la migrazione richiede prove chiare. Prima misura il sistema esistente. La saturazione della CPU potrebbe dipendere in realtà da una query inefficiente, una gestione mediocre delle connessioni, troppi indici o una cache mancante, non dalla necessità di scritture orizzontali.
Coerenza, disponibilità e latenza
Lo SQL distribuito di solito preserva la coerenza transazionale durante i guasti rifiutando operazioni che non riescono a raggiungere il quorum richiesto. Questo comportamento protegge lo stato confermato, ma significa che alcune richieste possono fallire o attendere durante una partizione di rete.
CAP descrive il comportamento in caso di guasto
Il teorema CAP si applica quando la comunicazione tra parti del cluster viene interrotta. Per i dati interessati, un sistema non può garantire sia coerenza linearizzabile sia risposte riuscite da ogni lato isolato. Un database orientato alla coerenza permette al lato con quorum di continuare e rifiuta altrove le scritture non sicure.
CAP non spiega la latenza durante il normale funzionamento. Anche quando ogni collegamento funziona, le repliche devono comunicare. La decisione ingegneristica più ampia include ciò che accade durante una partizione e quanto coordinamento l'applicazione accetta quando il sistema è sano.
Un'applicazione deve gestire esplicitamente gli esiti di indisponibilità. Timeout, errori transazionali ritentabili e perdita temporanea di una regione di scrittura sono possibilità normali. Restituire successo da entrambe le regioni isolate sarebbe peggio per un saldo o una prenotazione, perché la riconciliazione potrebbe non avere una risposta automatica valida.
Letture forti e letture intenzionalmente non aggiornate sono diverse
Una lettura forte osserva uno stato del database coerente con la garanzia di ordinamento richiesta. Alcuni prodotti espongono anche letture follower o a obsolescenza limitata, che scambiano freschezza con minore latenza e meno lavoro sul coordinatore delle scritture.
La scelta dovrebbe seguire il campo letto. Una descrizione di prodotto può spesso tollerare una replica leggermente datata. Una password appena modificata, il saldo corrente del conto o l'inventario residuo devono usare un percorso forte o coerente con la sessione adeguato. Le applicazioni non dovrebbero etichettare ogni lettura come obsoleta per guadagnare velocità e poi ricostruire la correttezza nel codice di servizio.
Il comportamento read-your-writes va testato con il driver e il livello di routing effettivi. Dopo un aggiornamento, la richiesta successiva può raggiungere un altro server applicativo o endpoint del database. Token di sessione, confini della transazione o un'impostazione di lettura forte possono essere necessari per garantire che l'utente veda la modifica accettata.
L'isolamento controlla gli esiti concorrenti
L'isolamento transazionale determina quali anomalie possono produrre transazioni concorrenti. L'isolamento serializzabile cerca di far apparire le transazioni completate come se fossero state eseguite una alla volta, anche quando il database le esegue contemporaneamente.
L'esecuzione serializzabile può annullare un partecipante quando le operazioni concorrenti non possono essere ordinate in sicurezza. Questo annullamento protegge da un risultato errato, non indica corruzione del database. Le applicazioni hanno bisogno di retry limitati attorno all'intera transazione, incluse tutte le letture che hanno influenzato le scritture.
I retry devono essere idempotenti fuori dal database. Se il codice invia un'email o chiama un provider di pagamenti prima che la transazione abbia eseguito sicuramente il commit, un retry può ripetere l'effetto collaterale. Registra un evento outbox nella transazione del database, esegui il commit e lascia che un worker separato invii l'azione esterna.
La distanza pone un limite inferiore alla latenza di scrittura
Una transazione tra regioni non può completarsi più rapidamente dei messaggi richiesti dal suo protocollo. Un viaggio di andata e ritorno di 80 millisecondi tra i membri del quorum aggiunge tempo reale prima di contare esecuzione della query, manutenzione degli indici, lavoro applicativo e accodamento.
Il modello costoso è spesso costituito da più transazioni sequenziali in una sola azione utente. Se il checkout esegue inserimento dell'ordine, prenotazione dell'inventario, aggiornamento dello stato di pagamento e scrittura di audit come quattro commit bloccanti, il costo di rete si accumula. Combinare modifiche al database che condividono un esito atomico può eliminare viaggi superflui, mentre le chiamate di pagamento esterne devono restare fuori da una transazione aperta.
Misura la latenza percentile invece delle medie. Movimento della leadership, contesa, stalli dello storage e retry emergono nella coda. Un progetto che raggiunge l'obiettivo mediano ma non il 99° percentile durante un normale ribilanciamento può comunque causare guasti visibili agli utenti.
Confronto tra Spanner, CockroachDB e YugabyteDB
Spanner, CockroachDB e YugabyteDB risolvono problemi di distribuzione simili, ma differiscono per modello di distribuzione, compatibilità, implementazione delle transazioni e presupposti operativi. Per scegliere tra loro occorre testare il comportamento dell'applicazione invece di decidere in base all'etichetta SQL comune.
| Area | Google Spanner | CockroachDB | YugabyteDB |
|---|---|---|---|
| Interfaccia SQL principale | Dialetto GoogleSQL o PostgreSQL | SQL compatibile con PostgreSQL sul protocollo wire PostgreSQL | YSQL compatibile con PostgreSQL, più YCQL per accesso in stile Cassandra |
| Fondamento della replica | Gruppi Paxos con ordinamento basato su TrueTime | Replica Raft sui range | Replica Raft sui tablet |
| Distribuzione tipica | Database Google Cloud gestito | Servizio cloud gestito o installazione self-managed | Servizio cloud gestito o installazione self-managed |
| Problema di portabilità | Dialetto e comportamento specifici della piattaforma | Differenze in funzionalità, estensioni e semantica di PostgreSQL | Differenze di versione e funzionalità tra YSQL e PostgreSQL |
| Caso di valutazione naturale | Sistemi Google Cloud che richiedono posizionamento transazionale globale | Team che cercano sviluppo orientato a PostgreSQL con funzionamento distribuito | Team che vogliono accesso orientato a PostgreSQL o la scelta tra API SQL e API in stile Cassandra |
Spanner si adatta a una strategia Google Cloud gestita
Spanner si adatta alle organizzazioni pronte a usare un database Google Cloud gestito e a progettare attorno al suo dialetto, alla topologia e al modello operativo. TrueTime supporta transazioni esternamente coerenti, cioè le transazioni confermate rispettano l'ordinamento temporale reale entro la semantica documentata.
Il dialetto PostgreSQL può ridurre le differenze di sintassi SQL, ma un dialetto non equivale alla compatibilità completa con PostgreSQL. Estensioni, funzioni amministrative, cataloghi di sistema, tipi di dati, driver e presupposti dell'ORM richiedono ancora verifiche. I team dovrebbero inventariare ogni dipendenza dal database prima di considerare portabile un'applicazione esistente.
Spanner merita particolare attenzione quando il sistema desiderato dipende già da identità, rete, osservabilità e controlli regionali di Google Cloud. Il modello gestito elimina l'amministrazione dei nodi del database, ma progettazione dello schema, ottimizzazione delle query, quote, gestione dei costi e recupero applicativo restano responsabilità del cliente.
CockroachDB si adatta ad applicazioni distribuite orientate a PostgreSQL
CockroachDB si adatta ai team che desiderano accesso applicativo in stile PostgreSQL distribuendo dati transazionali tra range. Usa per impostazione predefinita l'isolamento serializzabile, quindi le applicazioni devono ritentare correttamente le transazioni rifiutate per contesa o conflitti di ordinamento.
La compatibilità va testata ai livelli di migrazione, driver e ORM. Estensioni PostgreSQL e comportamenti specializzati possono essere assenti o diversi. Anche le query che dipendono da piani di esecuzione su un singolo nodo possono comportarsi diversamente dopo che tabelle e indici sono stati divisi tra range.
Lo spostamento dei range e il ribilanciamento automatico semplificano le modifiche di capacità, ma una scelta errata della chiave primaria può comunque produrre range caldi. Le astrazioni multi-regione aiutano a esprimere la località delle tabelle, tuttavia gli sviluppatori devono decidere quali record siano regionali, quali globali e dove coordinare le scritture.
YugabyteDB si adatta a requisiti YSQL e API miste
YugabyteDB si adatta alle applicazioni che apprezzano un'interfaccia relazionale compatibile con PostgreSQL e possono beneficiare della sua API separata compatibile con Cassandra. YSQL offre tabelle relazionali e transazioni distribuite, mentre YCQL segue un modello dati diverso e non va trattato come un'altra via per ogni operazione YSQL.
Il suo livello di storage distribuisce i dati attraverso tablet. Progettazione delle tabelle, divisione dei tablet, posizionamento degli indici e ambito delle transazioni influenzano il modo in cui il lavoro si distribuisce nel cluster. Le applicazioni PostgreSQL hanno comunque bisogno di test di compatibilità per estensioni, funzioni, strumenti e comportamento del planner.
La disponibilità di approcci di distribuzione differenti può adattarsi a policy infrastrutturali che richiedono il controllo sul posizionamento. Con il self-management, questo controllo trasferisce al cliente la responsabilità operativa: aggiornamenti, procedure di riparazione, capacità, osservabilità, certificati, backup e test dei guasti richiedono tutti un responsabile.
Un test utile del prodotto usa prove dall'applicazione
Un confronto utile esegue lo stesso carico rappresentativo su ogni prodotto valido. Testa creazione dello schema, migrazioni, SQL generato dall'ORM, retry transazionali, ripristino dei backup, failover, eventi di scalabilità e query dal volume più elevato.
Non confrontare solo il massimo delle transazioni al secondo. Registra latenza p50, p95 e p99, tassi di conflitto e retry, byte trasferiti tra regioni, amplificazione dello storage, tempo di ripristino e sforzo dell'operatore durante un incidente simulato. La scelta migliore soddisfa gli obiettivi di correttezza e recupero con costi e oneri operativi accettabili.
SaaS globale con utenti regionali
Un'applicazione SaaS globale trae vantaggio dallo SQL distribuito quando i tenant richiedono posizionamento regionale dei dati e accesso transazionale senza stack di database separati per ogni area geografica. Il progetto funziona meglio quando la tenancy è esplicita nello schema e la maggior parte delle transazioni resta all'interno di un tenant.
La località del tenant deve seguire contratti e traffico
Un identificatore del tenant può guidare il posizionamento, così i record europei restano in posizioni europee approvate mentre i record di un altro cliente restano nel paese o nella regione previsti dal contratto. Questo mantiene uno schema logico unico consentendo policy fisiche diverse.
Le regole di posizionamento devono coprire più della tabella di base. Voci di indice, flussi di modifica, dati temporanei, backup e record esportati possono contenere informazioni regolamentate. Una policy che blocca le righe ma invia un indice secondario globale altrove può violare il confine previsto.
L'isolamento dei tenant influisce anche sulle prestazioni. Un tenant grande può sovraccaricare una partizione condivisa o dominare un nodo. Può essere necessario usare hash o sotto-partizionamento all'interno di quel tenant, preservando però un accesso efficiente alle transazioni circoscritte al tenant.
Le letture regionali richiedono una policy di freschezza esplicita
Dashboard ricche di letture possono usare repliche vicine quando sono accettabili dati con un leggero ritardo. Modifiche dell'account, decisioni di autorizzazione e schermate di conferma post-transazione richiedono un comportamento più forte. Classifica i percorsi di query in base al requisito di freschezza invece di adottare un'unica impostazione globale.
Il posizionamento delle scritture deve seguire il writer abituale di ciascun tenant. Se il personale di un cliente lavora soprattutto a Singapore, coordinare le sue scritture in un altro continente crea latenza evitabile. Una procedura di migrazione del tenant deve aggiornare il posizionamento senza perdere scritture, violare requisiti di residenza o lasciare le cache dell'applicazione puntate a vecchie posizioni.
Il codice applicativo globale deve tollerare gli spostamenti
I leader si spostano, i nodi vengono riavviati e il routing cambia durante la manutenzione. I driver richiedono timeout sensati, policy di retry, rinnovo delle connessioni e logica di riavvio delle transazioni. I retry devono usare jitter e un limite, così un cluster sovraccarico non riceve subito un'ondata sincronizzata di richieste ripetute.
Il monitoraggio deve separare la latenza utente per regione e classe di tenant. Una media globale può nascondere un gruppo di clienti lontani che paga diversi viaggi di rete aggiuntivi. Gli identificatori di traccia che collegano gli span API alle istruzioni del database rendono più facili da individuare gli errori di località.
Workflow finanziari e libri mastri
I workflow finanziari traggono vantaggio quando vincoli e transazioni del database applicano gli invarianti del libro mastro in presenza di guasti e richieste concorrenti. La distribuzione non crea da sola una contabilità corretta, quindi lo schema deve codificare le regole che non possono essere violate.
Un libro mastro deve conservare una sequenza verificabile di registrazioni
Un libro mastro orientato all'append registra ogni movimento come registrazione invece di sostituire ripetutamente un unico valore di saldo senza storico. Ogni contabilizzazione deve avere un identificatore stabile della transazione, conti, importi, valuta, timestamp di business e metadati di creazione. Le regole della partita doppia devono essere controllate prima del commit, affinché addebiti e accrediti siano in equilibrio per l'unità di contabilizzazione.
Un saldo in cache può velocizzare le letture, ma deve cambiare nella stessa transazione delle registrazioni oppure essere trattato chiaramente come dato derivato. I job di riconciliazione dovrebbero confrontare i totali derivati con le registrazioni di origine e segnalare le differenze senza riscrivere silenziosamente lo storico.
Raramente è necessario un ordinamento globale per ogni conto. Le transazioni che coinvolgono un conto o una coppia di trasferimento richiedono un ordine coerente, mentre i conti non correlati possono procedere in parallelo. Progettare attorno a questo confine riduce la contesa rispetto a una sequenza globale o a una sola riga di regolamento.
L'idempotenza rende sicuri i retry
API di pagamento, code e webhook ritentano dopo timeout, quindi ogni operazione di business necessita di una chiave di idempotenza stabile. Applica l'unicità nell'ambito corretto, come un commerciante o un conto, poi crea il record del pagamento e le registrazioni del libro mastro in un'unica transazione del database.
CREATE TABLE payment_attempts (
account_id UUID NOT NULL,
idempotency_key TEXT NOT NULL,
provider_reference TEXT,
status TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL,
PRIMARY KEY (account_id, idempotency_key)
);
Se due worker inviano la stessa operazione, il vincolo univoco decide quale inserimento riesce. Il worker che perde dovrebbe leggere il record esistente e restituire il risultato già stabilito. Non deve creare un secondo addebito del provider solo perché una transazione del database è stata ritentata.
Le chiamate esterne richiedono un confine transazionale
Un database non può eseguire il commit atomico con un provider di pagamenti non correlato, a meno che entrambi non partecipino a un protocollo di coordinamento specializzato, cosa che la maggior parte delle API pubbliche non fa. Mantieni la chiamata di rete fuori dalla transazione del database e modella il workflow tramite stati espliciti come in attesa, autorizzato, acquisito, non riuscito e stornato.
Un transactional outbox può pubblicare le modifiche confermate verso worker downstream. I consumer dovrebbero eliminare i duplicati tramite identificatore dell'evento, perché la consegna dei messaggi può avvenire più di una volta. Questo fornisce elaborazione recuperabile senza sostenere l'impossibile esistenza di un'unica transazione tra ogni servizio.
I conti caldi richiedono una progettazione specifica per il carico
Elaborazioni payroll, regolamenti di marketplace e grandi commercianti possono concentrare le scritture su un solo conto. Aggiungere nodi al database non divide una singola riga in conflitto tra di essi. Le opzioni includono partizioni di registrazioni immutabili, accumulatori per periodo, contabilizzazione in coda per un conto o una gerarchia di sottoconti definita con attenzione.
Testa la distribuzione effettiva dello sbilanciamento. Il traffico sintetico uniforme può far apparire pronto un cluster, mentre un solo commerciante di produzione crea conflitti serializzabili ripetuti. La correttezza viene prima, ma il modello dati dovrebbe esporre concorrenza sicura dove le regole contabili lo consentono.
Inventario, prenotazioni e riserve
I sistemi di inventario e prenotazione richiedono una transazione autorevole di assegnazione quando più utenti possono richiedere lo stesso bene scarso. Le letture rapide della disponibilità migliorano l'esplorazione, ma solo il percorso di commit può decidere chi riceve l'ultima unità.
Le scritture condizionali evitano l'overselling
Un aggiornamento condizionale può riservare lo stock solo quando ne resta abbastanza. Il numero di righe coinvolte indica all'applicazione se l'assegnazione è riuscita.
UPDATE inventory
SET available = available - 1
WHERE sku = $1
AND available > 0;
Questa istruzione deve condividere una transazione con il record di prenotazione. Leggere prima la disponibilità e decrementarla in seguito crea una race condition, a meno che livello di isolamento e gestione del predicato proteggano la decisione. I vincoli del database dovrebbero rifiutare quantità negative come ulteriore livello di sicurezza.
Per posti assegnati, un vincolo univoco su spettacolo e identificatore del posto garantisce una sola prenotazione vincente. L'inventario alberghiero viene spesso modellato per camera-notte o data del pool di inventario, così soggiorni sovrapposti non possono richiedere la stessa capacità. La corretta unità di contesa deriva dalla regola di business.
Le riserve temporanee separano assegnazione e pagamento
Una riserva temporanea blocca l'inventario mentre procedono pagamento o conferma dell'utente. Memorizza data di scadenza e stato, quindi convertila in una prenotazione confermata tramite una transazione condizionale. Un worker di scadenza deve rilasciare solo le riserve ancora attive, perché conferma e scadenza possono entrare in conflitto.
Il semplice ritardo dell'orologio non garantisce il rilascio. I worker possono fermarsi, le code possono accumulare ritardo e le regioni possono guastarsi. Le query che calcolano l'inventario vendibile dovrebbero considerare coerentemente lo stato scaduto, mentre i job di riparazione recuperano le riserve mancate.
La durata della riserva è una decisione di prodotto e capacità. Dieci minuti possono essere ragionevoli per il checkout, ma durante un picco possono bloccare una quota significativa di inventario scarso. Misura abbandono e tempo di completamento del pagamento prima di impostarla.
La contesa estrema non scala in modo lineare
Migliaia di acquirenti in competizione per una riga non possono lavorare in parallelo semplicemente aggiungendo repliche. Ogni decremento riuscito deve essere ordinato rispetto agli altri. Controlli di ammissione, una coda, bucket di stock o quote regionali preassegnate possono proteggere il database durante un lancio.
Le quote regionali riducono il coordinamento ma cambiano la semantica. Se l'Europa ha unità inutilizzate mentre un'altra regione esaurisce le proprie, il sistema ha bisogno di un modo sicuro per trasferire la quota o accettare uno squilibrio temporaneo. Usa questo modello solo quando il business può definire come riconciliare la capacità regionale.
Alta disponibilità e disaster recovery
Lo SQL distribuito può mantenere il servizio durante determinati guasti infrastrutturali quando posizionamento delle repliche, capacità disponibile e comportamento dell'applicazione corrispondono a un obiettivo di servizio definito. La sola replica non garantisce questo risultato.
Gli SLO devono specificare i domini di guasto
Un obiettivo di uptime richiede un carico di lavoro e uno scenario di guasto. Definisci se il servizio deve sopravvivere alla perdita di un nodo, una zona di disponibilità o un'intera regione. Indica tasso di errore e latenza accettabili durante l'evento, non solo dopo il recupero.
Un cluster con tre repliche in un solo edificio presenta un profilo di rischio diverso da tre repliche in zone indipendenti. Una topologia multi-regione protegge da un evento più ampio ma introduce percorsi di quorum più lunghi e richiede capacità residua sufficiente per assorbire il traffico dopo la scomparsa di una posizione.
Il recovery time objective definisce quanto rapidamente deve tornare il servizio. Il recovery point objective definisce quanti dati confermati possono andare persi. La replica sincrona con quorum può supportare l'obiettivo di zero perdita di dati confermati per i guasti coperti, ma solo finché le repliche richieste e il percorso applicativo funzionano come previsto.
Il failover genera eventi visibili nell'applicazione
I cambiamenti di leadership possono interrompere transazioni in corso, chiudere connessioni e aumentare la latenza. Le applicazioni devono distinguere gli esiti del database ritentabili dagli errori di business permanenti. Una transazione fallita deve ripartire come unità, invece di rieseguire soltanto l'istruzione finale.
I pool di connessioni possono conservare endpoint non più raggiungibili dopo un guasto. Controlli di integrità, comportamento DNS, bilanciatori di carico, convalida dei certificati e rilevamento della topologia da parte del driver fanno parte del piano di test. Il database può essere sano mentre l'applicazione non riesce comunque a trovarlo.
La capacità dopo un guasto merita un calcolo esplicito. Se tre regioni lavorano normalmente vicino al 70% di utilizzo, perderne una non lascia spazio sufficiente per la sua quota di lavoro. Mantenere capacità di riserva costa, ma una topologia senza capacità di failover non soddisfa l'obiettivo dichiarato.
I game day validano il progetto
Le esercitazioni sui guasti dovrebbero disabilitare un nodo, isolare una zona, interrompere la connettività regionale e rimuovere un endpoint applicativo. Misura durata degli errori, tasso di retry delle transazioni, percentili di latenza, crescita delle code e risposta dell'operatore.
Esegui queste esercitazioni dopo modifiche rilevanti alla topologia, al driver o allo schema. Una procedura dimostrata con il traffico dell'anno scorso può fallire dopo il raddoppio del volume dati o quando un tenant diventa dominante. Automatizza le parti sicure dell'esercitazione, così le prove non dipendono da un evento manuale annuale.
La replica non è un backup
Le repliche copiano fedelmente cancellazioni accidentali, migrazioni difettose e scritture applicative dannose. Backup e ripristino point-in-time proteggono dai danni logici che la replica non può rilevare.
Le prove di ripristino dovrebbero costruire un ambiente pulito separato, verificare checksum o invarianti applicativi e misurare il tempo totale di recupero. Includi chiavi di cifratura, policy di accesso, versioni dello schema e configurazione dipendente. Un backup che esiste ma non può essere ripristinato entro l'obiettivo non è un sistema di recupero adeguato.
Residenza dei dati e architettura guidata dalla conformità
Lo SQL distribuito può collocare tenant o gruppi di record in regioni approvate, ma la conformità dipende da ogni copia, percorso di accesso e processo operativo. La località del database è un controllo all'interno di un programma più ampio.
Le regole di residenza richiedono definizioni precise
Un requisito secondo cui i dati devono restare in un paese può riferirsi a archiviazione, elaborazione, accesso del supporto, backup, chiavi di cifratura o a tutti questi elementi. Queste interpretazioni producono topologie diverse. Consulenti legali e revisori dovrebbero tradurre regolamenti e contratti in controlli tecnici verificabili.
I team necessitano di un inventario dei campi regolamentati e dei dati derivati. Log, trace, indici di ricerca, esportazioni analitiche, allegati del supporto e code di messaggi possono contenere le stesse informazioni personali della tabella primaria. Limitare il database esportando al contempo payload grezzi globalmente non soddisfa la policy prevista.
La minimizzazione dei dati può semplificare il progetto. Se un servizio globale necessita solo di identificatore dell'account e stato aggregato, conserva i dettagli sensibili nella regione approvata ed esponi altrove la rappresentazione minima consentita.
Le policy di posizionamento devono includere le operazioni del ciclo di vita
Le policy dovrebbero indicare dove possono esistere repliche live, repliche temporanee, backup, snapshot, record delle modifiche e ambienti di ripristino. Ribilanciamento e manutenzione devono rispettare lo stesso confine. Una procedura di emergenza non dovrebbe copiare dati regolamentati in una regione non approvata per comodità.
Il controllo degli accessi richiede limiti geografici e organizzativi. Le identità di servizio dovrebbero ricevere soltanto tabelle e operazioni necessarie. L'accesso umano alla produzione dovrebbe essere registrato, limitato nel tempo quando possibile e revisionato. Chiavi di cifratura vincolate alla regione possono aggiungere controllo, anche se disponibilità delle chiavi e disaster recovery richiedono allora una progettazione propria.
Il trasferimento di un tenant merita un workflow documentato. Modifiche contrattuali, migrazione del cliente o ristrutturazione aziendale possono richiedere lo spostamento dei record tra giurisdizioni. Il processo dovrebbe identificare quando scompaiono le copie precedenti, come decadono i backup e quali prove dimostrano il completamento.
Il reporting globale può richiedere dataset derivati
Una dashboard globale può entrare in conflitto con un posizionamento rigoroso se esegue scansioni di dati grezzi dei clienti tra regioni. L'elaborazione regionale può calcolare localmente aggregati approvati e poi pubblicare risultati non sensibili in un archivio centrale di reporting.
Le regole di aggregazione devono impedire la ricostruzione di record limitati. Gruppi piccoli, campi di testo libero e dimensioni dettagliate possono esporre informazioni personali anche quando vengono rimossi gli identificatori diretti. La governance analitica appartiene quindi alla revisione dell'architettura, non a un successivo progetto di reporting.
I carichi operativi e analitici meritano spesso sistemi separati. Il database transazionale protegge lo stato corrente del prodotto, mentre pipeline circoscritte alla regione producono dataset governati per i report. Questa separazione tiene le lunghe scansioni analitiche lontane dalle transazioni sensibili alla latenza.
Pianificazione di costi e prestazioni
Lo SQL distribuito costa più di un database di base in una sola regione perché mantiene capacità ridondante e coordina il lavoro attraverso una rete. L'investimento può comunque essere giustificato quando sostituisce costoso lavoro di sharding o previene perdite superiori al sovrapprezzo operativo.
Calcolo e storage includono il costo della replica
Un dataset logico da 2 TB con tre repliche complete parte da circa 6 TB di dati replicati, prima di indici secondari, spazio temporaneo per compattazione, backup e metadati. Fatturazione effettiva e compressione variano in base al prodotto, quindi le stime dovrebbero usare storage fisico misurato invece della sola dimensione logica delle tabelle.
La capacità di calcolo deve coprire lavoro normale, elaborazione del consenso, ribilanciamento, attività di backup e capacità residua per guasti. I nodi non sono unità intercambiabili di throughput quando una partizione è calda. Aggiungere capacità aiuta solo se il carico può distribuirsi su di essa.
Gli indici moltiplicano lavoro di scrittura e storage. Rivedi ogni indice secondario in base al valore per le query, frequenza di aggiornamento e posizionamento geografico. Un indice inutilizzato in un cluster distribuito spreca disco e rende più costosa ogni scrittura interessata.
I costi di rete possono diventare rilevanti
La replica invia le scritture tra le posizioni delle repliche. Query tra regioni, feed di modifica, backup e traffico applicativo aggiungono ulteriori trasferimenti. Il traffico attivo in più regioni può generare una fattura che un benchmark in una sola regione non rivela mai.
Stima byte per transazione, fattore di replica, velocità di scrittura, amplificazione degli indici e direzione dei trasferimenti. Poi testa con dati di fatturazione del provider durante un'esecuzione con carico rappresentativo. Il solo numero di richieste non coglie payload grandi e spostamenti in background.
Gli errori di località aumentano sia costi sia latenza. Un servizio distribuito in una regione può interrogare ripetutamente un coordinatore in un'altra a causa della selezione dell'endpoint o del posizionamento del tenant. Tracing distribuito e ripartizione dei costi per regione possono rivelare questo schema.
I percorsi utente rivelano la latenza accumulata
Modella azioni utente complete invece di istruzioni isolate. Per il checkout, conta ogni commit sequenziale nel database, lettura forte, chiamata API esterna e passaggio in coda. Applica tempi di andata e ritorno regionali e percentili di esecuzione delle query misurati al percorso critico.
Supponi che un percorso contenga due scritture di quorum sequenziali, ognuna con 90 millisecondi di coordinamento di rete. Aggiungono circa 180 millisecondi prima dell'elaborazione applicativa. Combinare modifiche che condividono una decisione atomica può eliminare un commit, mentre parallelizzare letture indipendenti può accorciare il percorso.
I test di carico dovrebbero includere contesa e dimensioni dei payload realistiche. Un benchmark con identificatori casuali può distribuirsi perfettamente anche se le scritture di produzione riguardano pochi tenant popolari. Includi cambi di leadership e ribilanciamento affinché la latenza in coda rifletta il normale funzionamento del cluster.
Confronta il costo totale di proprietà con alternative realistiche
Il confronto rilevante non è SQL distribuito contro un database immaginario senza costi operativi. Confrontalo con un'alternativa specifica: PostgreSQL gestito, repliche, servizi di sharding, recupero regionale, routing applicativo e gli ingegneri necessari per mantenerli.
Includi lavoro di migrazione, formazione, osservabilità, risposta agli incidenti, piani di supporto e costi di uscita. La gestione affidata al provider può ridurre il lavoro infrastrutturale, mentre il self-management può soddisfare requisiti di controllo al costo di personale più specializzato.
Un semplice modello finanziario può confrontare il sovrapprezzo annuo della piattaforma con perdite attese da indisponibilità, lavoro ingegneristico rimandato, esposizione alla conformità e ricavi influenzati dalla latenza regionale. Usa intervalli per gli input incerti e individua l'ipotesi che cambia la decisione. Se il risultato dipende da una stima di indisponibilità implausibilmente grande, il sistema più semplice resta probabilmente appropriato.
Modelli di progettazione per schema e applicazione
Uno schema SQL distribuito offre buone prestazioni quando i suoi percorsi di accesso distribuiscono il lavoro indipendente mantenendo vicine le transazioni correlate. Portare uno schema da singolo nodo senza modificarlo può preservare la correttezza ma produrre latenza scarsa o grave contesa.
Le chiavi primarie influenzano la distribuzione
Una chiave primaria monotonicamente crescente può indirizzare le nuove righe verso la fine di un range. Identificatori casuali distribuiscono gli inserimenti, ma una distribuzione del tutto casuale può rendere costose le scansioni per tenant o il posizionamento regionale. Le chiavi composte spesso bilanciano questi obiettivi iniziando con un identificatore di tenant o bucket e mantenendo un valore ordinabile all'interno del gruppo.
Scegli il prefisso in base ai confini delle transazioni. Se quasi ogni operazione è circoscritta al tenant, raggruppare per tenant può ridurre il lavoro distribuito. Un tenant molto grande può richiedere bucket nel proprio namespace, così più partizioni possono accettare scritture contemporaneamente.
Modificare una chiave primaria dopo che una tabella è cresciuta può richiedere una grande riscrittura dei dati. Testa layout candidati con uno sbilanciamento realistico prima della migrazione. Esamina calore delle partizioni, fan-out delle transazioni, località degli indici e comportamento delle scansioni invece di valutare soltanto il throughput totale.
La contesa richiede una riprogettazione prima della capacità
Un contatore globale, una riga di configurazione singleton o il saldo di un commerciante possono serializzare richieste altrimenti indipendenti. Più nodi non possono eliminare un requisito logico che impone a ogni transazione di aggiornare lo stesso valore.
Sostituisci i contatori globali esatti con contatori partizionati quando l'aggregazione temporanea è accettabile. Versiona la configurazione invece di aggiornare una riga ad alta frequenza. Per lo stato monetario, preserva l'invariante contabile e trova concorrenza in registrazioni append-only o sottoconti indipendenti invece di indebolire la correttezza.
Le lunghe transazioni di lettura-modifica-scrittura peggiorano i conflitti. Leggi il minor insieme necessario, evita interazioni dell'utente all'interno di una transazione ed esegui prontamente il commit. Se il lavoro di business dura minuti, rappresentalo come macchina a stati su più transazioni brevi.
Il comportamento dei retry fa parte del contratto dell'applicazione
I driver possono ritentare singole istruzioni o esporre un errore ritentabile al codice applicativo. Comprendi quale livello possiede la riesecuzione completa della transazione. La riesecuzione parziale può usare decisioni obsolete o omettere letture precedenti.
Un ciclo di retry dovrebbe avere un numero massimo di tentativi, backoff casuale e strumentazione. Registra tipo di conflitto, operazione interessata, numero di tentativi ed esito finale. Retry illimitati trasformano la contesa in latenza nascosta e possono sovraccaricare il cluster.
Le richieste di business richiedono identificatori stabili affinché una risposta client incerta possa essere verificata in sicurezza. Se il database esegue il commit ma la risposta si perde, il client dovrebbe interrogare l'operazione già stabilita invece di inviarne una semanticamente nuova.
Le modifiche dello schema richiedono prove su scala di produzione
Le modifiche dello schema distribuito possono aggiornare rapidamente i metadati mentre backfill e creazione degli indici proseguono in background. Questi job consumano storage, rete e CPU e possono interagire con le scritture live.
Usa migrazioni expand-and-contract. Aggiungi prima campi o tabelle compatibili, distribuisci codice che può lavorare con entrambe le forme, esegui il backfill in batch controllati, cambia le letture e poi rimuovi la vecchia forma dopo la verifica. La pianificazione del rollback dovrebbe considerare i dati scritti dalla nuova versione.
Testa migrazioni grandi con volume e topologia regionale simili alla produzione. Una modifica che finisce rapidamente in un piccolo cluster di staging può richiedere ore in produzione e competere con il traffico dei clienti. Prima di iniziare, monitora avanzamento, controlli di pausa, spazio libero su disco e comportamento dei retry.
Checklist di adozione e proof of concept
Un proof of concept utile testa un carico rappresentativo rispetto a obiettivi espliciti di correttezza, latenza, resilienza e costi. Benchmark generici non possono determinare se uno schema e un'applicazione specifici si comporteranno bene.
Scegli un carico con vincoli reali
Scegli un workflow come prenotare un articolo scarso, registrare un trasferimento nel libro mastro o effettuare il provisioning di un tenant in una regione obbligatoria. Riutilizza schema, query, confini delle transazioni, dimensioni dei payload e sbilanciamento del traffico simili alla produzione.
Definisci il successo prima di eseguire il test:
- Esiti corretti in presenza di concorrenza e retry
- Latenza p50, p95 e p99 per regione
- Throughput di picco sostenuto con capacità residua per guasti
- Comportamento di recupero durante guasti di nodi e regioni
- Costo misurato di calcolo, storage e rete
Il margine di sicurezza dovrebbe derivare dalla crescita prevista e dalla capacità necessaria in caso di guasto, non da un moltiplicatore arbitrario. Se la perdita di una regione rientra nell'ambito, le posizioni rimanenti devono gestire il carico reindirizzato durante il test.
Crea una superficie applicativa realistica
Un'API e una piccola interfaccia utente mostrano sequenziamento delle transazioni, comportamento del driver e latenza percepita dall'utente che uno strumento limitato al database può non rilevare. Koder.ai può creare via chat un'interfaccia React, un backend Go e una base PostgreSQL. La sua modalità di pianificazione può aiutare a definire il workflow prima della generazione, mentre l'esportazione del codice sorgente permette agli ingegneri di adattare il livello dati per un database candidato.
Usa l'applicazione generata come struttura di test, non come prova di compatibilità del database. Esegui le migrazioni, ispeziona lo SQL generato, configura il driver ufficiale e implementa deliberatamente i retry transazionali. Snapshot e rollback di Koder.ai possono proteggere le iterazioni dell'applicazione, ma non sostituiscono backup del database o prove di ripristino.
Koder.ai supporta anche distribuzione e hosting, che possono collocare le istanze dell'applicazione di test vicino alle regioni del database. Questo rende possibile misurare l'intero percorso della richiesta invece di lanciare ogni benchmark da una sola posizione. Mantieni sintetici i dati di test, a meno che l'ambiente non disponga dei controlli necessari per record di produzione.
Esercita il funzionamento normale e i guasti
Il test dovrebbe coprire traffico costante, picchi, partizioni calde, query a lunga esecuzione, modifiche dello schema, lavoro di backup e sostituzione dei nodi. Poi interrompi la connettività e rimuovi un dominio di guasto nell'ambiente di test approvato.
Acquisisci annullamenti delle transazioni, tentativi di retry, risposte di indisponibilità, movimento della leadership, profondità delle code, utilizzo del disco e trasferimento regionale. Registra ciò che l'operatore ha dovuto fare. Un recupero automatico che richiede un passaggio manuale non documentato non è ancora pronto per la produzione.
Ripristina un backup in un ambiente separato e verifica gli invarianti dell'applicazione. Per l'inventario, conferma che le assegnazioni non superino lo stock. Per un libro mastro, ricalcola i saldi e verifica le contabilizzazioni in equilibrio. Per la tenancy SaaS, conferma che policy di posizionamento e accesso siano sopravvissute al ripristino.
Verifica la compatibilità prima della migrazione
Inventaria estensioni del database, procedure memorizzate, trigger, tipi di dati, presupposti di isolamento, funzionalità dell'ORM, query di reporting, strumenti di backup e script amministrativi. Classifica ogni elemento come compatibile, sostituibile o bloccante.
Esegui migrazioni rappresentative su una copia a dimensione intera o su un dataset generato. Misura durata del backfill, ritardo del change-data-capture, costo dell'esecuzione parallela e tempo di cutover. Se la migrazione usa doppie scritture, definisci come rilevare le discrepanze e quale sistema resti autorevole in ogni fase.
Le shadow read possono confrontare i risultati senza modificare lo stato di produzione. Considera differenze temporali e query intenzionalmente non aggiornate, così il confronto non segnala variazioni previste come corruzione. Ogni differenza non spiegata nei dati transazionali richiede una soluzione prima del cutover.
Verifica la prontezza per la produzione
Una revisione di produzione dovrebbe assegnare responsabili per funzionamento del database, retry dell'applicazione, sicurezza, policy di residenza, costi e risposta agli incidenti. Dovrebbe includere dashboard, avvisi, runbook, soglie di capacità, prove di ripristino e un punto decisionale per il rollback.
La decisione finale può comunque essere restare su PostgreSQL o MySQL. Un proof of concept ha successo quando produce prove affidabili, anche se mostrano che l'opzione distribuita costa più di quanto giustifichino i requisiti attuali. Quando i requisiti sostengono l'adozione, migra gradualmente, misura ogni fase e conserva una via di ritorno testata finché il nuovo sistema non si sarà dimostrato sotto carico reale.
Domande frequenti
Che cos'è un database «SQL distribuito», in parole semplici?
Un database SQL distribuito offre un'interfaccia relazionale e SQL, con tabelle, join, vincoli e transazioni, ma funziona come un cluster su più macchine, spesso in regioni diverse, comportandosi come un unico database logico.
In pratica cerca di combinare:
- Il comportamento familiare di SQL e ACID
- Scalabilità orizzontale, aggiungendo nodi
- Alta disponibilità e tolleranza ai guasti senza sharding manuale
In cosa differisce lo SQL distribuito da una configurazione PostgreSQL/MySQL tradizionale?
Un RDBMS su singolo nodo o con primaria e repliche è spesso più semplice, meno costoso e più veloce per l'OLTP in una sola regione.
Lo SQL distribuito diventa interessante quando l'alternativa prevede:
- Sharding gestito dall'applicazione
- Failover multi-regione complesso
- Requisiti di coerenza forte tra zone o regioni
- Esigenze di residenza dei dati con un unico modello operativo
Perché i sistemi SQL distribuiti usano protocolli di consenso come Raft o Paxos?
La maggior parte dei sistemi si basa su due idee fondamentali:
- Replica: ogni shard o partizione di dati viene archiviato su più nodi.
- Consenso: con Raft o Paxos, le repliche concordano sull'ordine delle scritture; in genere il commit richiede la conferma della maggioranza.
Questo consente una coerenza forte anche quando alcuni nodi si guastano, ma aggiunge il costo del coordinamento in rete.
Come vengono partizionati e distribuiti i dati tra nodi e regioni?
Le tabelle vengono divise in parti più piccole, spesso chiamate partizioni/shard oppure, a seconda del prodotto, range, tablet o split. Ogni partizione:
- Ha il proprio gruppo di repliche
- Può essere collocata su nodi o regioni specifici
- Può spostarsi quando il cluster viene ribilanciato
In genere puoi influire sul posizionamento tramite policy, così i dati più richiesti e i writer principali restano vicini e riducono gli attraversamenti di rete.
Perché le transazioni possono essere più lente nello SQL distribuito, soprattutto tra regioni?
Le transazioni distribuite spesso coinvolgono più partizioni, potenzialmente su nodi o regioni diverse. Un commit sicuro può richiedere:
- Lock o validazione tra i partecipanti
- Conferme di replica dal quorum
- Una decisione di commit coordinata
Questi ulteriori viaggi di andata e ritorno in rete sono il motivo principale per cui la latenza di scrittura può aumentare, soprattutto quando il consenso coinvolge più regioni.
Quali sono i segnali più chiari che mi serve davvero lo SQL distribuito?
Valuta lo SQL distribuito quando sono vere almeno due di queste condizioni:
- Hai utenti importanti in più regioni e vuoi dati coerenti
- Ti serve failover automatico tra zone o regioni, con RTO/RPO stringenti
- La scalabilità verticale non basta più per le scritture
- Ti serve coerenza forte per transazioni essenziali, come denaro, inventario o prenotazioni
- La conformità richiede il posizionamento geografico dei dati
Se il carico entra in una regione con repliche e caching, un RDBMS convenzionale rimane spesso la scelta migliore.
Cosa mi offre la coerenza forte e qual è il suo costo?
La coerenza forte significa che, una volta eseguito il commit di una transazione, le letture non vedranno dati precedenti.
Per il prodotto, aiuta a evitare:
- Doppie spese o saldi errati
- Vendita multipla dell'ultimo articolo
- Due utenti che prenotano lo stesso posto
Il compromesso è che durante una partizione di rete un sistema fortemente coerente può bloccare o rifiutare alcune operazioni invece di accettare versioni divergenti della realtà.
Come gestisco i retry in modo sicuro, con idempotenza, nello SQL distribuito?
Affidati a vincoli e transazioni del database:
- Memorizza una
idempotency_keyo equivalente per ogni richiesta o tentativo - Aggiungi un vincolo univoco, ad esempio
(account_id, idempotency_key) - In un'unica transazione, scrivi il record di business e tutte le righe di libro mastro o outbox
Così i retry non producono duplicati, un aspetto essenziale per pagamenti, provisioning e rielaborazione di job in background.
Come scelgo tra Spanner, CockroachDB e YugabyteDB?
Una distinzione pratica:
- Spanner: in genere gestito su GCP, con una solida impostazione multi-regione; la scelta del dialetto SQL incide sulla portabilità.
- CockroachDB: esperienza e protocollo wire simili a Postgres; gestito o self-hosted; non è compatibile al 100% con Postgres.
- YugabyteDB: API SQL compatibile con Postgres, YSQL, più API opzionale in stile Cassandra, YCQL; gestito o self-hosted.
Prima di scegliere, prova il tuo ORM, le migrazioni e qualsiasi estensione Postgres da cui dipendi. Non dare per scontata una sostituzione diretta.
Qual è un buon piano di proof of concept prima di scegliere lo SQL distribuito?
Inizia con un PoC mirato su un workflow critico, come checkout, prenotazione o registrazione nel libro mastro. Verifica:
- Correttezza, senza doppie prenotazioni o aggiornamenti persi
- Latenza p50/p95 delle query principali, includendo obiettivi interregionali
- Comportamento in caso di guasto, perdita di nodo, zona e, se rilevante, regione
- Aspetti operativi fondamentali, monitoraggio, backup e prove di ripristino
Per definire costi e piani, consulta prezzi. Per note di implementazione correlate, esplora il blog.