Um sistema multilocatário com SQL Server 2016, Shard ou deve ter isolamento de inquilino por meio de banco de dados separado por inquilino?


12

Dado o caso de uso:

  • Os dados do inquilino não devem ter interferência, um inquilino não precisa dos dados de outro inquilino.
  • Cada inquilino pode ter um grande volume de dados históricos.
  • O SQL Server está hospedado na instância do AWS EC2.
  • Cada inquilino está geograficamente distante.
  • Existe a intenção de usar ferramentas de visualização de terceiros, como o PowerBI Embedded
  • O volume de dados deve crescer com o tempo
  • O custo do sistema é restrito.
  • A solução deve ser mantida sem um DBA de produção 24/7
  • A solução deve poder ser dimensionada horizontalmente.
  • O número total de inquilinos é inferior a 50

Qual seria uma arquitetura recomendada, existem implementações de referência para este caso de uso? Acredito que muitas pessoas podem já ter enfrentado esse problema no desenvolvimento de software corporativo.

Penso que esta é uma situação diferente da manipulação de um número crescente de inquilinos na arquitetura de banco de dados multilocatário . O caso de uso mencionado nessa pergunta lida com um número maior de inquilinos, o que é muito diferente de ter muito poucos (50) inquilinos grandes. A arquitetura mencionada pode ser uma solução aqui, e é sobre isso que quero saber mais.

Respostas:


16

O problema com o sharding é que o aplicativo precisa saber qual shard consultar. Geralmente, isso é feito dividindo algo como o cliente. Vou adaptar um dos meus posts antigos para usar como resposta.

Ao criar um aplicativo para muitos clientes, há duas maneiras comuns de criar o (s) banco (s) de dados:

  • Opção A: Coloque todos os clientes no mesmo banco de dados
  • Opção 2: criar um banco de dados por cliente

Colocando todos os clientes no mesmo banco de dados

É simples: basta adicionar uma tabela Client na parte superior do esquema, adicionar uma tabela ClientUsers para garantir que as pessoas apenas vejam seus próprios dados e lá vamos nós.

Benefícios desta abordagem:

Gerenciamento de esquema mais fácil. Quando os desenvolvedores implantam uma nova versão do aplicativo, eles apenas precisam fazer alterações de esquema em um banco de dados. Não há preocupações sobre diferentes clientes estarem fora de sincronia ou na versão errada.

Ajuste de desempenho mais fácil. Podemos verificar o uso e as estatísticas do índice em um único local, implementar melhorias facilmente e ver os efeitos imediatamente em todos os nossos clientes. Com centenas ou milhares de bancos de dados, até a menor alteração pode ser difícil de coordenar. Podemos verificar o conteúdo do cache do procedimento e saber com certeza quais consultas ou procedimentos armazenados são mais intensivos em todo o aplicativo, enquanto que, se estivermos usando bancos de dados separados por cliente, podemos ter um tempo de agregação de consulta mais difícil em diferentes planos de execução.

Mais fácil de criar uma API externa. Se precisarmos conceder acesso a todo o banco de dados para que terceiros construam produtos, isso será mais fácil se todos os dados estiverem em um único banco de dados. Se a API precisar lidar com o agrupamento de dados de vários bancos de dados em vários servidores, ela adicionará tempo de desenvolvimento e teste. (Por outro lado, essa coisa de “vários servidores” começa a sugerir uma restrição para o cenário de um banco de dados para governar todos: um banco de dados geralmente significa que toda a nossa carga afeta apenas um servidor de banco de dados.) No seu caso , com o PowerBI, ter todos em um banco de dados facilitará muito o gerenciamento de conexões.

Alta disponibilidade e recuperação de desastres mais fáceis. É muito, muito simples gerenciar o espelhamento de banco de dados, o envio de logs, a replicação e o cluster, se tudo o que precisamos nos preocupar é apenas um banco de dados. Podemos construir uma grande quantidade de infraestrutura rapidamente.

Colocando cada cliente em seu próprio banco de dados ou fragmento

Você ainda precisa de uma lista de clientes, mas agora ela se torna um diretório - para cada cliente, você também rastreia o fragmento em que vive. Na inicialização, seu aplicativo consulta essa tabela e a armazena em cache na RAM. Quando precisa de dados para um cliente, ele se conecta diretamente a esse fragmento (banco de dados e servidor).

Benefícios desta abordagem:

Restauração de cliente único mais fácil. Os clientes são sacos de carne não confiáveis. (Exceto os meus - são sacos de carne confiáveis.) Eles têm todos os tipos de momentos em que desejam recuperar todos os seus dados de volta a um ponto no tempo, e isso é uma grande dor na retaguarda se os dados forem misturados com outros dados do cliente nas mesmas tabelas. As restaurações em um cenário de banco de dados de cliente único são fáceis de matar: basta restaurar o banco de dados do cliente. Ninguém mais é afetado.

Exportação de dados mais fácil. Os clientes adoram colocar as mãos em seus dados. Eles querem a segurança de saber que podem obter seus dados a qualquer momento, evitando o temido cenário de dependência de fornecedores e desejam fazer seus próprios relatórios. Com os dados de cada cliente isolados em seu próprio banco de dados, podemos simplesmente fornecer uma cópia do backup de seu próprio banco de dados. Não precisamos criar APIs de exportação de dados.

Escalabilidade multi-servidor mais fácil. Quando nosso aplicativo precisa de mais energia do que podemos obter em um único servidor, podemos dividir os bancos de dados entre vários servidores. Também podemos distribuir a carga geograficamente, colocando os servidores na Ásia ou na Europa mais próximos dos clientes.

Ajuste de desempenho mais fácil por cliente. Se alguns clientes usam recursos ou relatórios diferentes, podemos criar um conjunto especializado de índices ou visualizações indexadas apenas para esses clientes, sem aumentar o tamanho dos dados de todos. Concedido, há algum risco aqui - ao permitir diferenças de esquema entre clientes, acabamos de tornar nossas implantações de código um pouco mais arriscadas e nosso gerenciamento de desempenho mais difícil.

Gerenciamento de segurança mais fácil. Desde que tenhamos bloqueado adequadamente a segurança com um usuário por banco de dados, não precisamos nos preocupar com o acesso do Cliente X aos dados do Cliente Y. No entanto, se apenas usarmos um único login para todos, não abordaremos realmente essa preocupação.

Janelas de manutenção mais fáceis. Em um ambiente global em que os clientes estão espalhados pelo mundo, é mais fácil colocar os clientes offline para manutenção, se pudermos fazê-lo em grupos ou zonas.

Qual é a certa para você?

Não há uma escolha certa: você precisa conhecer os pontos fortes e fracos da sua própria empresa. Vamos dar dois de meus clientes como exemplos.

A empresa A se destaca no ajuste de desempenho de hardware. Eles são realmente muito bons em extrair o último pedaço de desempenho do hardware e não se importam em substituir o hardware do SQL Server em um ciclo de 12 a 18 meses. (Eles atualizam os servidores da Web a cada 4-6 meses!) O calcanhar de Aquiles deles / delas é exigências extremas de conformidade e segurança. Eles têm necessidades de auditoria incríveis, e é mais fácil implementar controles à prova de balas em um único servidor, único banco de dados do que gerenciar esses requisitos em milhares de bancos de dados em dezenas de servidores. Eles escolheram um banco de dados, um servidor, muitos clientes.

A empresa 2 se destaca nas práticas de desenvolvimento. Gerenciar alterações de esquema e implantações de código em milhares de bancos de dados simplesmente não é um problema para eles. Eles têm clientes em todo o mundo e estão processando transações com cartão de crédito para esses clientes o tempo todo. Eles precisam da capacidade de distribuir a carga geograficamente e não querem substituir servidores ao redor do mundo a cada 12 ou 18 meses. Eles escolheram um banco de dados para cada cliente, e está valendo a pena quando começam a colocar SQL Servers na Ásia e na Europa para seus clientes offshore.


"No seu caso, com o PowerBI, ter todos em um banco de dados facilitará muito o gerenciamento de conexões". No momento, o PowerBI Embedded não possui segurança no nível de linha e, portanto, tendo cada inquilino em um banco de dados está causando algumas dúvidas sobre esse caso de uso, consulte: community.powerbi.com/t5/Developer/… , à luz dessas informações, você pode reformular isso ou sugerir uma alternativa ou corrigir meu entendimento?
DS

Além disso, "colocando cada cliente em seu próprio banco de dados ou Shard", você poderia elaborar sobre a diferença aqui entre estas duas sugestões
DS

Vou apenas dizer que ter que implantar em mais de um banco de dados não é tão ruim quanto você faz parecer. Em 2017, temos muitas opções que facilitam a implantação de alterações em 1, 5 ou 900 bancos de dados. E quando você tem exceções para clientes específicos, elas geralmente podem ser introduzidas nesses bancos de dados de forma que não interfiram no código comum.
Aaron Bertrand

5

Mais uma consideração que ainda não vi em outras respostas.

Ter um design que permita muitos inquilinos em um único banco de dados dará flexibilidade posteriormente. Se as demandas de carregamento / expansão / segurança / localização geográfica sugerirem posteriormente que um inquilino deve ter um banco de dados separado, ele pode ser criado restaurando o banco de dados atual na nova instância. Os dados dos outros inquilinos ainda estão protegidos por quaisquer mecanismos existentes. Os dados agora obsoletos podem ser removidos aos poucos dos bancos de dados antigos e novos conforme o tempo permitir.

O contrário não é verdade. A consolidação de muitos bancos de dados de um inquilino exigirá consideravelmente mais trabalho.


4

Uma prática que facilita muito os modelos de vários inquilinos, mesmo que quebre a normalização *, é incluir uma coluna em todas as tabelas do inquilino. Você poderia chamá-lo de TenantID. Dessa forma, toda consulta executada no banco de dados pode filtrar o TenantID em todas as tabelas, e você pode usar o particionamento do banco de dados para isolar os dados de cada inquilino e acelerar as consultas alinhando partições. Muito mais fácil ter todos os inquilinos em um banco de dados dessa maneira.

* Nem sempre quebra a normalização, mas pode. Por exemplo, se você tiver uma Persone uma PersonAddresstabela. A Persontabela terá TenantID, PersonIDcomo chave primária. A PersonAddresstabela terá TenantID, PersonID, AddressTypeIDcomo chave primária o que estou sugerindo.

Normalmente, PersonIDisso seria suficiente, porque você poderia juntar isso de volta à Personmesa para encontrar o Tenant. Estou sugerindo que você carregueTenantID avance para todas as tabelas subseqüentes, mesmo quando uma chave mais fina funcionaria.

Entendi que transportar qualquer informação para uma tabela que pudesse ser derivada de outros dados era considerada uma quebra da normalização. Mas talvez o uso de teclas finas seja apenas uma prática recomendada.


Obrigado, concordo com a sugestão e, para acrescentar, gostaria de mencionar esse campo TenantID deve ser um tipo inteiro e não um GUID, fomos queimados dessa maneira pelo desempenho.
DS,

3
Mas, mesmo se você optar por transportar o TenantID em tabelas filho, o que você não precisa fazer, uma chave mais ampla não significa que a normalização esteja "interrompida". Assim como escolher um GUID sobre IDENTITY (uma chave mais larga) não quebra a normalização, nem escolher uma chave natural mais ampla em vez de usar substitutos.
Aaron Bertrand
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.