Como lidar com um número crescente de inquilinos na arquitetura de banco de dados com vários inquilinos

26

O tratamento de um número modesto de clientes (inquilinos) em um servidor comum com bancos de dados separados para a instância de cada inquilino do aplicativo é relativamente simples e normalmente é a maneira correta de fazer isso. Atualmente, estou analisando a arquitetura de um aplicativo em que cada inquilino tem sua própria instância de banco de dados.

No entanto, o problema é que esse aplicativo terá um grande número de inquilinos (5.000 a 10.000) com um número substancial de usuários, talvez 2.000 para um único inquilino. Precisamos apoiar o crescimento do sistema por vários inquilinos toda semana.

Além disso, todos os inquilinos e seus usuários receberão um processo de login comum (ou seja, cada inquilino não pode ter seu próprio URL). Para fazer isso, preciso de um processo de login centralizado e um meio de adicionar dinamicamente bancos de dados ao sistema e registrar usuários.

  • Como o processo de criação de registro e banco de dados pode ser automatizado de maneira robusta?

  • É o processo de criação e registro de bancos de dados de inquilinos no sistema que pode causar problemas de desempenho ou bloqueio. Se você acha que isso pode ser um problema, alguém pode sugerir maneiras de mitigá-lo?

  • Como gerenciar a autenticação central de uma maneira em que as credenciais do usuário serão associadas ao banco de dados de um inquilino específico, mas o usuário pode efetuar login através de uma página comum (ou seja, através do mesmo URL de login, mas seu aplicativo inicial estará no banco de dados de algum inquilino específico ) Os inquilinos deverão poder manter seus próprios logins e permissões, mas um sistema central de logon deve estar ciente disso. Alguém pode sugerir uma maneira de fazer isso?

  • Se eu precisar 'expandir' adicionando vários servidores de banco de dados, alguém pode sugerir quais problemas eu posso ter para lidar com o gerenciamento de identificações de usuários entre servidores (representação etc.) e alguma maneira de atenuar esses problemas?

coddey
fonte
11
Não tive que lidar com uma situação como essa, mas minha intuição seria lidar com a implantação de inquilino pré-configurando servidores com o máximo de bancos de dados de inquilino que você acha que eles podem lidar e, em seguida, apenas atribuir os bancos de dados de inquilino pré-criados como novos inquilinos inscrever-se. Dessa forma, você não precisa se preocupar com a contenção de recursos ao implantar pelo menos bancos de dados de inquilino.
Joel Brown
11
Tem certeza de que chegará perto de 5.000 a 10.000 inquilinos? E que todos os seus inquilinos estarão na faixa de 2.000 usuários? No meu sistema, acho que o maior número de usuários de nosso aplicativo para um único inquilino era de cerca de 100. E desses, apenas 20 ou mais estavam consistentemente ativos. Posso perguntar qual é o setor / aplicativo?
Aaron Bertrand
@AaronBertrand É um sistema de gerenciamento de aprendizado em que os serviços serão parcialmente gratuitos e parcialmente pagos.
Coddey 19/04/12

Respostas:

25

Na extremidade inferior (500 inquilinos / 10000 usuários), foi assim que eu fiz. Primeiro, você tem um banco de dados de "controle" que é global, central e contém todas as informações sobre inquilinos e usuários (eu realmente não acho que você queira gerenciá-los como logins de autenticação SQL). Imagine um banco de dados chamado "Controle" com as seguintes tabelas:

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, '[email protected]', 0x43..., 1;

No nosso caso, quando adicionamos um novo inquilino, construiríamos o banco de dados dinamicamente, mas não quando o usuário administrador clicou em OK na interface do usuário ... tivemos um trabalho em segundo plano que retirou novos bancos de dados de uma fila a cada 5 minutos, configurou o modelo para single_user e, em seguida, criou cada novo banco de dados em série. Fizemos isso para (a) impedir que o usuário administrador aguarde a criação do banco de dados e (b) evitar que dois usuários administradores tentem criar um banco de dados ao mesmo tempo ou que não sejam capazes de bloquear o modelo (necessário ao criar um novo banco de dados )

Os bancos de dados foram criados com o esquema de nomes Tenant000000xxonde xxrepresentados Tenants.TenantID. Isso fez trabalhos de manutenção muito fácil, em vez de ter todos os tipos de bancos de dados nomeados BurgerKing, McDonalds, KFCetc. Não que estávamos em fast food, apenas usando isso como um exemplo.

O motivo de não pré-alocarmos milhares de bancos de dados, conforme sugerido pelo comentário, é que nossos usuários administrativos geralmente tinham uma idéia de quão grande o inquilino se tornaria, se eram de alta prioridade etc. Então, eles tinham escolhas básicas na interface do usuário que ditariam suas configurações iniciais de tamanho e crescimento automático, para qual subsistema de disco seus arquivos de dados / log seriam, suas configurações de recuperação, agendamento de backup e até mesmo saber sobre qual instância implantar o banco de dados para equilibrar melhor o uso ( embora nossos administradores possam substituir isso). Depois que o banco de dados é criado, a tabela de inquilino foi atualizada com a instância escolhida, um usuário administrador foi criado para o inquilino e nossos administradores receberam por e-mail as credenciais para repassar ao novo inquilino.

Se você estiver usando um único ponto de entrada, não é possível permitir que vários inquilinos tenham usuários com o mesmo nome de usuário. Optamos por usar o endereço de email, que - se todos os usuários trabalharem para a empresa e usarem o endereço de email corporativo - deve estar bem. Embora nossa solução tenha se tornado mais complexa por dois motivos:

  1. Tínhamos consultores que trabalhavam para mais de um de nossos clientes e precisávamos de acesso a vários
  2. Tínhamos inquilinos que, na verdade, eram compostos por vários inquilinos

Então, acabamos com uma TenantUserstabela que permite que um usuário seja associado a vários inquilinos.

Inicialmente, quando um usuário faz login, o aplicativo conhece a cadeia de conexão apenas para o banco de dados de controle. Quando um login é bem-sucedido, ele pode criar uma cadeia de conexão com base nas informações encontradas. Por exemplo

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;

Agora, o aplicativo poderia se conectar ao banco de dados do usuário (cada usuário tinha um inquilino padrão ) ou o usuário poderia selecionar um dos inquilinos que poderia acessar. O aplicativo simplesmente recupera a nova cadeia de conexão e redireciona para a página inicial desse inquilino.

Se você entrar nessa área de 10 milhões de usuários que propõe, você definitivamente precisará que isso seja melhor equilibrado. Você pode associar o aplicativo para que eles tenham pontos de entrada diferentes, conectando-se a diferentes bancos de dados de controle. Se você atribuir a cada inquilino um subdomínio (por exemplo, TenantName.YourApplicationDomain.com), poderá fazer isso nos bastidores com DNS / roteamento sem interrompê-los quando precisar expandir ainda mais.

Há muito mais nisso - como @Darin, estou apenas arranhando a superfície aqui. Entre em contato se precisar de uma consulta não gratuita. :-)

Aaron Bertrand
fonte
Obrigado por compartilhar sua experiência.Ele precisa me iluminar.Veja mais sobre ele. Mas você já escreveu Não-livre. :(
coddey
11
Meu argumento era que eu só tenho muito tempo para alocar para aconselhamento gratuito. :-)
Aaron Bertrand
+1 - praticamente exatamente a mesma abordagem que eu usei antes. ~ o mesmo número de inquilinos também funcionou muito bem.
AdaTheDev 20/04
Como lidar com o relacionamento entre o banco de dados mestre e o banco de dados de inquilinos? (sem uso de gatilhos etc)
Jitendra Pancholi
@jitendra não tem muitas opções - quantos dados você realmente tem em um banco de dados de inquilinos que precisa se relacionar com dados no banco de dados mestre? Estou também não tenho certeza eu entendo o medo popular de gatilhos - um gatilho escrito corretamente há nada a temer ...
Aaron Bertrand
10

Você tem um projeto bastante interessante. Eu nunca vi diretamente alguém tentar implementar algo tão grande, pelo menos no SQL Server. Quanto mais eu leio sua postagem, mais perguntas surgem ...

No pior cenário, em termos de infraestrutura (que é o melhor cenário, em termos de negócios), você precisa de 10 mil bancos de dados vezes 2 mil usuários. São 20.000.000 de usuários. Você não terá êxito ao tentar gerenciar logons de 20 M do SQL Server. IMO. Apenas o número absoluto deles, lidando com a transferência de servidor para servidor, observando colisões de ID e IDs incompatíveis, além de não ter certeza de como o SQL Server se comportaria com linhas de 20 M em sys.server_principals. Além disso, seu aplicativo da web provavelmente desejará conectar-se como um número único ou muito baixo de usuários. O IIS não pode agrupar conexões, a menos que suas seqüências DSN sejam idênticas. Um dos atributos de uma sequência DSN é o nome do usuário. Usuários diferentes significa que não há pool.

Você precisará rolar seu próprio esquema de credenciais de usuário. Ele deve ser capaz de descobrir a que inquilino um usuário pertence e, em seguida, seu código da web precisará selecionar o banco de dados apropriado. Esses metadados do usuário são críticos, precisam ser armazenados em algum lugar, precisam ser agrupados ou espelhados, precisam ser rápidos e precisam ser bem protegidos (do ponto de vista da segurança. IOW, criptografá-lo.). Supondo que o SQL seja uma boa idéia aqui, eu manteria esse banco de dados longe das instâncias que o servidor inquilina. Isso ajuda do ponto de vista da segurança e do ponto de vista da carga, embora eu ache que, uma vez que os usuários sejam validados e o aplicativo Web seja direcionado para o banco de dados correto em outra instância, não haverá mais consultas sobre os metadados desse usuário relacionados a esse do utilizador.

Pergunta rápida: dois usuários diferentes, pertencentes a dois inquilinos diferentes, devem ter o mesmo nome de usuário?

Outra pergunta rápida: se eu lhe disser que trabalho na FuBar, Inc., como você sabe disso? O FuBar fornecerá uma lista de usuários e você fornecerá uma lista de nomes de usuários ou eles serão auto-fornecidos?

Você precisará executar várias instâncias. Se mesmo uma fração desses usuários decidir acessar o aplicativo de uma vez, uma única instância derreterá. Não haverá threads de trabalho suficientes para executar todas essas solicitações de uma só vez. Se apenas 1.000 usuários atingirem sua instância ao mesmo tempo, provavelmente ela ficará sem threads de trabalho e a solicitação começará a se acumular e aguardar. Eu já vi isso acontecer; o sintoma mais próximo é que novas conexões não poderão efetuar logon na instância porque não há threads de trabalho disponíveis para atendê-las. Se esse comportamento for de curta duração, seu aplicativo poderá sobreviver. Caso contrário, ou seu aplicativo é exigente, os usuários receberão erros.

Mesmo que você não tenha muitos inquilinos para iniciar, comece a pensar no futuro e na automação, porque quando perceber que o servidor está atolado e houver 10 novos inquilinos para colocar online, é muito tarde e seu serviço (e seus clientes e seus futuros ex-clientes) sofrerão até que você resolva o problema.

Você precisará de uma maneira de mover bancos de dados, de servidores sobrecarregados para servidores pouco carregados (ou novos). A obtenção ou não de uma janela de tempo de inatividade dependerá do seu SLA.

Você está fornecendo um aplicativo específico, como SalesForce, ou esses bancos de dados são apenas contêineres para o que seus inquilinos desejam incluir?

Qual o tamanho dos bancos de dados? Se eles não forem muito grandes, você poderá restaurar a partir de um arquivo de backup que fornece um modelo. (Isso não é muito diferente do que o banco de dados do modelo faz, mas não vejo ninguém realmente usar o modelo desde os meus dias com o SQL 6.5.) Depois que o modelo for restaurado para o novo nome do banco de dados, você poderá em seguida, personalize o novo banco de dados conforme necessário para um inquilino específico. Você não pode fazer a personalização antes de ter o inquilino, obviamente. Se o banco de dados for grande, você poderá seguir o mesmo procedimento básico, exceto a restauração antecipada, antes que qualquer novo inquilino precise do espaço. Você pode manter alguns desses bancos de dados por perto, talvez um por instância. Se você mantiver muitos por perto, isso forçará você a comprar mais hardware e / ou armazenamento do que o necessário,

Se esse for seu próprio aplicativo, como você lida com as atualizações dos esquemas? Como você manterá as versões do banco de dados alinhadas com as versões do código, se você estiver usando um único URL que acessa seu aplicativo Web?

Como você detecta e destrói bancos de dados que não estão mais em uso? Você espera até o seu grupo de contas a receber dizer que alguém não paga a conta há três meses?

Se os inquilinos estão gerenciando permissões, isso significa que eles têm alguma compreensão do funcionamento interno do aplicativo ou que seu aplicativo tem uma estrutura de funções muito simples. Usando algo como o Blogger como exemplo, os usuários podem (ler postagens), (ler postagens e fazer comentários), (... e criar postagens), (... e editar postagens de outras pessoas), (... e podem redefinir senhas de outros usuários) ou (... e o que for). Ter uma função para cada um desses diferentes conjuntos de direitos e atribuir um usuário a uma função ou outra não deve ser muito difícil, mas você não deseja que seu aplicativo execute instruções 'GRANT'. Cuidado com as funções que têm uma hierarquia e dependem da herança, pois pode ficar confuso. Se você estiver promovendo ou rebaixando um usuário, sugiro retirá-lo de todas as funções associadas e adicioná-lo novamente à única função necessária. Oh,

Eu acho que apenas arranhei a superfície aqui, e este post já é muito longo. O que você realmente precisa é de um livro, ou pelo menos de um whitepaper de alguém que fez isso. A maioria desses caras não fala, se eles vêem isso como uma vantagem competitiva.

darin strait
fonte
Obrigado pelos comentários. De fato, o projeto é interessante. Devido à limitação da palavra, manteremos o comentário muito preciso. É um sistema de gerenciamento de aprendizado em que cada inquilino terá em torno de 120-150 tabelas. Nenhum usuário terá o mesmo nome de usuário, independentemente do inquilino. Para reduzir ainda mais a complexidade, o mapeamento DNS CNAME será usado, exemplo tenant1.abc.com. Agora, o ponto de ebulição é - projetá-lo da maneira correta, para atender a todas as sugestões que você compartilhou e estou me preocupando. Obter um white paper será louvável, mas talvez não seja fácil. Procurando mais informações, se puder. !!!!
Coddey 19/04/12