Un sistema multi-tenant con SQL Server 2016, Shard o deve avere l'isolamento del tenant tramite un database separato per tenant?


12

Dato il caso d'uso:

  • I dati degli inquilini non devono essere incrociati, un inquilino non ha bisogno dei dati di un altro inquilino.
  • Ogni inquilino potrebbe potenzialmente avere un grande volume di dati storici.
  • SQL Server è ospitato nell'istanza di AWS EC2.
  • Ogni inquilino è geograficamente distante.
  • Vi è l'intenzione di utilizzare strumenti di visualizzazione di terze parti come PowerBI Embedded
  • Il volume di dati dovrebbe crescere nel tempo
  • Il costo del sistema è limitato.
  • La soluzione deve essere mantenibile senza un DBA di produzione 24/7
  • La soluzione dovrebbe essere in grado di ridimensionarsi orizzontalmente.
  • Il numero totale di inquilini è inferiore a 50

Quale sarebbe un'architettura consigliata, ci sono implementazioni di riferimento per questo caso d'uso? Credo che molte persone potrebbero aver già affrontato questo problema per lo sviluppo del software aziendale.

Penso che questa sia una situazione diversa dalla gestione del numero crescente di titolari nell'architettura di database multi-tenant . Il caso d'uso menzionato in quella domanda riguarda un numero maggiore di inquilini, che è molto diverso dall'avere pochissimi (50) locatari di grandi dimensioni. L'architettura menzionata potrebbe essere una soluzione qui, che è quello che voglio sapere di più.

Risposte:


16

Il problema con lo sharding è che l'applicazione deve sapere quale frammento interrogare. Generalmente, questo viene fatto partendo da qualcosa come client. Adatterò uno dei miei vecchi post sul blog da utilizzare come risposta.

Quando si crea un'applicazione per molti client, esistono due modi comuni per progettare i database:

  • Opzione A: inserire tutti i client nello stesso database
  • Opzione 2: creare un database per client

Mettere tutti i client nello stesso database

È semplice: basta aggiungere una tabella Client nella parte superiore dello schema, aggiungere una tabella ClientUsers per assicurarsi che le persone vedano solo i propri dati e via.

Vantaggi di questo approccio:

Gestione dello schema più semplice. Quando gli sviluppatori distribuiscono una nuova versione dell'applicazione, devono solo apportare modifiche allo schema in un database. Non ci sono preoccupazioni per i diversi clienti che sono fuori sincrono o nella versione sbagliata.

Ottimizzazione delle prestazioni più semplice. Possiamo controllare l'utilizzo dell'indice e le statistiche in un solo posto, implementare facilmente i miglioramenti e vedere immediatamente gli effetti su tutti i nostri clienti. Con centinaia o migliaia di database, anche il più piccolo cambiamento può essere difficile da coordinare. Siamo in grado di controllare i contenuti della cache delle procedure e sapere con certezza quali query o procedure memorizzate sono le più intense in tutta la nostra intera applicazione, mentre se utilizziamo database separati per client, potremmo avere un tempo più duro aggregando l'uso delle query tra piani di esecuzione diversi.

Più facile da costruire un'API esterna. Se dobbiamo consentire l'accesso a tutto il nostro database affinché gli estranei possano costruire prodotti, possiamo farlo più facilmente se tutti i dati si trovano in un unico database. Se l'API deve gestire il raggruppamento dei dati da più database su più server, aggiunge tempo di sviluppo e test. (D'altra parte, quella cosa di "più server" inizia a suggerire una restrizione per lo scenario da un database a dominarli tutti: un database di solito significa che tutto il nostro carico influisce su un solo server di database.) Nel tuo caso , con PowerBI, avere tutti in un database renderà molto più semplice la gestione delle connessioni.

Alta disponibilità e ripristino di emergenza più facili. È davvero molto semplice gestire il mirroring del database, il log shipping, la replica e il clustering se tutto ciò di cui dobbiamo preoccuparci è solo un database. Siamo in grado di costruire rapidamente un controllo di un'infrastruttura.

Mettere ciascun cliente nel proprio database o frammento

Hai ancora bisogno di un elenco di client, ma ora diventa una directory - per ogni client, segui anche il frammento in cui vive. All'avvio, la tua app interroga questa tabella e la memorizza nella cache. Quando necessita di dati per un client, si connette direttamente a quel frammento (database e server).

Vantaggi di questo approccio:

Ripristini più semplici per client singolo. I clienti sono sacchetti di carne inaffidabili. (Tranne il mio - sono sacchi di carne affidabili.) Hanno tutti i tipi di "oops" momenti in cui vogliono recuperare tutti i loro dati in un punto nel tempo, e questo è un enorme dolore nella parte posteriore se i loro dati si mescolano con altri dati client nelle stesse tabelle. I ripristini in uno scenario di database a client singolo sono semplicissimi: basta ripristinare il database del client. Nessun altro è interessato.

Esportazioni di dati più semplici. Ai clienti piace mettere le mani sui propri dati. Vogliono la sicurezza di sapere che possono ottenere i loro dati ogni volta che vogliono, evitando il temuto scenario di blocco del fornitore e vogliono fare i propri report. Con i dati di ciascun client isolati nel proprio database, possiamo semplicemente dare loro una copia del proprio backup del database. Non è necessario creare API di esportazione dei dati.

Scalabilità multi-server più semplice. Quando la nostra applicazione richiede più potenza di quella che possiamo ottenere da un singolo server, possiamo dividere i database tra più server. Possiamo anche distribuire il carico geograficamente, mettendo i server in Asia o in Europa più vicini ai clienti.

Ottimizzazione delle prestazioni per client più semplice. Se alcuni client utilizzano funzionalità o report diversi, possiamo creare un set specializzato di indici o visualizzazioni indicizzate solo per quei client senza aumentare le dimensioni dei dati di tutti. Certo, qui c'è qualche rischio: consentendo differenze di schema tra i client, abbiamo appena reso le nostre implementazioni di codice un po 'più rischiose e la nostra gestione delle prestazioni più difficile.

Gestione della sicurezza più semplice. Finché abbiamo bloccato correttamente la sicurezza con un utente per database, non dobbiamo preoccuparci che il Cliente X acceda ai dati del Cliente Y. Tuttavia, se utilizziamo un solo login per tutti, non abbiamo davvero affrontato questa preoccupazione.

Finestre di manutenzione più semplici. In un ambiente globale in cui i clienti sono sparsi in tutto il mondo, è più facile portare i clienti offline per manutenzione se possiamo farlo in gruppi o zone.

Quale è giusto per te?

Non esiste una scelta giusta: devi conoscere i punti di forza e di debolezza della tua azienda. Prendiamo due dei miei clienti come esempi.

La società A eccelle nell'ottimizzazione delle prestazioni dell'hardware. Sono davvero bravi a strappare le ultime prestazioni dall'hardware e non si preoccupano di sostituire l'hardware di SQL Server su un ciclo di 12-18 mesi. (Aggiornano i server Web ogni 4-6 mesi!) Il loro tallone d'Achille ha requisiti di conformità e sicurezza estremi. Hanno incredibili esigenze di controllo ed è più semplice implementare controlli a prova di proiettile su un singolo server, un singolo database piuttosto che gestire tali requisiti su migliaia di database su dozzine di server. Hanno scelto un database, un server, molti client.

La società 2 eccelle nelle pratiche di sviluppo. La gestione delle modifiche dello schema e delle distribuzioni di codice su migliaia di database non è un problema per loro. Hanno clienti in tutto il mondo e stanno elaborando transazioni con carta di credito per quei clienti tutto il giorno. Hanno bisogno della capacità di distribuire il carico geograficamente e non vogliono sostituire i server in tutto il mondo ogni 12-18 mesi. Hanno scelto un database per ogni client e sta pagando quando iniziano a mettere server SQL in Asia ed Europa per i loro clienti offshore.


"Nel tuo caso, con PowerBI, avere tutti in un database renderà molto più semplice la gestione delle connessioni". In questo momento PowerBI Embedded non ha la sicurezza a livello di riga e quindi avere ogni tenant in un database sta causando alcuni dubbi su questo caso d'uso, vedi: community.powerbi.com/t5/Developer/… , alla luce di queste informazioni potresti riformulare questo o suggerire un'alternativa o correggere la mia comprensione?
DS

Inoltre, "Mettendo ogni cliente nel proprio database o frammento" potresti approfondire qui la differenza tra questi due suggerimenti
DS

Dirò solo che dover distribuire in più di un database non è così male come sembra. Nel 2017 abbiamo molte opzioni che semplificano l'implementazione delle modifiche a 1, 5 o 900 database. E quando si hanno eccezioni per clienti specifici, questi di solito possono essere introdotti in quei database in modo tale che non interferiscano con il codice comune.
Aaron Bertrand

5

Un'ulteriore considerazione che non ho ancora visto in altre risposte.

Avere un design che consente a molti tenant in un singolo database darà flessibilità in seguito. Se le richieste di caricamento / ridimensionamento / sicurezza / posizione geografica suggeriscono in seguito che un tenant dovrebbe disporre di un database separato che può essere creato ripristinando il DB currect sulla nuova istanza. I dati degli altri inquilini sono ancora protetti da qualunque meccanismo fosse in atto. I dati ormai obsoleti possono essere rimossi frammentariamente dai database vecchi e nuovi quando il tempo lo consente.

Il contrario non è vero. Il consolidamento di molti database a un tenant richiederà molto più lavoro.


4

Una pratica che semplifica notevolmente i modelli multi-tenant, anche se interrompe la normalizzazione *, consiste nell'includere una colonna su ogni tabella per l'inquilino. Potresti chiamarlo TenantID. In questo modo ogni query eseguita sul database può filtrare su TenantID su ogni tabella e puoi utilizzare il partizionamento del database per isolare i dati per ciascun tenant e velocizzare le query avendo partizioni allineate. In questo modo è molto più semplice avere tutti i tenant in un database.

* Non sempre interrompe la normalizzazione, ma può farlo. Ad esempio, se si dispone di una Persone una PersonAddresstabella. La Persontabella avrà TenantID, PersonIDcome chiave primaria. La PersonAddresstabella avrà TenantID, PersonID, AddressTypeIDcome chiave primaria ciò che sto suggerendo.

Normalmente PersonIDsarebbe sufficiente, perché potevi unirti di nuovo al Persontavolo per trovare il file Tenant. Sto suggerendo di TenantIDandare avanti per ogni tabella successiva, anche quando una chiave più sottile funzionerebbe.

Comprendevo che il trasferimento di qualsiasi informazione a una tabella che potesse essere derivata da altri dati era considerato una violazione della normalizzazione. Ma forse usare i tasti sottili è solo una buona pratica.


Grazie, sono d'accordo con il suggerimento e per aggiungerlo, vorrei menzionare questo campo TenantID deve essere un tipo intero e non un GUID, ci siamo bruciati in questo modo per le prestazioni.
DS

3
Ma anche se scegli di portare TenantID nelle tabelle secondarie, cosa che non devi fare, una chiave più ampia non significa che la normalizzazione sia "interrotta". Proprio come scegliere un GUID su IDENTITÀ (una chiave più ampia) non rompe la normalizzazione, né scegliere una chiave naturale più ampia invece di usare i surrogati.
Aaron Bertrand
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.