Gestione del numero crescente di titolari nell'architettura di database multi-tenant


26

Gestire un numero modesto di clienti (tenant) in un server comune con database separati per l'istanza dell'applicazione di ogni tenant è relativamente semplice ed è normalmente il modo corretto per farlo. Attualmente sto esaminando l'architettura di un'applicazione in cui ogni tenant ha la propria istanza di database.

Tuttavia, il problema è che questa applicazione avrà un gran numero di inquilini (5.000-10.000) con un numero considerevole di utenti, forse 2.000 per un singolo inquilino. Dovremo supportare la crescita del sistema da parte di diversi inquilini ogni settimana.

Inoltre, a tutti gli inquilini e ai loro utenti verrà presentato un processo di accesso comune (ovvero ogni inquilino non può avere il proprio URL). Per fare ciò, ho bisogno di un processo di accesso centralizzato e di un mezzo per aggiungere dinamicamente database al sistema e registrare utenti.

  • In che modo il processo di registrazione e creazione del database può essere automatizzato in modo efficace?

  • È probabile che il processo di creazione e registrazione dei database degli inquilini sul sistema causi problemi di prestazioni o blocco. Se pensi che questo possa essere un problema, qualcuno può suggerire modi per mitigarlo?

  • Come posso gestire l'autenticazione centrale in un modo in cui le credenziali dell'utente saranno associate al database di un determinato inquilino ma l'utente può accedere attraverso una pagina comune (cioè attraverso lo stesso URL di accesso, ma la loro applicazione principale sarà su un database di un inquilino specifico ). Gli inquilini dovranno essere in grado di mantenere i propri accessi e autorizzazioni, ma un sistema di accesso centrale deve essere consapevole di questi. Qualcuno può suggerire un modo per farlo?

  • Se ho bisogno di "ridimensionare" aggiungendo più server di database, qualcuno può suggerire quali problemi potrei avere a che fare con la gestione delle identità degli utenti tra server (imitazione ecc.) E un modo per mitigarli?


1
Non ho avuto a che fare con una situazione come questa, ma la mia intuizione sarebbe quella di gestire l'implementazione dell'inquilino preconfigurando i server con tutti i database degli inquilini che pensi possano gestire e quindi assegnare i database degli inquilini pre-costruiti come nuovi inquilini Iscriviti. In questo modo non è necessario preoccuparsi della contesa di risorse durante la distribuzione almeno di DB tenant.
Joel Brown,

1
Sei sicuro di arrivare ovunque vicino a 5.000-10.000 inquilini? E che tutti i tuoi inquilini saranno nel raggio di 2.000 utenti? Nel mio sistema penso che il maggior numero di utenti della nostra applicazione per un singolo tenant fosse di circa 100. E di questi solo 20 circa erano costantemente attivi. Posso chiedere cos'è l'industria / l'applicazione?
Aaron Bertrand

@AaronBertrand È un sistema di gestione dell'apprendimento in cui i servizi saranno parzialmente gratuiti e parzialmente pagati.
coddey,

Risposte:


25

All'estremità inferiore (500 inquilini / 10000 utenti) è così che l'ho fatto. Innanzitutto, disponi di un database di "controllo" globale, centrale e che contiene tutte le informazioni sugli inquilini e sugli utenti (non credo davvero che tu voglia gestirli come accessi con autenticazione SQL). Quindi immagina un database chiamato "Control" con le seguenti tabelle:

CREATE TABLE dbo.Instances
(
  InstanceID INT PRIMARY KEY,
  Connection VARCHAR(255)
  --, ...
);

INSERT dbo.Instances SELECT 1, 'PROD1\Instance1';
INSERT dbo.Instances SELECT 1, 'PROD2\Instance1';
-- ...

CREATE TABLE dbo.Tenants
(
  TenantID INT PRIMARY KEY,
  Name NVARCHAR(255) NOT NULL UNIQUE,
  InstanceID INT -- Foreign key tells which instance this tenant's DB is on
  --, ...
);

INSERT dbo.Tenants SELECT 1, 'MyTenant', 1;
-- ...

CREATE TABLE dbo.Users
(
  UserID INT PRIMARY KEY,
  Username VARCHAR(320) NOT NULL UNIQUE,
  PasswordHash VARBINARY(64), -- because you never store plain text, right?
  TenantID INT -- foreign key
  --, ...
);

INSERT dbo.Users SELECT 1, 'foo@bar.com', 0x43..., 1;

Nel nostro caso, quando abbiamo aggiunto un nuovo tenant, avremmo creato il database in modo dinamico, ma non quando l'utente amministratore ha fatto clic su OK nell'interfaccia utente ... abbiamo avuto un processo in background che ha rimosso i nuovi database da una coda ogni 5 minuti, impostando il modello su single_user e quindi creato ogni nuovo database in serie. Abbiamo fatto questo per (a) impedire all'utente amministratore di attendere la creazione del database e (b) per evitare che due utenti amministratori cercassero di creare un database contemporaneamente o che venisse loro negata la possibilità di bloccare il modello (richiesto durante la creazione di un nuovo database ).

I database sono stati creati con lo schema dei nomi Tenant000000xxdove xxrappresentato Tenants.TenantID. Ciò ha reso i lavori di manutenzione abbastanza facile, invece di avere tutti i tipi di database di nome BurgerKing, McDonalds, KFCecc Non che siamo stati in fast food, usando solo come esempio.

Il motivo per cui non abbiamo pre-allocato migliaia di database come suggerito dal commento è che i nostri utenti amministratori di solito avevano un'idea di quanto sarebbe diventato grande l'inquilino, se fossero prioritari, ecc. Quindi avevano delle scelte di base nell'interfaccia utente che detterebbe le dimensioni iniziali e le impostazioni di crescita automatica, a quale sottosistema del disco andrebbero a finire i file di dati / log, le impostazioni di ripristino, la pianificazione del backup a cui agganciarsi e persino a sapere su quale istanza distribuire il database per bilanciare al meglio l'utilizzo ( anche se i nostri amministratori potrebbero ignorarlo). Una volta creato il database, la tabella dei titolari è stata aggiornata con l'istanza scelta, è stato creato un utente amministratore per il titolare e ai nostri amministratori sono state inviate per e-mail le credenziali per passare al nuovo titolare.

Se si utilizza un unico punto di accesso, non è possibile consentire a più tenant di avere utenti con lo stesso nome utente. Abbiamo scelto di utilizzare l'indirizzo e-mail, che - se tutti gli utenti lavorano per l'azienda e usano il proprio indirizzo e-mail aziendale - dovrebbe andare bene. Sebbene la nostra soluzione alla fine sia diventata più complessa per due motivi:

  1. Avevamo consulenti che lavoravano per più di uno dei nostri clienti e avevano bisogno dell'accesso a più
  2. Avevamo inquilini che a loro volta erano in realtà composti da più inquilini

Quindi, abbiamo finito con una TenantUserstabella che consentiva a un utente di essere associato a più tenant.

Inizialmente quando un utente accede, l'app conoscerà la stringa di connessione solo per il database di controllo. Quando un accesso ha esito positivo, può quindi creare una stringa di connessione in base alle informazioni trovate. Per esempio

SELECT i.Connection
  FROM dbo.Instances AS i
  INNER JOIN dbo.Tenants AS t
  ON i.InstanceID = t.InstanceID
  INNER JOIN dbo.TenantUsers AS u
  ON i.TenantID = u.TenantID
  WHERE u.UserID = @UserID;

Ora l'app potrebbe connettersi al database dell'utente (ogni utente aveva un tenant predefinito ) o l'utente poteva selezionare uno dei tenant a cui poteva accedere. L'app recupera quindi semplicemente la nuova stringa di connessione e reindirizza alla home page per quel tenant.

Se entri in questa area utente da 10 MM che proponi, avrai sicuramente bisogno di questo per bilanciare meglio. È possibile che si desideri federare l'applicazione in modo che abbiano punti di ingresso diversi che si collegano a database di controllo diversi. Se si assegna a ciascun tenant un sottodominio (ad esempio TenantName.YourApplicationDomain.com), è possibile farlo dietro le quinte con DNS / routing senza interromperlo quando è necessario ridimensionare ulteriormente.

C'è molto di più in questo - come @Darin sto solo grattando la superficie qui. Fammi sapere se hai bisogno di una consulenza non gratuita. :-)


Grazie per aver condiviso la tua esperienza, mi ha illuminata e ho cercato di più. Ma hai già scritto Non libero. :(
coddey,

1
Il mio punto era che ho solo così tanto tempo da dedicare alla consulenza gratuita. :-)
Aaron Bertrand

+1 - praticamente esattamente lo stesso approccio che ho usato prima. ~ stesso numero di inquilini, ha funzionato davvero bene.
AdaTheDev,

Come gestire la relazione tra database master e database tenant? (senza l'uso di trigger ecc.)
Jitendra Pancholi

@jitendra non ci sono davvero molte opzioni: quanti dati hai davvero in un database di titolari che devono essere correlati ai dati nel database principale? Inoltre, non sono sicuro di capire la paura popolare dei trigger: un trigger correttamente scritto non è nulla di cui aver paura ...
Aaron Bertrand

10

Hai un progetto abbastanza interessante. Non ho mai visto direttamente nessuno provare a implementare qualcosa di così grande, almeno su SQL Server. Più leggo il tuo post, più domande mi vengono in mente ...

Nel peggiore dei casi, dal punto di vista dell'infrastruttura (che in realtà è lo scenario migliore, dal punto di vista aziendale), sono necessari database 10K ogni 2k utenti. Sono 20.000.000 di utenti. Non riuscirai a provare a gestire accessi di 20 M SQL Server. IMO. Solo il loro numero assoluto, che si occupa di spostarli da un server all'altro, facendo attenzione alle collisioni tra ID e ID non corrispondenti, inoltre non sono sicuro di come SQL Server si comporterebbe con 20 M righe in sys.server_principals. Inoltre, la tua app web probabilmente vorrà connettersi come un numero singolo o molto basso di utenti. IIS non può raggruppare le connessioni a meno che le stringhe DSN non siano identiche. Uno degli attributi di una stringa DSN è il nome utente. Utenti diversi non significano pool.

Dovrai creare il tuo schema di credenziali utente. Dovrà essere in grado di capire a quale inquilino appartiene un utente e quindi il tuo codice web dovrà selezionare il database corretto. I metadati dell'utente sono fondamentali, dovranno essere archiviati da qualche parte, dovranno essere raggruppati o sottoposti a mirroring, dovranno essere veloci e dovranno essere ben protetti (dal punto di vista della sicurezza. IOW, crittografarlo.). Supponendo che SQL sia anche una buona idea qui, terrei questo database lontano dalle istanze che i tenant del server. Questo aiuta da un punto di vista della sicurezza e da un punto di vista del carico, anche se immagino che una volta che un utente è stato convalidato e l'app Web viene indirizzata al database corretto in un'altra istanza, non ci saranno più query su questi metadati dell'utente correlati a quello utente.

Domanda rapida: due utenti diversi, che appartengono a due inquilini diversi, dovrebbero avere lo stesso nome utente?

Un'altra domanda veloce: se ti dico che lavoro per FuBar, Inc., come lo sai? FuBar ti fornirà un elenco di utenti e tu restituirai loro un elenco di nomi utente o si autoalimenteranno?

Dovrai passare a più istanze. Se anche una minima parte di quegli utenti decide di colpire l'applicazione in una sola volta, si scioglierà una singola istanza. Non avrà abbastanza thread di lavoro per eseguire tutte quelle richieste contemporaneamente. Se solo 1000 utenti accedono contemporaneamente alla tua istanza, probabilmente si esauriranno i thread di lavoro e la richiesta inizierà ad accumularsi e attendere. L'ho visto accadere; il sintomo prossimo è che le nuove connessioni non saranno in grado di accedere all'istanza perché non ci sono thread di lavoro disponibili per servirle. Se si tratta di un comportamento di breve durata, l'app potrebbe sopravvivere. In caso contrario, o se l'app è pignola, gli utenti riceveranno errori.

Anche se non avrai molti inquilini da avviare, dovresti iniziare a pensare al futuro e all'automazione perché quando vedi che il tuo server è impantanato e ci sono 10 nuovi inquilini da portare online, è praticamente troppo tardi e il tuo servizio (e i tuoi clienti e i tuoi futuri ex clienti) soffriranno fino a quando non riuscirai a risolvere il problema.

Avrai bisogno di un modo per spostare i database, dai server sovraccaricati ai server leggermente caricati (o nuovi). La possibilità di ottenere o meno una finestra di inattività dipenderà dal tuo SLA.

Stai fornendo un'applicazione specifica, come SalesForce, o questi database sono solo contenitori per qualunque cosa i tuoi inquilini vogliano inserire?

Quanto sono grandi i database? Se non sono molto grandi, puoi semplicemente ripristinare da un file di backup che fornisce un modello. (Questo non è molto diverso da quello che fa il database dei modelli, ma non ho mai visto nessuno usare il modello in modo positivo dai miei giorni con SQL 6.5.) Una volta ripristinato il modello con il nuovo nome del database, potresti quindi personalizzare il nuovo database come necessario per un tenant specifico. Non è possibile eseguire la personalizzazione prima di avere l'inquilino, ovviamente. Se il database è di grandi dimensioni, è possibile seguire la stessa procedura di base, ad eccezione del ripristino anticipato, prima che qualsiasi nuovo tenant abbia bisogno di spazio. Potresti tenere un paio di questi database in giro, forse uno per istanza. Se ne conservi troppi, questo ti costringerà forse ad acquistare più hardware e / o spazio di archiviazione di cui hai bisogno,

Se questa è la tua app, come gestirai gli aggiornamenti degli schemi? Come manterrai le versioni del database diritte con le versioni del codice, se stai utilizzando un singolo URL che arriva alla tua app web?

Come si rilevano e distruggono i database che non sono più in uso? Aspetti che il tuo gruppo A / R dica che qualcuno non ha pagato il conto per tre mesi?

Se i tenant gestiscono le autorizzazioni, ciò implica che hanno una certa comprensione del funzionamento interno dell'app o che l'app ha una struttura di ruolo molto semplice. Usando qualcosa come Blogger come esempio approssimativo, gli utenti possono (leggere post), (leggere post e commentare), (... e creare post), (... e modificare post di altri), (... e ripristinare password di altri utenti) o (... e quant'altro). Avere un ruolo per ciascuno di questi diversi set di diritti e assegnare un utente a un ruolo o a un altro non dovrebbe essere troppo difficile, ma non vuoi che la tua app esegua le dichiarazioni "GRANT". Fai attenzione ai ruoli che hanno una gerarchia e dipendono dall'eredità, può creare confusione. Se stai promuovendo o degradando un utente, direi di estrarlo da tutti i ruoli associati e quindi di aggiungerli all'unico ruolo di cui hanno bisogno. Oh,

Penso di aver solo graffiato la superficie qui e questo post è già troppo lungo. Ciò di cui hai veramente bisogno è un libro, o almeno un white paper di qualcuno che lo ha fatto. Molti di questi ragazzi non parleranno, se lo vedono come un vantaggio competitivo.


Grazie per i commenti, anche se il progetto è interessante. A causa della limitazione delle parole, mantengo il commento molto preciso. È un sistema di gestione dell'apprendimento in cui ogni inquilino avrà una tabella di 120-150 circa. Nessun utente avrà lo stesso nome utente indipendentemente dall'affittuario. Per ridurre ulteriormente la complessità verrà utilizzato il mapping CNAME DNS esempio tenant1.abc.com. Ora il punto di ebollizione è: progettarlo in modo corretto in modo da soddisfare tutti i suggerimenti che hai condiviso e che mi preoccupo. Ottenere il white paper sarà lodevole, ma non è facile, forse. Se vuoi, puoi aggiungere più input. !!!!
coddey,
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.