Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Divida um arquivo de dados num conjunto de partições horizontais ou shards. Esta abordagem pode melhorar a escalabilidade quando armazena e acede a grandes volumes de dados.
Contexto e problema
Um armazenamento de dados num único servidor tem as seguintes limitações:
Espaço de armazenamento: Um repositório de dados para uma aplicação cloud em grande escala pode conter um grande volume de dados que cresce ao longo do tempo. Um servidor fornece uma quantidade finita de armazenamento em disco, e pode substituir discos existentes por discos maiores ou adicionar mais discos à medida que os volumes de dados crescem. O sistema acaba por atingir um limite em que não se pode aumentar a capacidade de armazenamento num único servidor.
Recursos computacionais: Uma aplicação na cloud deve suportar um grande número de utilizadores simultâneos, cada um a executar consultas no armazenamento de dados. Um único servidor pode não fornecer potência computacional suficiente para esta carga, o que resulta em tempos de resposta prolongados e timeouts. Podes adicionar memória ou atualizar processadores, mas o sistema atinge um limite em que não podes aumentar mais os recursos de computação.
Largura de banda de rede: A taxa a que um único servidor pode receber pedidos e enviar respostas limita o desempenho do armazenamento de dados. O volume de tráfego de rede pode exceder a capacidade da ligação de rede, o que resulta em pedidos falhados.
Geografia: Os requisitos legais, de conformidade ou de desempenho podem exigir que armazene os dados dos utilizadores na mesma região geográfica dos utilizadores. Se os utilizadores forem diferentes países/regiões, pode não conseguir armazenar todos os dados da aplicação num único armazenamento de dados.
Para adiar temporariamente estas limitações, pode escalar verticalmente adicionando capacidade de disco, potência de processamento, memória e ligações de rede. Uma aplicação na cloud que deve suportar um grande número de utilizadores e elevados volumes de dados precisa de escalar horizontalmente.
Solução
Divida o arquivo de dados em partições horizontais. Cada fragmento tem o mesmo esquema mas contém o seu próprio subconjunto distinto dos dados. Cada fragmento é um armazenamento completo de dados que pode conter dados de várias entidades de diferentes tipos. Um shard corre num servidor que funciona como nó de armazenamento.
Este padrão possui as vantagens seguintes:
Podes escalar o sistema adicionando mais fragmentos em nós de armazenamento extra.
Um sistema pode usar hardware pré-fabricado em vez de computadores especializados e caros para cada nó de armazenamento.
Pode reduzir a disputa e melhorar o desempenho ao balancear a carga de trabalho nos fragmentos.
Na cloud, os fragmentos podem residir fisicamente perto dos utilizadores que acedem aos dados.
Quando dividires um armazenamento de dados em fragmentos, decide quais os dados a colocar em cada fragmento. Cada fragmento normalmente contém itens agrupados por um ou mais atributos de dados. Estes atributos formam a chave shard, por vezes referida como chave de partição.
A fragmentação organiza fisicamente os dados. Quando uma aplicação armazena e recupera dados, a lógica de fragmentação encaminha-os para o shard apropriado. Pode implementar esta lógica no código de acesso aos dados da aplicação ou no sistema de armazenamento de dados se este suportar sharding de forma transparente.
Abstrair a localização física dos dados na lógica de fragmentação proporciona controlo sobre quais fragmentos contêm que dados. Também pode migrar dados entre shards sem modificar a lógica de negócio da aplicação quando precisa de redistribuir dados, como quando os shards ficam desbalanceados. O compromisso é a sobrecarga adicional de acesso aos dados para determinar a localização de cada item durante o processo de recuperação.
Seleção da chave do fragmento
A chave de fragmentação é a decisão de conceção mais crítica num sistema fragmentado. Para alterar uma chave de shard depois de a escolher, normalmente tens de migrar todos os dados para um novo layout de shard, o que é uma operação cara e arriscada num sistema ativo. Tome esta decisão cuidadosamente antes de escrever qualquer código.
Uma chave de shard eficaz é imutável, tem alta cardinalidade, distribui os dados e a carga de maneira uniforme, e está alinhada com os seus padrões dominantes de consulta, de modo que a maioria dos pedidos se resolve contra um único shard. Evite valores que aumentam monotonamente (números inteiros de auto incremento e timestamps sequenciais), atributos de baixa cardinalidade (booleanos e pequenas enumerações) e atributos voláteis que mudam frequentemente. Estes atributos conduzem a zonas de alta concentração ("hotspots") ou a uma transferência dispendiosa de dados entre fragmentos.
Se nenhum atributo individual cumprir estes critérios, define-se uma chave de fragmento composta combinando dois ou mais atributos. Se as consultas precisarem de recuperar dados por atributos que não fazem parte da chave do fragmento, use um padrão como o padrão Tabela de Índice para fornecer consultas secundárias.
Para mais informações sobre como escolher chaves de partição entre os serviços Azure, consulte orientação para particionamento de dados e estratégias de particionamento de dados.
Estratégias de fragmentação
Use uma das seguintes estratégias ao selecionar a chave do fragmento e decidir como distribuir os dados entre os fragmentos. Não precisas de uma correspondência um a um entre os shards e os servidores que os alojam. Um único servidor pode alojar vários shards.
Estratégia de consulta de sharding
Na estratégia de pesquisa, também chamada de estratégia baseada em diretórios, a lógica de fragmentação implementa um mapa que encaminha um pedido de dados para o fragmento que contém esses dados usando a chave do fragmento. Numa aplicação multitenant, pode armazenar todos os dados de um locatário numa partição usando o ID do locatário como chave da partição. Vários inquilinos podem partilhar o mesmo shard, mas os dados de um único inquilino ficam confinados a um único shard. O diagrama seguinte mostra a fragmentação dos dados de inquilinos com base nos IDs de inquilino.
O mapeamento entre os valores das chaves de fragmento e o armazenamento físico pode ser direto, onde cada valor da chave de fragmento corresponde a uma partição física. Uma técnica mais flexível é a partição virtual, onde os valores-chave dos fragmentos são mapeados para fragmentos virtuais, e o sistema depois mapeia esses fragmentos virtuais para menos partições físicas. Uma aplicação localiza dados usando um valor de chave de fragmento que se refere a um fragmento virtual, e o sistema mapeia transparentemente fragmentos virtuais para partições físicas. O mapeamento entre um fragmento virtual e uma partição física pode mudar sem necessidade de modificações no código da aplicação.
Estratégia de sharding baseada em faixas
A estratégia baseada em alcance agrupa itens relacionados no mesmo fragmento e ordena-os por chave sequencial do fragmento. Esta estratégia suporta aplicações que frequentemente recuperam conjuntos de itens através de consultas por intervalo. As consultas por intervalo retornam um conjunto de itens de dados para uma chave de fragmento que se enquadra num determinado intervalo.
Por exemplo, se uma aplicação precisar regularmente de encontrar todas as encomendas feitas num determinado mês, podes recuperar os dados mais rapidamente se guardares todas as encomendas de um mês na mesma shard. Se armazenar cada ordem num shard diferente, a aplicação tem de os buscar individualmente, realizando um grande número de consultas de pontos. O diagrama seguinte mostra conjuntos sequenciais, ou intervalos, de dados armazenados em fragmentos.
Neste exemplo, a chave de partição é uma chave composta que contém o mês de encomenda como o elemento mais significativo, seguido pelo dia e hora de encomenda. As novas ordens são automaticamente ordenadas à medida que são criadas e adicionadas a uma partição.
Alguns repositórios de dados suportam chaves shard de duas partes. Uma chave de partição identifica o fragmento, e uma chave de linha identifica de forma única um item dentro do fragmento. O fragmento normalmente armazena dados por ordem de chaves de linha. Para itens que precisam de consultas de intervalo e que têm de ser agrupados, pode usar uma chave de fragmento que tenha o mesmo valor para a chave de partição, mas um valor único para a chave de linha.
Estratégia de sharding baseada em hash
A estratégia baseada em hash reduz a probabilidade de hotspots, que são fragmentos que recebem uma carga desproporcional. Esta estratégia distribui os dados entre fragmentos para equilibrar o tamanho de cada fragmento e a carga média que cada fragmento encontra. A lógica de fragmentação calcula o fragmento para armazenar um item com base num hash de um ou mais atributos dos dados. A função de hash escolhida deve distribuir os dados de forma uniforme entre os shards. O diagrama seguinte mostra a partição dos dados de inquilinos com base num hash dos IDs dos inquilinos.
Para compreender a vantagem da estratégia de hash em relação a outras estratégias de sharding, considere como uma aplicação multitenant que inscreve novos locatários sequencialmente pode vir a atribuir os locatários a shards na base de dados. Quando se usa a estratégia de intervalo, os dados dos inquilinos 1 a n são armazenados no fragmento A, os dados dos inquilinos n+1 a m são armazenados no fragmento B, e os intervalos de inquilinos posteriores mapeiam para fragmentos sucessivos. Se os inquilinos mais recentemente registados também forem os mais ativos, a maior parte da atividade de dados ocorre em poucos fragmentos, o que pode causar hotspots. Em contraste, a estratégia de hash aloca os clientes a fragmentos com base num hash do seu ID de cliente. O hash normalmente distribui inquilinos sequenciais entre diferentes shards, o que equilibra a carga. O diagrama anterior mostra esta abordagem para os inquilinos 55 e 56.
Estratégia de fragmentação geográfica
A estratégia geográfica atribui dados a fragmentos com base na origem geográfica ou na região de consumo pretendida desses dados. Em muitas cargas de trabalho, os utilizadores e os dados que geram concentram-se em regiões específicas. Requisitos regulatórios, como as leis de residência de dados, podem exigir que dados específicos permaneçam dentro de uma jurisdição específica. Mesmo sem influências regulamentares, colocar os dados perto dos utilizadores que mais frequentemente acedem a eles reduz a latência da rede para leituras e escritas.
Nesta estratégia, deriva-se a chave shard a partir de um atributo geográfico, como o país/região do utilizador, a região do datacenter de origem ou um identificador de inquilino regional. Alojas cada fragmento em, ou atribuis-no a, infraestrutura dentro desse limite geográfico.
Por exemplo, uma aplicação que serve clientes na América do Norte, Europa e Asia-Pacific pode manter três grupos de shards, um grupo em cada região Azure correspondente. Uma aplicação europeia que serve apenas utilizadores europeus encaminha um pedido para o shard europeu. Esta abordagem reduz a latência e cumpre os requisitos de residência de dados.
A fragmentação geográfica introduz o risco de distribuição desigual dos dados. Se a maioria dos seus utilizadores reside numa região, o fragmento dessa região suporta uma quantidade desproporcional da carga e do armazenamento. Pode combinar o sharding geográfico com outra estratégia, como hash ou pesquisa, dentro de cada região para distribuir a carga de forma uniforme entre múltiplos shards dentro do mesmo limite geográfico.
Vantagens e considerações para cada estratégia
As quatro estratégias de fragmentação têm as seguintes vantagens e considerações:
A estratégia de pesquisa oferece mais controlo sobre a configuração do fragmento. Os shards virtuais reduzem o impacto do reequilíbrio porque é possível adicionar novas partições físicas para distribuir a carga de trabalho. Pode modificar o mapeamento entre um shard virtual e as suas partições físicas sem afetar o código da aplicação. Procurar a localização dos fragmentos adiciona sobrecarga.
A estratégia de intervalo é fácil de implementar e funciona bem com consultas de intervalo. As consultas por intervalo podem recuperar múltiplos itens de dados de um único fragmento numa única operação. A gestão de dados é mais simples. Por exemplo, pode agendar atualizações para cada fuso horário com base em padrões de carga locais quando utilizadores da mesma região partilham o mesmo shard. No entanto, esta estratégia não equilibra a carga de forma equilibrada entre os fragmentos. Reequilibrar é difícil e pode não resolver a carga desigual quando a atividade se concentra principalmente em chaves de partição adjacentes.
A estratégia de hash oferece uma melhor hipótese de distribuição uniforme dos dados e da carga. Pode encaminhar pedidos diretamente usando a função de hash sem manter um mapa. Calcular o hash acrescenta algum overhead. Reequilibrar é difícil sem um hashing consistente.
A estratégia geográfica cumpre requisitos de residência de dados e soberania que outras estratégias não abordam inerentemente. Reduz a latência de leitura e escrita quando os utilizadores acedem a dados na sua região. No entanto, o sharding geográfico pode criar um desequilíbrio significativo de dados e carga quando as populações de utilizadores não estão distribuídas de forma uniforme entre regiões. Consultas que abrangem regiões, como o relatório global, devem recolher dados de todos os fragmentos geográficos e incorrer em maior latência. Combine o sharding geográfico com outra estratégia dentro de cada região quando precisar tanto de conformidade como de distribuição de carga equilibrada.
A maioria dos sistemas de sharding implementa uma destas abordagens, mas deve também considerar os requisitos de negócio da sua aplicação e os seus padrões de utilização de dados. Por exemplo, em um aplicativo multilocatário:
Podes fragmentar dados com base na carga de trabalho. Segregar dados para locatários altamente voláteis em partições separadas para melhorar a velocidade de acesso aos dados para outros locatários.
Podes fragmentar dados com base na localização do inquilino. Coloque os dados de inquilinos numa região geográfica específica offline para backup e manutenção durante as horas de menor afluência dessa região, enquanto os dados de inquilinos noutras regiões permanecem online durante o horário comercial.
Atribuir aos clientes de alto valor os seus próprios fragmentos dedicados e com baixa carga. Inquilinos de menor valor podem partilhar fragmentos mais densamente compactados.
Armazene dados de inquilinos que necessitem de forte isolamento e privacidade em servidores separados.
Operações de escalabilidade e movimentação de dados para cada estratégia
Cada estratégia de fragmentação oferece diferentes capacidades e níveis de complexidade para gerir o escalonamento horizontal, o escalonamento vertical, a movimentação de dados e a manutenção do estado.
A estratégia de pesquisa permite operações de escalabilidade e movimentação de dados ao nível do utilizador, seja online ou offline. Para mover dados:
Suspender parte ou toda a atividade do utilizador, normalmente durante períodos fora de ponta.
Mude os dados para a nova partição virtual ou para o shard físico.
Atualiza os mapeamentos.
Invalide ou atualize quaisquer caches que contenham estes dados.
Retomar a atividade dos utilizadores.
Muitas vezes pode gerir esta operação de forma centralizada. A estratégia de pesquisa no sistema exige que o estado seja com alta capacidade de cache e amigável para réplicas.
A estratégia de alcance limita a escalabilidade e as operações de movimentação de dados porque é necessário dividir e fundir dados entre fragmentos, normalmente enquanto parte ou todo o armazenamento de dados está offline. Quando mover dados para reequilibrar fragmentos, pode não eliminar a carga desigual se a maior parte da atividade estiver concentrada em chaves de fragmento adjacentes ou identificadores de dados dentro do mesmo intervalo. A estratégia de alcance pode também exigir que o estado mapeie os intervalos para partições físicas.
A estratégia de hash complica a escalabilidade e as operações de movimentação de dados. As chaves de partição são hashes das chaves de fragmento ou identificadores de dados. Com uma função hash padrão, como
hash(key) mod N, adicionar ou remover um fragmento reatribui a maioria das chaves e desencadeia uma migração de dados em grande escala. O hashing consistente reduz este impacto ao organizar o espaço de hash de modo a que apenas uma pequena fração de chaves se mova quando a contagem de fragmentos muda. A estratégia de hash não requer a manutenção de um estado de mapeamento separado.A estratégia geográfica liga diretamente as operações de escalonamento ao fornecimento de infraestruturas regionais. Aumentar a capacidade numa região não alivia a carga noutra região. Requisitos regulamentares que exigem fragmentação geográfica também podem restringir a circulação de dados através de fronteiras geográficas. Dentro de cada região, a escalabilidade utiliza a estratégia secundária que distribui os dados entre os fragmentos dessa região.
Problemas e considerações
Considere os seguintes pontos ao decidir como implementar este padrão:
Use o sharding complementarmente a outras formas de particionamento, como a partição vertical e a partição funcional. Por exemplo, um único shard pode conter entidades particionadas verticalmente, e podes implementar uma partição funcional como múltiplos shards. Para mais informações, consulte Particionamento de dados horizontal, vertical e funcional.
Mantém os fragmentos equilibrados para que todos possam lidar com um volume semelhante de entrada/saída (I/O). O desequilíbrio de dados acumula-se com o tempo quando os registos são inseridos e eliminados, o que leva a hotspots. Planeie reequilibrar periodicamente.
O reequilíbrio move dados entre fragmentos e frequentemente causa indisponibilidade ou redução de rendimento. Para reequilibrar menos vezes, use partições virtuais. Mapeia muitas partições lógicas para menos fragmentos físicos. Quando um shard está sobrecarregado, redistribua as suas partições virtuais para novos shards físicos sem repetir todo o conjunto de dados. O Azure Cosmos DB utiliza esta abordagem para desacoplar o esquema de partição da infraestrutura física.
Prefiro muitos fragmentos pequenos a alguns grandes. Fragmentos mais pequenos migram mais rapidamente, equilibram a carga de forma mais uniforme e oferecem mais flexibilidade para a redistribuição de dados.
Utilize dados estáveis para a chave de fragmento. Se a chave de fragmento mudar, pode ser necessário mover o elemento de dados correspondente entre fragmentos, o que aumenta a sobrecarga das operações de atualização. Evita basear a chave de fragmento em informação potencialmente volátil. Escolha atributos que sejam invariantes ou que formem naturalmente uma chave.
Certifique-se de que as chaves de fragmentação são exclusivas. Por exemplo, evite utilizar campos que incrementam automaticamente como a chave de partição. Em alguns sistemas, campos autoincrementados não conseguem coordenar-se entre fragmentos, o que pode resultar em itens em fragmentos diferentes terem a mesma chave de fragmento.
Observação
Valores autoincrementados noutros campos que não são chaves de fragmento também podem causar problemas. Por exemplo, se usares campos autoincrementados para gerar IDs únicos, dois itens diferentes em shards diferentes podem receber o mesmo ID.
Fragmente os dados para suportar as consultas mais frequentemente realizadas. Pode ser que não seja possível desenhar uma chave de shard que corresponda aos requisitos de todas as consultas nos dados. Se necessário, crie tabelas de índice secundárias para suportar consultas que recuperam dados por atributos que não fazem parte da chave do fragmento. Para mais informações, consulte o padrão da Tabela de Índices.
Desenhe a chave do shard e o modelo de dados de forma a manter a maioria das operações limitadas a um único shard. Consultas que acedem apenas a um único fragmento são mais eficientes do que consultas que recuperam dados de múltiplos fragmentos. Desnormalize os seus dados para manter as entidades relacionadas, que são frequentemente consultadas em conjunto, como clientes e as suas encomendas, no mesmo shard de forma a reduzir o número de operações de leitura distintas.
As consultas cross-shard acrescentam latência, consumo de recursos e complexidade. Quando uma aplicação precisa de recuperar dados de múltiplos fragmentos, utilize consultas de difusão paralela executadas em cada fragmento em simultâneo e agregue os resultados. Mesmo com paralelismo, o fragmento mais lento determina a latência global.
Sugestão
Se uma entidade num shard referenciar uma entidade noutro shard, inclua a chave do shard para a segunda entidade como parte do esquema para a primeira entidade. Esta abordagem pode melhorar o desempenho de consultas que referenciam dados relacionados distribuídos por partições.
Reconsidere a sua chave de shard ou se o sharding se adequa às suas necessidades se a sua carga de trabalho exige uma forte integridade transacional através dos limites dos shards. As transações entre shards apresentam desafios. Protocolos de coordenação distribuída, como o commit bifásico, adicionam latência, introduzem modos de falha e reduzem a eficiência. A maioria dos sistemas com fragmentos evita transações distribuídas e adota a consistência eventual. Neste modelo, cada fragmento atualiza-se de forma independente e a aplicação lida com inconsistências temporárias.
Certifique-se de que os recursos disponíveis para cada nó de armazenamento de fragmentos conseguem lidar com os requisitos de escalabilidade em termos de tamanho e taxa de transferência de dados. Para obter mais informações, consulte Estratégias de particionamento de dados.
Considere replicar os dados de referência para todos os fragmentos. Se uma consulta contra um shard também faz referência a dados estáticos ou de movimento lento, adicione esses dados ao shard. A aplicação pode então obter todos os dados da consulta sem ter de fazer uma viagem de ida e volta para um armazenamento de dados separado.
Observação
Se os dados de referência armazenados em múltiplos fragmentos mudarem, o sistema deve sincronizar essas alterações entre todos os fragmentos. Pode ocorrer algum grau de inconsistência durante a execução desta sincronização. Desenhe as suas aplicações para tolerar esta inconsistência.
Sistemas fragmentados multiplicam a carga operacional. Planeie para estas preocupações:
Monitorização: Tens de agregar métricas e registos de todos os shards para obter uma visão completa da saúde do sistema.
Backup e restauração: Deves fazer backup de cada fragmento de forma independente e desenhar procedimentos de restauração para manter a consistência entre fragmentos. Um restauro pontual de um fragmento pode criar inconsistências com outros fragmentos.
Alterações no esquema: Deve coordenar as alterações da Linguagem de Definição de Dados (DDL) em cada fragmento.
Pode implementar estas tarefas usando scripts ou outras soluções de automação.
Podes geolocalizar fragmentos para colocar os seus dados perto das instâncias de aplicação que os utilizam. Esta abordagem pode melhorar o desempenho, mas requer planeamento extra para operações que têm de aceder a múltiplos fragmentos em diferentes locais.
Quando utilizar este padrão
Sugestão
Antes de desenhar uma camada de sharding personalizada, determine quais as responsabilidades de sharding que a sua plataforma de dados já gere. Alguns serviços gerem completamente o sharding. Por exemplo, o Azure Cosmos DB distribui dados entre partições físicas, gere divisões e encaminha consultas sem envolvimento da aplicação. Outros serviços gerem sharding de forma parcial. Por exemplo, o Azure SQL Database fornece ferramentas elásticas de base de dados para gestão de mapas de shard e roteamento dependente de dados, mas você desenha a chave de shard e gere as operações de divisão. Usa o padrão de fragmentação quando construíres e operares a lógica de fragmentação tu próprio.
Utilize este padrão quando:
O volume total de dados excede a capacidade de armazenamento de uma única instância de base de dados, e nenhuma opção de escalonamento vertical resolve a deficiência.
A taxa de transações ou a concorrência de consultas excede o que uma única instância pode suportar, e as réplicas de leitura sozinhas não resolvem o gargalo porque a carga das operações de escrita também é elevada.
Observação
O sharding melhora o desempenho e a escalabilidade de um sistema, e também pode melhorar a disponibilidade. Uma falha numa partição não impede necessariamente uma aplicação de aceder a dados noutras partições. E um operador pode realizar manutenção ou recuperação de uma partição sem tornar todos os dados indisponíveis. Para obter mais informações, consulte Diretrizes de particionamento de dados.
Requisitos regulatórios ou de conformidade exigem que subconjuntos de dados específicos residam em jurisdições geográficas específicas, e nenhuma implementação numa única região pode cumprir todos os requisitos.
Inquilinos distintos ou segmentos de clientes exigem isolamento físico de dados por razões de segurança, desempenho ou contratuais.
Em cenários como estes, o padrão de fragmentação é por vezes aplicado para além dos repositórios de dados tradicionais. Por exemplo, um sistema de gestão de zonas DNS pode ser fragmentado por equipa, ambiente ou região para reduzir o raio de impacto das alterações DNS e estabelecer limites claros de propriedade. Nesse contexto, a principal motivação é a segmentação operacional em vez da escalabilidade. Para mais informações, consulte Sharding de zonas DNS privadas.
O sharding introduz uma complexidade substancial e permanente na sua arquitetura de dados. Essa complexidade afeta o desenvolvimento, operações, testes, design de consultas e recuperação de falhas ao longo da vida útil do sistema.
Este padrão pode não ser adequado quando:
O volume e a taxa de transferência de dados cabem numa única instância de base de dados, mesmo com o crescimento projetado. A escalabilidade vertical preserva a simplicidade das consultas e a integridade das transações.
O teu gargalo é o volume de leitura, não o volume de escrita ou a capacidade de armazenamento. Réplicas de leitura e camadas de cache podem descarregar o tráfego de leitura sem a complexidade de consultas entre fragmentos que a fragmentação introduz.
O seu motor de base de dados suporta particionamento ao nível de tabela que satisfaz as suas necessidades de desempenho. Particionar dentro de uma única instância não requer múltiplos servidores ou lógica de encaminhamento.
Os seus padrões de consulta dominantes exigem juntas entre entidades, transações multientidades ou agregações de conjunto de dados completos. O sharding torna estas operações dispendiosas, e o sobrecusto das consultas fan-out e da coordenação distribuída pode superar os benefícios de escalabilidade.
Design da carga de trabalho
Avalie como usar o padrão de Sharding no design de uma carga de trabalho para abordar os objetivos e princípios abordados nos pilares do Azure Well-Architected Framework. A tabela a seguir fornece orientação sobre como esse padrão suporta as metas de cada pilar.
| Pilar | Como esse padrão suporta os objetivos do pilar |
|---|---|
| As decisões de projeto de confiabilidade ajudam sua carga de trabalho a se tornar resiliente ao mau funcionamento e garantem que ela se recupere para um estado totalmente funcional após a ocorrência de uma falha. | Os dados e o processamento estão isolados ao fragmento, pelo que uma avaria num fragmento permanece isolado nesse fragmento. - Particionamento de dados - RE:07 Autopreservação |
| A Otimização de Custos foca-se em manter e melhorar o retorno do investimento da sua carga de trabalho. | Um sistema que implementa fragmentos geralmente se beneficia do uso de várias instâncias de recursos de computação ou armazenamento mais baratos, em vez de um único recurso mais caro. Em muitos casos, esta configuração pode poupar-lhe dinheiro. - CO:07 Custos dos componentes |
| A Eficiência de Desempenho ajuda sua carga de trabalho a atender às demandas de forma eficiente por meio de otimizações em escala, dados e código. | Quando usas sharding na tua estratégia de escalabilidade, os dados e o processamento ficam isolados a cada shard, por isso os pedidos só competem por recursos dentro do shard atribuído. Você também pode usar a fragmentação para otimizar com base na geografia. - PE:05 Dimensionamento e particionamento - PE:08 Desempenho de dados |
Se este padrão introduzir compensações dentro de um pilar, considere-as em relação aos objetivos dos outros pilares.
Exemplo
Considere um site que apresenta uma vasta coleção de informações sobre livros publicados em todo o mundo. O número de livros possíveis catalogados nesta carga de trabalho e os padrões típicos de consulta e utilização excedem o que uma única base de dados relacional consegue gerir. O arquiteto da carga de trabalho decide fragmentar os dados entre múltiplas instâncias de base de dados usando o ISBN estático dos livros como chave de fragmentação. Especificamente, o arquiteto utiliza o dígito de verificação (0 - 10) do ISBN, que fornece 11 possíveis fragmentos lógicos com uma distribuição de dados bastante equilibrada.
Para começar, o arquiteto coloca os 11 fragmentos lógicos nos três bancos de dados físicos. Nesta abordagem de partição virtual, muitas partições lógicas correspondem a menos nós físicos. O arquiteto utiliza a abordagem de lookup sharding e armazena o mapeamento de chave para servidor numa base de dados de mapas de shards.
O Azure App Service está rotulado como site de catálogo de livros. Liga-se a múltiplas instâncias de base de dados SQL e a uma instância Azure AI Search. Uma das bases de dados é designada como base de dados ShardMap. Inclui uma tabela de exemplo que espelha uma parte da tabela de mapeamento, que é listada mais adiante neste artigo. A tabela inclui três instâncias de bases de dados de fragmentos: bookdbshard0, bookdbshard1 e bookdbshard2. As outras bases de dados incluem listagens idênticas de tabelas dentro delas. As Tabelas incluem Livros, Catálogo da Biblioteca do Congresso e também um indicador de mais tabelas. A Pesquisa por IA é usada para navegação facetada e pesquisa em sites. A identidade gerida está associada ao Serviço de Aplicações.
Mapa de fragmentos de consulta
O banco de dados de mapa de estilhaços contém a seguinte tabela e dados de mapeamento de estilhaços.
SELECT ShardKey, DatabaseServer
FROM BookDataShardMap
| ShardKey | DatabaseServer |
|----------|----------------|
| 0 | bookdbshard0 |
| 1 | bookdbshard0 |
| 2 | bookdbshard0 |
| 3 | bookdbshard1 |
| 4 | bookdbshard1 |
| 5 | bookdbshard1 |
| 6 | bookdbshard2 |
| 7 | bookdbshard2 |
| 8 | bookdbshard2 |
| 9 | bookdbshard0 |
| 10 | bookdbshard1 |
Exemplo de código de site: acesso a um único fragmento
O site não tem conhecimento de quantas bases de dados físicas de shard existem (três neste caso) ou da lógica que mapeia uma chave de shard para uma instância de base de dados. Só sabe que o dígito de verificação do ISBN de um livro é a chave de fragmento. O site tem acesso somente leitura ao banco de dados de mapa de fragmentos e acesso de leitura/escrita a todos os bancos de dados de fragmentos. Neste exemplo, o site utiliza a identidade gerida pelo sistema do seu host do Azure App Service para autorização, o que mantém segredos fora das strings de conexão.
O site está configurado com as seguintes cadeias de ligação, quer num appsettings.json ficheiro, como mostrado neste exemplo, quer através das definições da aplicação App Service.
{
...
"ConnectionStrings": {
"ShardMapDb": "Data Source=tcp:<database-server-name>.database.windows.net,1433;Initial Catalog=ShardMap;Authentication=Active Directory Default;App=Book Site v1.5a",
"BookDbFragment": "Data Source=tcp:SHARD.database.windows.net,1433;Initial Catalog=Books;Authentication=Active Directory Default;App=Book Site v1.5a"
},
...
}
O código seguinte mostra como o website executa uma consulta de atualização no pool de shards da base de dados da carga de trabalho.
...
// All data for this book is stored in a shard based on the book's ISBN check digit,
// which is converted to an integer 0 - 10 (special value 'X' becomes 10).
int isbnCheckDigit = book.Isbn.CheckDigitAsInt;
// Establish a pooled connection to the database shard for this specific book.
using (SqlConnection sqlConn = await shardedDatabaseConnections.OpenShardConnectionForKeyAsync(key: isbnCheckDigit, cancellationToken))
{
// Update the book's Library of Congress catalog information.
SqlCommand cmd = sqlConn.CreateCommand();
cmd.CommandText = @"UPDATE LibraryOfCongressCatalog
SET ControlNumber = @lccn,
...
Classification = @lcc
WHERE BookID = @bookId";
cmd.Parameters.AddWithValue("@lccn", book.LibraryOfCongress.Lccn);
...
cmd.Parameters.AddWithValue("@lcc", book.LibraryOfCongress.Lcc);
cmd.Parameters.AddWithValue("@bookId", book.Id);
await cmd.ExecuteNonQueryAsync(cancellationToken);
}
...
No código de exemplo anterior, se book.Isbn era 978-8-1130-1024-6, então isbnCheckDigit deveria ser 6. A OpenShardConnectionForKeyAsync(6) chamada é normalmente implementada através de uma abordagem cache-aside. Se a informação em cache do shard key 6 não estiver disponível, o método consulta a base de dados do shard map identificada pela ShardMapDb string de ligação. O método recupera o valor bookdbshard2 a partir da cache da aplicação ou da base de dados do shard e substitui-o por SHARD na string de ligação em BookDbFragment. O método então estabelece ou restabelece uma ligação em pool para bookdbshard2.database.windows.net, abre-a e devolve-a ao código de chamada. Em seguida, o código atualiza o registro existente nessa instância de banco de dados.
Exemplo de código de website: acesso múltiplo a partições
Na rara eventualidade em que o site requer uma consulta direta entre segmentos, a aplicação realiza uma consulta paralela de fan-out em todos os segmentos.
...
// Retrieve all shard keys.
var shardKeys = shardedDatabaseConnections.GetAllShardKeys();
// Run the query in a fan-out style against each shard in the shard list.
Parallel.ForEachAsync(shardKeys, async (shardKey, cancellationToken) =>
{
using (SqlConnection sqlConn = await shardedDatabaseConnections.OpenShardConnectionForKeyAsync(key: shardKey, cancellationToken))
{
SqlCommand cmd = sqlConn.CreateCommand();
cmd.CommandText = @"SELECT ...
FROM ...
WHERE ...";
SqlDataReader reader = await cmd.ExecuteReaderAsync(cancellationToken);
while (await reader.ReadAsync(cancellationToken))
{
// Collect the results into a thread-safe data structure.
}
reader.Close();
}
});
...
Como alternativa às consultas cross-shard, esta carga de trabalho pode usar um índice mantido externamente no Azure AI Search para pesquisa de sites ou navegação facetada.
Adicionar instâncias de partição
A equipa de carga de trabalho sabe que, se o catálogo de dados ou a sua utilização simultânea crescerem significativamente, podem ser necessárias mais de três instâncias de base de dados. A equipa de carga de trabalho não espera adicionar servidores de base de dados dinamicamente e aceita o tempo de inatividade da carga de trabalho quando um novo shard entra online. Para colocar online uma nova instância de shard, é necessário mover dados dos shards existentes para o novo shard e atualizar a tabela de mapas de shards. Com esta abordagem relativamente estática, a carga de trabalho pode armazenar em cache com confiança o mapeamento da base de dados da chave de shard no código do site.
A lógica da chave de fragmento neste exemplo tem um limite superior de 11 fragmentos físicos. Se a equipa de carga de trabalho determinar, através da estimativa de carga, que eventualmente necessita de mais de 11 instâncias de base de dados, terá de fazer uma alteração invasiva à lógica da chave de shard. Esta alteração envolve um planeamento cuidadoso das modificações do código e a migração de dados para a nova lógica de chaves.
Funcionalidade SDK
Em vez de escrever código personalizado para gestão de shards e encaminhamento de consultas para instâncias de base de dados SQL, avalie a biblioteca cliente da base de dados elástica. Esta biblioteca suporta gerenciamento de mapas de estilhaços, roteamento de consultas dependentes de dados e consultas entre estilhaços em C# e Java.
Próximo passo
- Níveis de consistência no Azure Cosmos DB: Distribuir dados entre shards introduz compromissos em termos de consistência. Este artigo descreve o espectro dos modelos de consistência, do forte ao eventual, e os seus efeitos na disponibilidade e latência.
Recursos relacionados
- Particionamento horizontal, vertical e funcional de dados: Este artigo descreve outras estratégias para particionar dados na cloud para melhorar a escalabilidade, reduzir a contenção e otimizar o desempenho.
- Padrão de Tabela de Índice: Por vezes, não é possível suportar todas as consultas apenas com o design da chave shard. Uma aplicação pode usar o padrão Tabela de Índice para recuperar dados de um grande armazenamento de dados, especificando uma chave diferente da chave shard.
- Padrão de Visualização Materializada: Para manter o desempenho de algumas operações de consulta, pode criar vistas materializadas que agregam e resumem dados, especialmente se distribuir esses dados entre fragmentos.