L'approccio migliore per lo sharding delle tabelle MySQL per non farlo a meno che non sia assolutamente inevitabile farlo.
Quando scrivi un'applicazione, di solito vuoi farlo in un modo che massimizzi la velocità, la velocità dello sviluppatore. Ottimizzi per latenza (tempo prima che la risposta sia pronta) o throughput (numero di risposte per unità di tempo) solo quando necessario.
Si partiziona e quindi si assegnano le partizioni a host diversi (=shard) solo quando la somma di tutte queste partizioni non si adatta più a una singola istanza del server di database, il motivo è che scrive o legge.
Il caso di scrittura è a) la frequenza delle scritture sta sovraccaricando permanentemente i dischi di questo server oppure b) sono in corso troppe scritture in modo che la replica rimanga permanentemente in ritardo in questa gerarchia di replica.
Il caso di lettura per lo sharding si verifica quando la dimensione dei dati è così grande che il set di lavoro non si adatta più alla memoria e le letture dei dati iniziano a colpire il disco invece di essere servite dalla memoria per la maggior parte del tempo.
Solo quando hai per shard fallo.
Nel momento in cui shard, lo stai pagando in diversi modi:
Gran parte del tuo SQL non è più dichiarativo.
Normalmente, in SQL stai dicendo al database quali dati desideri e lascia che sia l'ottimizzatore a trasformare quella specifica in un programma di accesso ai dati. Questa è una buona cosa, perché è flessibile e perché scrivere questi programmi di accesso ai dati è un lavoro noioso che danneggia la velocità.
Con un ambiente partizionato probabilmente stai unendo una tabella sul nodo A rispetto a dati sul nodo B, oppure hai una tabella più grande di un nodo, sui nodi A e B e stai unendo i dati da essa con i dati che si trovano sui nodi B e C. Stai iniziando a scrivere manualmente risoluzioni di join basate su hash lato applicazione per risolverlo (o stai reinventando il cluster MySQL), il che significa che finisci con molto SQL che non è più dichiarativo, ma esprime la funzionalità SQL in modo procedurale (ad es. stai usando le istruzioni SELECT nei loop).
Stai riscontrando molta latenza di rete.
Normalmente, una query SQL può essere risolta localmente e l'ottimizzatore conosce i costi associati agli accessi al disco locale e risolve la query in un modo che riduce al minimo i costi.
In un ambiente partizionato, le query vengono risolte eseguendo accessi chiave-valore attraverso una rete a più nodi (si spera con accessi chiave in batch e non ricerche di chiavi individuali per round trip) o spingendo parti di WHERE
clausola in avanti ai nodi in cui possono essere applicati (che è chiamato 'condizione pushdown'), o entrambi.
Ma anche nel migliore dei casi ciò comporta molti più viaggi di andata e ritorno della rete rispetto a una situazione locale, ed è più complicato. Soprattutto perché l'ottimizzatore MySQL non sa nulla della latenza di rete (Ok, il cluster MySQL sta lentamente migliorando, ma per MySQL vanilla al di fuori del cluster è ancora vero).
Stai perdendo molto del potere espressivo di SQL.
Ok, probabilmente è meno importante, ma i vincoli di chiave esterna e altri meccanismi SQL per l'integrità dei dati non sono in grado di estendersi su più shard.
MySQL non ha API che consentano query asincrone funzionanti.
Quando i dati dello stesso tipo risiedono su più nodi (ad es. dati utente sui nodi A, B e C), spesso è necessario risolvere le query orizzontali su tutti questi nodi ("Trova tutti gli account utente che non sono stati registrati per 90 giorni o più"). Il tempo di accesso ai dati cresce linearmente con il numero di nodi, a meno che non sia possibile richiedere più nodi in parallelo e i risultati aggregati man mano che arrivano ("Map-Reduce").
Il presupposto per questo è un'API di comunicazione asincrona, che non esiste per MySQL in una buona forma funzionante. L'alternativa è un sacco di biforcazioni e connessioni nei processi figlio, che sta visitando il mondo di succhiare con un abbonamento stagionale.
Una volta avviato lo sharding, la struttura dei dati e la topologia di rete diventano visibili come punti di prestazioni per la tua applicazione. Per funzionare ragionevolmente bene, la tua applicazione deve essere consapevole di queste cose e ciò significa che solo il partizionamento orizzontale a livello di applicazione ha senso.
La domanda è più se vuoi eseguire il partizionamento automatico (determinando quale riga va in quale nodo eseguendo l'hashing delle chiavi primarie, ad esempio) o se vuoi dividere funzionalmente in modo manuale ("Le tabelle relative alla user story di xyz vanno a questo master, mentre le tabelle relative abc e def vanno a quel master").
Il partizionamento orizzontale funzionale ha il vantaggio che, se eseguito correttamente, è invisibile alla maggior parte degli sviluppatori per la maggior parte del tempo, poiché tutte le tabelle relative alla loro storia utente saranno disponibili localmente. Ciò consente loro di beneficiare dell'SQL dichiarativo il più a lungo possibile e comporterà anche una minore latenza di rete poiché il numero di trasferimenti tra reti è ridotto al minimo.
Il partizionamento orizzontale funzionale ha lo svantaggio di non consentire che una singola tabella sia più grande di un'istanza e richiede l'attenzione manuale di un designer.
Il partizionamento orizzontale funzionale ha il vantaggio di essere eseguito in modo relativamente semplice su una base di codice esistente con un numero di modifiche non eccessivamente elevato. http://Booking.com l'ha fatto più volte negli ultimi anni e ha funzionato bene per loro.
Detto questo, guardando la tua domanda, credo che tu stia ponendo le domande sbagliate, o sto completamente fraintendendo la tua affermazione del problema.