Práticas recomendadas para o design de serviços em grande escala em Serviços de Nuvem do Azure

Atualizado: outubro de 2014

Autores: Mark Simms e Michael Thomassy

Autores de contribuição: Jason Roth e Ralph Squillace

Revisores: Brad Calder, Dennis Mulder, Mark Ozur, Nina Sarawgi, Marc Mercuri, Conor Cunningham, Peter Carlin, Stuart Ozer, Lara Rubbelke e Nicholas Dritsas.

Computação em nuvem é computação distribuída. A computação distribuída exige planejamento e fornecimento ponderados, independentemente da plataforma escolhida. A finalidade deste documento é fornecer orientação com base nos cenários reais do cliente para criação de aplicativos escalonáveis no Azure e Banco de dados SQL, aproveitando a abordagem PaaS (plataforma como serviço) (como aplicativos que são criados como Serviços de Nuvem do Azure usando as funções Web e de trabalho).

ImportantImportante
OBSERVAÇÃO: todas as orientações de práticas recomendadas neste documento foram extraídas do estreito relacionamento com clientes que executam código de produção no Azure. Este documento aborda a plataforma Serviços de Nuvem do Azure (PaaS) com base na versão v1.6 SDK. Ele não aborda recursos como Sites do Azure ou Máquinas Virtuais do Azure (IaaS).

Este documento abrange os conceitos de design subjacentes para criação de aplicativos do Azure, os principais recursos da plataforma Azure, os limites e recursos, bem como as práticas recomendadas para trabalhar com os principais serviços do Azure. O foco é nesses aplicativos receptivos a um repositório de dados imprecisamente consistente (em oposição aos modelos de dados multilocatário de alta densidade ou rigorosamente consistentes).

Os aspectos inconstantes dos seus aplicativos e serviços no Azure podem ser atraentes por muitos motivos. Por exemplo:

  • Para conservar ou consolidar despesas de capital em despesas operacionais (capex no opex).

  • Para reduzir custos (e melhorar a eficiência) ao corresponder mais estritamente à demanda e a capacidade.

  • Para aumentar a agilidade e diminuir o tempo de colocação no mercado ao reduzir ou remover as barreiras da infraestrutura.

  • Para estender o alcance do público-alvo a novos mercados, como dispositivos móveis.

  • Para se beneficiar da escala em massa da computação em nuvem criando novos aplicativos que ofereçam suporte a um público-alvo global nos data centers distribuídos geograficamente.

Há muitos motivos técnicos excelentes para o desenvolvimento de novos aplicativos, ou para a sustentação de alguns ou todos os aplicativos existentes no Azure. Como o ambiente é rico em alternativas de implementação, é preciso avaliar cuidadosamente o padrão específico do seu aplicativo para selecionar a abordagem de implementação correta. Alguns aplicativos são uma boa opção para os Serviços de Nuvem do Azure (que é uma plataforma como serviço, ou abordagem PaaS), enquanto outros podem se beneficiar de uma abordagem de infraestrutura como serviço (ou IaaS) parcial ou completa, como as Máquinas Virtuais do Azure. Por fim, determinados requisitos de aplicativo podem ser mais bem atendidos quando se usa as duas abordagens juntas.

Seu aplicativo deve ter um ou mais dos três principais aspectos a seguir para maximizar os benefícios dos Serviços de Nuvem do Azure. (Nem todas estas necessidades precisam ser apresentadas no seu aplicativo; um aplicativo pode gerar um grande retorno sobre o investimento fazendo um excelente uso do Azure com apenas um dos aspectos a seguir. No entanto, uma carga de trabalho que não exiba nenhuma destas características, provavelmente, não será a opção ideal para os Serviços de Nuvem do Azure.)

Os aspectos importantes que devem ser avaliados em um aplicativo são:

  • Demanda elástica. Uma das principais propostas de valor da transferência para o Azure é a escala elástica: a capacidade de adicionar ou remover recursos do aplicativo (expansão ou redução) para corresponder mais estritamente à demanda dinâmica do usuário. Se a carga de trabalho tiver uma demanda estática e fixa (por exemplo, um número estático de usuários, transações e assim por diante), essa vantagem dos Serviços de Nuvem do Azure não será maximizada.

  • Usuários e dispositivos distribuídos. A execução no Azure proporciona acesso instantâneo à implantação global de aplicativos. Se sua carga de trabalho tiver uma base de usuários cativos em execução em um único local (como em um único escritório), a implantação na nuvem pode não fornecer um retorno sobre o investimento ideal.

  • Carga de trabalho particionável. Os aplicativos em nuvem são dimensionados pela expansão e, com isso, pela adição de mais capacidade em partes de dados menores. Se o aplicativo depender de expansão vertical (por exemplo, grandes bancos de dados e data warehouses) ou for uma carga de trabalho especializada e dedicada (por exemplo, armazenamento amplo, unificado e altamente veloz), ele deverá ser decomposto (particionado) para ser executado em serviços expandidos para que seja praticável na nuvem. Dependendo da carga de trabalho, esse pode ser um exercício incomum.

Para reiterar: ao avaliar seu aplicativo, você pode conseguir um alto retorno sobre o investimento ao transferi-lo ou criá-lo nos Serviços de Nuvem do Azure se sua carga de trabalho tiver apenas um dos três aspectos anteriores que se destaque em um ambiente de plataforma como serviço, como os Serviços de Nuvem do Azure. Os aplicativos que tiverem as três características, provavelmente, proporcionarão um excelente retorno sobre o investimento.

Embora muitos aspectos da criação de aplicativos para o Azure sejam bastante conhecidos no desenvolvimento local, há várias diferenças importantes no comportamento dos serviços e da plataforma subjacentes. Entender essas diferenças e, consequentemente, como é feito o design para a plataforma (e não ir de encontro a ela), é essencial para o fornecimento de aplicativos que cumprem a promessa de uma escala elástica na nuvem.

Esta seção descreve os cinco conceitos principais que são os pontos essenciais na criação de aplicativos expandidos amplamente distribuídos e em grande escala para ambientes PaaS, como os Serviços de Nuvem do Azure. Entender esses conceitos ajudará você a projetar e criar aplicativos que, além de funcionarem nos Serviços de Nuvem do Azure, prosperam nesse ambiente, retornando o máximo possível de benefícios em resposta ao seu investimento. Todas as considerações e opções de design que serão abordadas mais adiante neste documento serão associadas a um desses cinco conceitos.

Além disso, vale a pena observar que embora muitas dessas considerações e práticas recomendadas sejam vistas através das lentes de um aplicativo .NET, os conceitos e abordagens subjacentes são totalmente independentes de linguagem ou plataforma.

A principal mudança na transferência de um aplicativo local para os Serviços de Nuvem do Azure está relacionada a como os aplicativos são dimensionados. O método tradicional de criar aplicativos maiores se baseia em uma combinação de expansão horizontal (servidores de aplicativos e Web sem estado) e vertical (comprar um sistema maior com vários núcleos/de memória ampla, um servidor de banco de dados, criar um data center maior, etc.). Na nuvem, a expansão vertical não é uma opção realista; o único caminho para atingir esses aplicativos realmente escalonáveis é pelo design explícito para expansão horizontal.

Como muitos dos elementos de um aplicativo local já são preparados para a expansão horizontal (servidores Web, servidores de aplicativos), o desafio é identificar os aspectos do aplicativo com uma dependência de um serviço de expansão vertical e convertê-los (ou mapeá-los) para uma implementação de expansão horizontal. Geralmente, o principal candidato a uma dependência de expansão vertical é o banco de dados relacional (SQL Server/Banco de dados SQL do Azure).

Os designs relacionais tradicionais se concentravam em um modelo de dados globalmente coerente (expansão vertical de um único servidor) com consistência e comportamento transacional rígidos. A abordagem tradicional de fornecer escala em relação a esse repositório de backup tem sido "tornar tudo sem estado", postergando a responsabilidade de gerenciar o estado à expansão vertical do SQL Server.

No entanto, a incrível escalabilidade do SQL Server implica uma ausência distinta da escala verdadeiramente elástica. Isto é, em vez de disponibilidade de recursos extremamente reativos, é preciso comprar um servidor maior com uma fase dispendiosa de migração, com capacidade de exceder consideravelmente a demanda e que, cada vez que você expandir, tenha que incrementar. Além disso, há uma curva de custo exponencial ao dimensionar o hardware intermediário antigo.

Com a expansão da base arquitetônica dos Serviços de Nuvem do Azure, os aplicativos precisam ser criados para trabalhar com a expansão do repositório de dados. Isso significa desafios de design, como particionar dados explicitamente em partes de dados menores (cada um deles pode se ajustar em uma partição de dados ou unidade expandida) e gerenciar a consistência entre elementos de dados distribuídos. Isso atinge a escala por meio do particionamento de uma maneira que evita muitas das desvantagens do design para expansão vertical.

A dinamização de técnicas de expansão vertical conhecidas em direção ao gerenciamento de estado e dados de expansão horizontal, em geral, é uma das maiores dificuldades no design para a nuvem; vencer esses desafios e criar aplicativos que possam aproveitar os recursos elásticos e escalonáveis dos Serviços de Nuvem e do Banco de dados SQL do Azure para gerenciamento de dados duráveis é o foco de grande parte deste documento.

Em um mundo em que você executa seu próprio data center, você tem um grau quase infinito de controle, justaposto por um número quase infinito de opções. Tudo, desde a instalação física (ar condicionado, fornecimento de energia elétrica, espaço físico), a infraestrutura (racks, servidores, armazenamento, rede, etc.), até a configuração (topologia de roteamento, instalação de sistema operacional) está sob seu controle.

Esse grau de controle traz um custo - capital, operacional, de mão-de-obra e de tempo. O custo de gerenciar todos os detalhes em um ambiente ágil e em constante mudança é o centro do rumo à virtualização e um importante aspecto da marcha para a nuvem. Em troca de abrir mão de uma medida de controle, essas plataformas reduzem o custo da implantação, do gerenciamento e aumentam a agilidade. A condição que elas impõem é que o tamanho (capacidade, taxa de transferência, etc.) dos componentes e serviços disponíveis seja restrito a um conjunto fixo de ofertas.

Para usar uma analogia, o envio comercial em massa, basicamente, é fundamentado nos contêineres de envio. Esses contêineres podem ser carregados por vários meios de transporte (navios, trens e caminhões) e são disponibilizados em vários tamanhos padrão (até 16 metros de comprimento). Se a quantidade da carga que deseja enviar exceder a capacidade do maior trailer, você precisará optar por uma destas opções:

  • Usar vários trailers. Isso envolve divisão (ou particionamento) da carga para que ela caiba em diferentes contêineres e coordenação da entrega dos trailers.

  • Usar um método de envio especial. Para a carga que não pode ser distribuída em contêineres padrão (muito grande, volumosa, etc.), métodos altamente especializados, como barcas, precisarão ser usados. Normalmente, esses métodos são muito mais caros que o envio de carga padrão.

Trazendo essa analogia para o Azure (e para a computação em nuvem de forma geral), cada recurso de um limite. Seja uma instância de função individual, uma conta de armazenamento, um serviço de nuvem ou, até mesmo, um data center - cada recurso disponível no Azure tem um limite finito. Esses limites podem ser bem grandes, como a quantidade de armazenamento disponível em um data center (como os maiores navios de carga que podem transportar mais de 10.000 contêineres), mas são finitos.

Com isso em mente, a abordagem para dimensionar é: particionar a carga e compô-la em várias unidades de escala – sejam elas várias VMs, bancos de dados, contas de armazenamento, serviços de nuvem ou data centers.

Neste documento, usamos o termo unidade de escala para um grupo de recursos que podem (a) lidar com um grau definido de carga e (b) ser compostos em conjunto para lidar com a carga adicional. Por exemplo, uma Conta de Armazenamento do Azure tem um tamanho máximo de 100 TB. Se você precisar armazenar mais de 100 TB de dados, será preciso usar várias contas de armazenamento (isto é, pelo menos duas unidade de escala do armazenamento).

As diretrizes gerais de design para a capacidade de cada serviço principal ou componente do Azure são abordadas em seções posteriores, juntamente com abordagens recomendadas para compor esses serviços para a escala adicional.

Grandes quantidades de tempo, energia e capital intelectual foram investidas na criação de aplicativos locais altamente resilientes. Normalmente, isso se resume em separar o aplicativo em componentes de estado baixo (servidores de aplicativos, rede) e de estado alto (bancos de dados, redes SAN), bem como tornar cada um deles resiliente contra modos de falha.

Nesse contexto, um modo de falha se refere a uma combinação de (a) observar o sistema em um estado de falha, (b) como resultado da causa da falha. Por exemplo, um banco de dados que está inacessível devido a uma atualização de senha configurada incorretamente é um modo de falha; o estado de falha é a incapacidade de se conectar (conexão recusada, credenciais não aceitas) e a causa da falha é uma atualização de senha que não foi adequadamente comunicada ao código do aplicativo.

Os componentes de estado baixo fornecem resiliência por meio da redundância livremente acoplada, com sua "integração" no sistema gerenciado pelos sistemas externos. Por exemplo, colocar servidores Web adicionais atrás de um balanceador de carga; os servidores Web são idênticos (tornando a adição de nova capacidade uma questão de clonar uma imagem do servidor Web base), com integração no aplicativo geral gerenciado pelo balanceador de carga.

Os componentes de estado alto fornecem resiliência por meio da redundância rigidamente acoplada, com a "integração" rigidamente gerenciada entre os componentes. São exemplos:

  • SQL Server. A adição de uma instância redundante do SQL Server como parte de um cluster ativo/passivo requer seleção cuidadosa de hardware compatível (isto é, idêntico) e armazenamento compartilhado (como uma rede SAN) para fornecer failover consistente e transacional entre vários nós.

  • Fonte de energia. O fornecimento de energia redundante apresenta um exemplo bastante sofisticado, exigindo que vários sistemas ajam em acordo para reduzir problemas locais (várias fontes de alimentação para um servidor, com hardware integrado para alternar entre fontes primárias e secundárias) e em todo o centro (geradores para o caso de queda de energia).

As soluções de resiliência com base em abordagens rigidamente acopladas são inerentemente mais caras do que as abordagens livremente acopladas do tipo "adicionar mais itens clonados", exigindo pessoal altamente treinado, hardware especializado, com processos cuidadosos de configuração e teste. Além de ser difícil fazer tudo certo, também custa dinheiro fazer corretamente.

Essa abordagem de se concentrar em garantir que as plataformas de hardware sejam altamente resilientes pode ser pensada como uma "casca de ovo de titânio". Para proteger o conteúdo do ovo, cobrimos a casca com uma camada de titânio rígido (e caro).

A experiência de executar sistemas na escala (consulte http://www.mvdirona.com/jrh/TalksAndPapers/JamesRH_Lisa.pdf para discussão posterior) demonstrou que em qualquer sistema suficientemente amplo (como sistemas de dados na escala do Azure), o número absoluto de partes móveis físicas faz com que algumas partes do sistema sempre sejam desfeitas. A plataforma Azure foi desenvolvida para trabalhar com essa limitação, e não contra ela, dependendo da recuperação automática dos eventos de falha no nível de nó. Essa intenção de design flui por todos os serviços principais do Azure, sendo essencial para o design de um aplicativo que trabalha com o modelo de disponibilidade do Azure.

Mudar para o Azure altera o tema da resiliência de um desafio de redundância na infraestrutura para um desafio de redundância nos serviços. Muitos dos principais serviços que dominam o planejamento local da disponibilidade "apenas continuam funcionando" no Azure:

  • O Banco de dados SQL mantém automaticamente várias réplicas transacionalmente consistentes dos seus dados. As falhas no nível de nó de um banco de dados fazem failover automaticamente no banco de dados secundário consistente; compare a facilidade dessa experiência com o tempo e as despesas exigidos para fornecer resiliência local.

  • O Armazenamento do Azure mantém automaticamente várias cópias consistentes dos seus dados (para leitura complementar, consulte http://sigops.org/sosp/sosp11/current/2011-Cascais/11-calder-online.pdf). As falhas no nível de nó de um volume de armazenamento fazem failover automaticamente em um armazenamento secundário consistente. Da mesma forma que foi feito com o Banco de dados SQL, compare essa experiência totalmente gerenciada com o gerenciamento direto do armazenamento resiliente em uma rede SAN ou um cluster local.

No entanto, o título desta seção é disponibilidade, e não resiliência. A resiliência é apenas uma parte da história toda sobre fornecer valor continuamente aos usuários dentro dos limites de um SLA. Se todos os componentes da infraestrutura de um serviço forem íntegros, mas o serviço não puder lidar com o volume de usuários esperado, ele não estará disponível, nem fornecerá valor.

As cargas de trabalho centradas no dispositivo móvel ou na rede social (como aplicativos Web públicos com aplicativos de dispositivo móvel) tendem a ser bem mais dinâmicas do que aquelas que se destinam a um público-alvo cativo, além de exigirem uma abordagem mais sofisticada para lidar com eventos de intermitência e picos de carga. Os principais conceitos a serem lembrados para design da disponibilidade nos aplicativos do Azure são abordados detalhadamente em todo este documento, com base nestes pontos principais:

  • Cada serviço ou componente do Azure fornece um determinado SLA (Contrato de Nível de Serviço); esse SLA pode não estar diretamente correlacionado à métrica de disponibilidade exigida para execução do seu aplicativo. Entender todos os componentes do seu sistema, o respectivo SLA de disponibilidade e como eles interagem é essencial para compreender a disponibilidade geral que pode ser fornecida aos usuários.

    • Evite pontos únicos de falha que prejudiquem seu SLA, como funções de única instância.

    • Componha ou faça fallback para vários componentes, de modo a reduzir o impacto de um serviço específico que esteja offline ou indisponível.

  • Cada serviço ou componente no Azure pode apresentar falha, ou um transitório de vida curta ou um evento de vida longa. Seu aplicativo deve ser escrito para que possa lidar normalmente com as falhas.

    • Para falhas transitórias, forneça mecanismos de tentativa apropriados para reconexão ou reenvio do trabalho.

    • Para outros eventos de falha, forneça instrumentação detalhada sobre o evento de falha (relatando o erro em operações) e uma mensagem de erro adequada de volta ao usuário.

    • Sempre que possível, faça fallback para outro serviço ou fluxo de trabalho. Por exemplo, se houver falha em uma solicitação para inserir dados no Banco de dados SQL (por qualquer motivo não transitório, como um esquema inválido), grave os dados no armazenamento de blob em um formato serializado. Isso permitirá que os dados sejam capturados por muito tempo e enviados ao banco de dados depois que o problema de esquema tiver sido resolvido.

  • Todos os serviços terão uma capacidade de pico, seja explicitamente (por meio de uma política de limitação ou assíntota de pico de carga) ou implicitamente (ao atingir um limite de recursos do sistema).

    • Projete seu aplicativo para decomposição normal diante do limite de recurso atingido e tome a medida adequada para suavizar o impacto sobre o usuário.

    • Implemente uma lógica de repetição/retirada adequada para evitar um efeito "comboio" nos serviços. Sem um mecanismo de retirada apropriado, os serviços de downstream nunca terão uma chance de se recuperar após passarem por um evento de pico (pois seu aplicativo tentará continuamente enviar mais carga por push para o serviço, disparando a política de limitação ou a privação de recursos).

  • Os serviços que podem passar por eventos rápidos de intermitência precisam lidar normalmente com o excesso do respectivo pico de carga de design, geralmente, por meio da funcionalidade de supressão.

    • Assim como o corpo humano restringe o fluxo de sangue para as extremidades quando em contato com o frio extremo, projete seus serviços para suprimir serviços menos importantes durante eventos de carga extrema.

    • A conclusão aqui é que nem todos os serviços fornecidos pelo seu aplicativo têm uma criticidade equivalente para os negócios e podem estar sujeitos a SLAs diferenciados.

Esses conceitos de alto nível serão aplicados mais detalhadamente em cada uma das seções que descrevem os principais serviços do Azure, juntamente com as metas de disponibilidade de cada serviço ou componente e as recomendações sobre como projetar o design para disponibilidade. Lembre-se de que o data center continua sendo um ponto único de falha para aplicativos grandes; desde as fontes de alimentação (veja um exemplo aqui) até erros do sistema (veja um exemplo aqui), os problemas de infraestrutura e aplicativo derrubaram os data centers. Embora seja relativamente raro, os aplicativos que exigem os níveis mais altos de tempo de ativação devem ser implantados em vários data centers redundantes.

A implantação de aplicativos em vários data centers requer vários recursos de infraestrutura e aplicativo:

  • Lógica de aplicativo para rotear usuários dos serviços para o data center apropriado (com base na geografia, no particionamento do usuário ou em outra lógica de afinidade).

  • Sincronização e replicação do estado do aplicativo entre data centers, com níveis adequados de latência e consistência.

  • Implantação autônoma de aplicativos, de tal modo que as dependências entre os data centers sejam minimizadas (isto é, evite situações em que uma falha no data center A dispare uma falha no data center B).

Assim como a disponibilidade, fornecer soluções de recuperação de desastres (no caso de perda do data center) exigia muito tempo, energia e capital. Esta seção se concentrará nas abordagens e considerações para fornecer continuidade de negócios no caso de falhas de sistemas e perda de dados (se disparadas pelo sistema ou usuário), pois o termo "recuperação de desastres" assumiu conotações específicas em torno das abordagens de implementação na comunidade de banco de dados.

O fornecimento de continuidade de negócios se divide em:

  • Manter acesso a sistemas essenciais aos negócios, bem como a disponibilidade desses sistemas (aplicativos operando em relação ao estado durável) no caso de uma falha catastrófica na infraestrutura.

  • Manter acesso a dados essenciais aos negócios, bem como a disponibilidade desses dados (estado durável) no caso de uma falha catastrófica na infraestrutura.

  • Manter a disponibilidade de dados essenciais aos negócios (estado durável) no caso de um erro do operador, ou corrupção, modificação e exclusão acidental.

Normalmente, os primeiros dois elementos têm sido resolvidos no contexto de recuperação de desastres geográficas (geo-DR), com o terceiro elemento sendo o domínio da restauração de dados e do backup de dados.

O Azure altera a equação de forma significativa para disponibilidade de sistemas essenciais aos negócios, permitindo a rápida implantação distribuída geograficamente dos principais aplicativos em data centers ao redor do mundo. Na verdade, o processo de implementar um aplicativo distribuído geograficamente é um pouco diferente de implementar um único serviço de nuvem.

O principal desafio continua sendo gerenciar o acesso no estado durável; acessar serviços de estado durável (como o Armazenamento e o Banco de dados SQL do Azure) pelos data centers geralmente produz resultados abaixo do ideal devido à latência alta e/ou variável, além de não atender ao requisito de continuidade de negócios no caso de falha do data center.

Assim como na resiliência, muitos serviços do Azure fornecem (ou têm em seu roteiro) replicação geográfica automática. Por exemplo, a menos que especificamente configurado em contrário, todas as gravações no armazenamento (blob, fila ou tabela) do Azure são replicadas automaticamente em outro data center (cada data center tem um destino de "espelho" específico na mesma região geográfica). Isso reduz significativamente o tempo e o esforço exigidos para fornecer soluções tradicionais de recuperação de desastres sobre o Azure. Uma visão geral dos recursos de replicação geográfica dos serviços do Azure gerenciando o estado durável é fornecida nas seções posteriores.

Para manter a continuidade de negócios frente a um erro do operador ou usuário, há várias considerações adicionais a serem contabilizadas no design do seu aplicativo. Embora o Armazenamento do Azure forneça auditoria limitada por meio do recurso Análise de Armazenamento (descrito em uma seção mais adiante), ele não fornece nenhum recurso de restauração pontual. Os serviços que exigem resiliência frente à modificação ou exclusão acidental precisarão de abordagens centradas no aplicativo, como copiar periodicamente blobs em uma conta de armazenamento diferente.

O Banco de dados do SQL fornece funcionalidade básica para manter instantâneos de dados históricos, incluindo Cópia e importação/exportação de BD via bacpac. Essas opções são abordadas detalhadamente mais adiante neste documento.

Com a escala elástica fornecida pela plataforma Azure, a curva de fornecimento pode corresponder estritamente à curva de demanda (em vez de ter uma grande quantidade de capacidade extra para lidar com o pico de carga).

Com a escala elástica, o custo de mercadorias é orientado pelo(a):

  • Quantidade de unidades de escala que são empregadas em uma solução; VMs, contas de armazenamento, etc. (composição para escala)

  • Nível de eficiência em que o trabalho é executado por essas unidades de escala.

Nós nos referimos à quantidade de trabalho que pode ser executada para uma determinada quantidade de capacidade, conforme a densidade do aplicativo. As estruturas e os serviços mais densos permitem que uma quantidade maior de trabalho seja executada para uma determinada implantação de recursos; ou melhor dizendo, o aprimoramento da densidade permite a redução na capacidade implantada (e no custo), ou a capacidade de absorver carga adicional com a mesma capacidade implantada. A densidade é orientada por dois importantes fatores:

  • Quão eficientemente o trabalho é executado em uma unidade de escala. Essa é a forma tradicional de otimização de desempenho - gerenciando os bloqueios e a contenção de thread, otimizando algoritmos, ajustando consultas SQL.

  • Quão eficientemente o trabalho é coordenado pelas unidades de escala. Em um mundo onde os sistemas são compostos de grandes quantidades de unidades menores, a capacidade de juntá-los de modo eficaz é essencial no fornecimento de eficiência. Isso envolve estruturas e ferramentas que se comunicam através dos componentes, como pilhas de mensagens SOAP (como WCF, ORMs [como Entity Framework], chamadas ao TDS [código de cliente SQL] e serialização de objeto [como Contratos de Dados ou JSON]).

Além das técnicas tradicionais de otimização usadas em um único computador (ou o banco de dados), otimizar a comunicação e as operações distribuídas é essencial no fornecimento de um serviço eficiente e escalonável do Azure. Essas otimizações importantes são abordadas detalhadamente em seções posteriores:

  • Robusto, e não verborrágico. Para cada operação distribuída (ou seja, uma que resulte em uma chamada de rede), há uma determinada quantidade de sobrecarga para enquadramento, serialização, processamento de pacote, e assim por diante. Para minimizar a quantidade de sobrecarga, tente enviar em lotes um número menor de operações "robustas", em vez de um grande número de operações "verborrágicas". Lembre-se de que o envio em lote de operações granulares aumenta a latência e a exposição a potencial perda de dados. Veja os exemplos de comportamento adequado do envio em lotes:

    • SQL. Executar várias operações em um único lote.

    • Serviços REST e SOAP (como WCF). Aproveitar interfaces de operação centradas na mensagem, em vez de um estilo RPC verborrágico, e considerar uma abordagem em REST se possível.

    • Armazenamento do Azure (blobs, tabelas, filas). Publicar várias atualizações em um lote, e não individualmente.

  • Impacto de serialização. A movimentação de dados entre os computadores (assim como a entrada e saída no armazenamento durável) normalmente exige que os dados sejam serializados em um formato com fio. A eficiência (isto é, o tempo gasto e o espaço consumido) dessa operação domina rapidamente o desempenho geral do aplicativo de sistemas maiores.

    • Aproveite as estruturas de serialização altamente eficientes.

    • Use JSON para comunicação com dispositivos ou para aplicativos interoperáveis (legíveis).

    • Use serialização binária muito eficiente (como protobuf ou Avro) para a comunicação entre serviços quando você controla os pontos de extremidade.

  • Use estruturas eficientes. Há muitas estruturas ricas disponíveis para desenvolvimento, com conjuntos de recursos grandes e sofisticados. O lado negativo de muitas dessas estruturas é que você muitas vezes paga o custo do desempenho de recursos que não usa.

    • Isole serviços e APIs de cliente atrás de interfaces genéricas para permitir a substituição ou a avaliação comparativa (seja por meio de fábricas estáticas ou de uma inversão do contêiner de controle). Por exemplo, forneça uma camada de cache conectável ao trabalhar em uma interface genérica, e não em uma implementação específica (como o Azure Caching).

Na seção anterior, apresentamos os principais conceitos de design e as perspectivas para criar aplicativos que aproveitam a malha de nuvem fornecida pelo Azure. Esta seção explora os principais serviços e recursos da plataforma, ilustrando suas capacidades, os limites de escala e os padrões de disponibilidade.

Como cada componente do serviço ou da infraestrutura do Azure fornece uma capacidade finita com uma SLA de disponibilidade, compreender esses limites e comportamentos é essencial para fazer escolhas apropriadas de design para suas metas de escalabilidade de destino e o SLA do usuário final. Cada um dos serviços principais do Azure é apresentado no contexto de quatro pontos principais: recursos e a respectiva intenção, densidade, escalabilidade e disponibilidade.

Uma assinatura do Azure é a unidade básica da administração, da cobrança e das cotas de serviço. Cada assinatura do Azure tem um conjunto padrão de cotas que podem ser aumentadas contatando o suporte, sendo destinadas a impedir o consumo acidental de recursos e excedentes.

Cada assinatura tem um proprietário de conta, e um conjunto de coadministradores, autorizados por meio das Contas da Microsoft (antigas Live IDs), que têm controle total sobre os recursos da assinatura pelo portal de gerenciamento. Eles podem criar contas de armazenamento, implantar serviços de nuvem, alterar configurações e adicionar ou remover coadministradores.

As APIs de Gerenciamento do Azure (serviços Web baseados em REST) fornecem uma interface de automação para criar, configurar e implantar os serviços do Azure (usados pelo portal de gerenciamento de modo subjacente). O acesso a essas APIs é restrito pelo uso de certificados de gerenciamento.

 

Serviço Limite padrão

Serviços de Nuvem

20

Contas de armazenamento

20

Núcleos

20

Servidores lógicos do Banco de dados SQL

5

TipDica
Para obter a última assinatura do Azure e os limites do serviço, consulte Assinatura do Azure e limites, cotas e restrições do serviço

Um serviço de nuvem do Azure (anteriormente serviço hospedado) é a unidade básica da implantação e da escala. Cada serviço de nuvem consiste em duas implantações (produção e preparo), cada uma com um conjunto de funções. O serviço de nuvem tem uma entrada de DNS público (no formato servicename.cloudapp.net) para implantação de produção e uma entrada de DNS para a implantação de preparação (no formato someguid.cloudapp.net).

Cada implantação contém uma ou mais funções, uma função Web ou uma função de Trabalho que, por sua vez, contém uma ou mais instâncias (VMs não duráveis). Cada instância contém um instantâneo idêntico, imutável e não durável de um pacote de software para essa função (isto é, todas as instâncias em uma determinada função têm uma compilação idêntica implantada para elas). Essas instâncias executam uma versão especializada do Windows Server para o Azure (com muitos serviços desabilitados por padrão para segurança adicional, configurados para funcionar perfeitamente com a malha de serviços e rede, etc. do Azure) e, por padrão, são corrigidas automaticamente pela malha do Azure. A aplicação dinâmica de patches é controlada por um esquema de atualização sem interrupção, que é descrito abaixo.

Os serviços de nuvem podem ser implantados em qualquer data center do Azure, diretamente (escolhendo a região de destino ao criar o serviço) ou por meio de um grupo de afinidade. Um grupo de afinidade é uma referência indireta a um destino de implantação que pode ser usado para simplificar a implantação de todos os componentes de um aplicativo no mesmo data center.

As funções Web são pré-configuradas com uma instância do IIS, que hospeda o código do aplicativo. O código do aplicativo hospedado nas funções de trabalho é executado no host do aplicativo de execução longa pré-configurado. Cada serviço de nuvem pode ter até 25 funções. A configuração padrão das funções é executar o código .NET. Porém, uma função pode ser configurada para executar qualquer código compatível com o Windows Server - Java, Python, Ruby, node.js, etc. Todos os recursos da plataforma mencionados neste documento estão disponíveis em qualquer plataforma, mas podem exigir desenvolvimento adicional de proxy de cliente para ter como objetivo as APIs baseadas em REST.

Em um serviço de nuvem, todas as instâncias recebem endereços IP privados (no bloco 10.x); todas as conexões de saída parecem vir de um único endereço IP virtual, ou VIP (que é o VIP da implantação do serviço de nuvem), por meio da Conversão de Endereços de Rede. Todas as conexões de entrada devem passar por pontos de extremidade configurados; esses pontos de extremidade fornecem acesso com balanceamento de carga a portas e funções internas. Por exemplo, por padrão, as conexões de entrada HTTP/HTTPS (porta 80 e 443) para a implantação do serviço de nuvem têm carga balanceada em relação a todas as instâncias disponíveis da função Web primária.

Observe que a latência entre serviços (isto é, atravessar a NAT fora de um serviço de nuvem e por meio do balanceador de carga em outro) é muito mais variável do que o equivalente local, além de ser um dos motivos pelos quais as conexões entre serviços "robustos" ou em lotes são encorajadas para escalabilidade.

A malha do Azure também fornece um serviço de configuração disponível para todas as instâncias na implantação do serviço de nuvem. Um conjunto estático de chaves de configuração esperadas é fornecido na definição do serviço (como parte do ciclo de desenvolvimento), com o conjunto inicial de valores de configuração implantado juntamente com o serviço quando ele é publicado no Azure. Esse conjunto de valores de configuração está disponível como uma pesquisa de tempo de execução para todas as instâncias na implantação do serviço e pode ser modificado no tempo de execução em uma interface REST, no portal do Azure ou em um script do PowerShell.

Quando a configuração de tempo de execução é alterada, todas as instâncias podem optar (no código do aplicativo) por capturar a notificação de alteração da configuração e por tratar as atualizações de configuração internamente. Se o código do aplicativo não for escrito para capturar o evento de alteração da configuração, todas as instâncias na função passarão por uma experiência de reinicialização sem interrupção para atualizar a respectiva configuração.

O estado de cada instância não é durável; qualquer configuração acima da imagem base do Azure (uma VM especializada do Windows Server) requer a configuração na inicialização para criar contadores de desempenho, ajustar configurações do IIS, instalar software dependente, etc. Geralmente, esses scripts de configuração são executados como uma tarefa de inicialização definida pela configuração do serviço de nuvem.

Dentro de um serviço de nuvem, a malha do Azure fornece informações sobre a configuração, endereços IP internos, configuração de serviço, etc., por meio de RoleEnvironment. Todos os processos executados em uma instância do Azure podem acessar as informações em RoleEnvironment para recuperar a configuração, detectar a topologia de rede, etc. Você também pode usar as APIs de gerenciamento do Azure para acessar essas informações externamente.

A malha do Azure expõe dois conceitos principais para o gerenciamento de falhas de componentes, reconfiguração e atualização/aplicação de patches: domínios de atualização e domínios de falha.

Os domínios de atualização são agrupamentos lógicos em um serviço do Azure; por padrão, cada serviço tem cinco (5) domínios de atualização (esse valor pode ser modificado na definição do serviço de nuvem). Qualquer alteração ou atualização no serviço afeta apenas um único domínio de atualização por vez Os exemplos dessas alterações incluem aplicação de patches no sistema operacional, alteração do tamanho da máquina virtual, adição de funções ou instâncias de função a um serviço em execução, ou modificação da configuração do ponto de extremidade.

Isso permite a reconfiguração dinâmica de um serviço de nuvem em execução mantendo a disponibilidade. Para funções que contêm apenas uma única instância, a malha do Azure não pode fornecer disponibilidade durante operações de atualização, razão pela qual executar funções de única instância não atende ao SLA do Azure.

Os domínios de falha são agrupamentos lógicos que se baseiam no hardware subjacente. Embora não seja garantido fazer o mapeamento diretamente para alguma configuração de hardware específica, pense no agrupamento lógico como o meio encontrado pela malha do Azure de separar instâncias automaticamente dos recursos subjacentes que representam um único ponto de falha (como um único servidor físico subjacente, rack, etc.). Para atender ao SLA de serviço, o Azure precisa implantar instâncias em, pelo menos, dois domínios de falha. Esse é outro motivo pelo qual as implantações de função de única instância não atendem ao SLA do Azure.

Em resumo:

  • A unidade básica da implantação e escala no Azure é o Serviço de Nuvem, que consiste em um conjunto de funções. Cada função contém um conjunto de instâncias de função idênticas, cada uma executando uma versão especializada do Windows Server configurada para nuvem.

  • Além da topologia física (funções e instâncias) e do código do aplicativo, os serviços de nuvem definem uma configuração de todo o serviço. Essa configuração pode ser atualizada em tempo de execução.

  • Cada instância de função é não durável (não há garantia de que alterações, arquivos, etc., sejam persistidos no evento de reinicialização, aplicação de patches, casos de falha).

  • Cada serviço de nuvem expõe um único IP virtual para o tráfego de entrada e saída. O serviço de nuvem expõe pontos de extremidade, que fornecem mapeamento com balanceamento de carga (round robin) para uma porta e função interna.

  • O Azure usa domínios de atualização para separar logicamente grupos de instâncias e fornecer atualizações ou modificações sem interrupção (enquanto mantém a disponibilidade).

  • O Azure usa domínios de falha para agrupar fisicamente instâncias longe de pontos únicos de falha (como executar todas as instâncias no mesmo computador físico subjacente).

  • Aproveite várias assinaturas para isolar os ambientes de desenvolvimento, teste, preparação e produção.

Cada função contém um conjunto de uma ou mais instâncias. Cada uma dessas instâncias é uma VM, executando uma versão especializada do Windows Server. Atualmente, as instâncias (VMs) estão disponíveis em cinco tamanhos; de extrapequenas a extragrandes. Cada um desses tamanhos é alocado para uma determinada quantidade de CPU, memória, armazenamento e largura de banda.

 

Tamanho da máquina virtual Núcleos de CPU Memória Espaço em disco para recursos de armazenamento local nas funções Web e de Trabalho Espaço em disco para recursos de armazenamento local em uma função VM Largura de banda alocada (Mbps)

Extrapequena

Compartilhado

768 MB

19,480 MB

(6.144 MB são reservados para arquivos do sistema)

20 GB

5

Pequena

1

1,75 GB

229,400 MB

(6.144 MB são reservados para arquivos do sistema)

165 GB

100

Média

2

3,5 GB

500,760 MB

(6.144 MB são reservados para arquivos do sistema)

340 GB

200

Grande

4

7 GB

1.023.000 MB

(6.144 MB são reservados para arquivos do sistema)

850 GB

400

Extragrande

8

14 GB

2.087.960 MB

(6.144 MB são reservados para arquivos do sistema)

1.890 GB

800

TipDica
Para obter a última assinatura do Azure e os limites do serviço, consulte Assinatura do Azure e limites, cotas e restrições do serviço

Desde que duas ou mais instâncias sejam implantadas em diferentes domínios de falha e atualização, o Azure fornece os seguintes SLAs para serviços de nuvem:

  • 99,95% de conectividade externa de funções para a Internet (aquelas com pontos de extremidade externos)

  • 99,9% de detecção de problemas de instância de função em dois minutos e início de medidas corretivas

Os tamanhos e as contagens de ambas as instâncias de função podem ser alterados dinamicamente em um aplicativo em execução (observe que alterar os tamanhos de uma instância de função disparam uma reimplantação sem interrupção). De acordo com a abordagem de expansão para criação de aplicativos do Azure, maior não é necessariamente melhor quando se trata de selecionar tamanhos de instância. Isso se aplica ao custo (por que pagar por aquilo que você não usa) e ao desempenho (dependendo se sua carga de trabalho está associada à CPU, associada à E/S, etc.). As abordagens para selecionar o número de instâncias e os tamanhos da instância são examinadas mais detalhadamente na seção Práticas recomendadas deste documento.

O Armazenamento do Azure é o serviço de dados durável de linha de base para o Azure, fornecendo o armazenamento de blobs (arquivo), fila e tabela (chave para valores). A conta de armazenamento é a unidade básica de escala e disponibilidade, que fornece os recursos a seguir. Toda comunicação com o serviço de armazenamento é baseada em uma interface REST por HTTP.

TipDica
Para obter a última assinatura do Azure e os limites do serviço, consulte Assinatura do Azure e limites, cotas e restrições do serviço

O SLA de disponibilidade do Armazenamento do Azure garante que pelo menos em 99,9% do tempo, as solicitações formatadas corretamente para adicionar, atualizar, ler e excluir dados serão bem-sucedidas e processadas corretamente e que, além disso, as contas de armazenamento terão conectividade com o gateway da Internet.

Esses limites são compartilhados por todo o uso da conta de armazenamento individual; isto é, o número de operações por segundo e a largura de banda geral serão compartilhados entre tabelas, blobs e filas. Se um aplicativo exceder o número total de operações por segundo, o serviço poderá retornar um código HTTP de servidor 503 ocupado. As operações são específicas a cada aspecto do armazenamento (tabelas, filas ou blobs) e são descritas nas subseções a seguir.

Relembrando a metáfora de contêiner de envio usada anteriormente, cada conta de armazenamento é um contêiner que fornece uma determinada quantidade de capacidade. Exceder o limite de uma única conta (contêiner de envio) requer a utilização de várias contas no mesmo aplicativo.

O Armazenamento do Azure fornece disponibilidade e resiliência por padrão; todas as gravações ou atualizações no Armazenamento do Azure são replicadas de modo transparente e consistente pelos três nós de armazenamento (que residem em diferentes domínios de atualização e falha). O acesso ao Armazenamento do Azure é controlado pela autenticação de fator único na forma de chaves de acesso. Cada conta de armazenamento tem uma chave primária e uma secundária, que permite disponibilidade contínua quando a chave primária é girada. Os dados no Armazenamento do Azure são replicados geograficamente de modo automático em um data center "espelho" (a menos que esse recurso seja especificamente desabilitado usando o portal). A replicação geográfica é obscura, uma vez que aproveita o redirecionamento de DNS para fazer failover de clientes no local secundário no caso de falha do data center primário.

Observe que embora o Armazenamento do Azure forneça resiliência de dados por meio de réplicas automáticas, isso não impede que o código do seu aplicativo (ou desenvolvedores/usuários) corrompa dados por exclusão acidental ou involuntária, atualização, etc. Manter a fidelidade dos dados frente a um erro do usuário ou aplicativo requer técnicas mais avançadas, como copiar os dados em um local de armazenamento secundário com um log de auditoria. O armazenamento de blob fornece um recurso de instantâneo que pode criar instantâneos pontuais somente leitura do conteúdo do blob, que podem ser usados como a base de uma solução de fidelidade de dados para blobs.

O Armazenamento do Azure fornece a telemetria por meio do seu recurso Análise de Armazenamento, coletando e expondo os dados de uso sobre chamadas de armazenamento individual a tabelas, filas e blobs. A Análise de Armazenamento precisa ser habilitada para cada conta de armazenamento com uma política de coleta (coletar de todos, somente de tabelas, etc.) e uma política de retenção (por quanto tempo manter os dados).

O armazenamento de blob fornece o serviço de gerenciamento de arquivos no Azure, oferecendo um método econômico e altamente disponível para armazenar dados não estruturados em massa. O serviço fornece dois tipos de blobs:

  • Blobs de blocos. Os blobs de blocos foram desenvolvidos para gerenciar blobs grandes de dados de maneira eficiente. Cada blob de blocos consiste em até 50.000 blocos, cada um com um tamanho máximo de 4 MB (com um tamanho geral máximo de blob de blocos de 200 GB). Os blobs de blocos oferecem suporte ao carregamento paralelo para transferência de arquivos por redes de forma eficaz e simultânea. Os blocos individuais podem se inseridos, substituídos ou excluídos, mas não podem ser editados in-loco.

  • Blobs de páginas. Os blobs de páginas foram desenvolvidos para fornecer de modo eficiente operações aleatórias de leitura/gravação (como acessar um VHD). Cada blob de páginas tem um tamanho máximo de 1 TB, consistindo em páginas de 512 bytes. Páginas individuais ou grupos de páginas podem ser adicionados ou atualizados, com uma substituição in-loco.

Os limites de design para armazenamento de blob são listados na tabela abaixo. Lembre-se de que todas essas operações são consideradas nos limites gerais da conta de armazenamento.

 

Categoria do blob Limite

Tamanho máximo do blob (bloco)

200 GB (50 mil blocos)

Tamanho máximo do bloco

4 MB

Tamanho máximo do blob (página)

1 TB

Tamanho da página

512 bytes

Largura de banda/blob máximo

480 Mbps

Ao exceder os limites de tamanho ou largura de banda de um único blob, os aplicativos podem gravar em vários arquivos de blob simultâneos (ou sequenciais). Se seu aplicativo exceder os limites de uma única conta de armazenamento, utilize as várias contas de armazenamento para capacidade adicional.

As filas do Azure fornecem um serviço de mensagens intermediário (orientado) entre publicadores e assinantes. As filas oferecem suporte a vários publicadores e assinantes simultâneos, mas não expõem originalmente primitivas de mensagens de ordem mais alta, como roteamento baseado em tópico ou publicador/assinante. Geralmente, elas são usadas para distribuir itens de trabalho (como mensagens, documentos, tarefas, etc.) a um conjunto de instâncias de função de trabalho (ou entre vários serviços hospedados e assim por diante).

Filas do Windows Azure

As mensagens da fila são excluídas automaticamente após 7 dias se não forem recuperadas e excluídas por um aplicativo. Elas fornecem o recurso de separação entre publicadores e consumidores de informações; desde que ambos os lados tenham a chave da conta de armazenamento e o nome da fila, eles poderão se comunicar.

 

Categoria da fila Limite

Máximo de mensagens na fila

N/D (até o limite da conta de armazenamento)

Tempo de vida máximo de uma mensagem

1 semana (removida automaticamente)

Tamanho máximo da mensagem

64 kB

Taxa máxima de transferência

Aproximadamente 500 mensagens/segundo

As filas são destinadas a passar mensagens de controle, e não dados brutos. Se suas mensagens forem muito grandes para caber em uma fila, refatore suas mensagens para separar dados e comandos. Armazene os dados no armazenamento de blob, com uma referência (URI) aos dados e à intenção (isto é, o que fazer com os dados no armazenamento de blob) armazenada em uma mensagem da fila.

Para aumentar a taxa de transferência em uma única fila, envie várias mensagens em lote em uma única mensagem e utilize o comando Atualizar Mensagem para rastrear o andamento do encapsulamento de tarefas de mensagem. Outra técnica é colocar várias mensagens em um blob com um ponteiro para o blob na mensagem da fila.

Se o aplicativo exigir mais taxa de transferência que a fornecida por uma única fila, utilize várias filas simultâneas. Nesse contexto, seu aplicativo deve implementar lógica adequada de roteamento e particionamento.

O armazenamento de tabela do Azure fornece um repositório consistente, escalonável e altamente durável para dados colunares (bidimensionais). Ele fornece uma semântica { chave de partição, chave de linha } -> { dados[] } para armazenar e acessar dados, conforme observado no diagrama abaixo. Cada tabela é dividida por partições que, por sua vez, contêm entidades. Cada entidade pode ter seu próprio esquema (plano) ou lista de propriedades (colunas).

Tabelas do Windows Azure

Cada partição oferece suporte a até 500 operações por segundo; e cada tabela oferece suporte até o máximo de operações disponíveis na conta de armazenamento. Como cada entidade contém, além de dados reais, os metadados colunares (pois cada entidade pode ter um esquema diferente), não é recomendável nomes longos de coluna, especialmente para uma abordagem ampla.

 

Categoria da tabela Limite

Máximo de operações por segundo, por partição

500

Tamanho máximo da entidade (nomes de coluna + dados)

1 MB

Tamanho máximo da coluna (byte [] ou cadeia de caracteres)

64 kB

Número máximo de linhas

N/D (até o limite da conta de armazenamento)

Tipos de dados com suporte

byte[], Boolean, datetime, double, Guid, int32, int64, string

As entidades individuais (que podem ser pensadas como linhas) têm um tamanho máximo de 1 MB, com colunas individuais sendo limitadas a um máximo de 64 kB. Os tipos de dados com suporte são listados na tabela acima; para tipos sem suporte (como DateTimeOffset), é necessário um proxy de serialização no código do aplicativo (por exemplo, armazenar DateTimeOffset em um formato padrão de cadeia de caracteres).

O armazenamento de tabela fornece acesso aos dados armazenados usando chaves associadas a partições e entidades, verificação de partição ou verificação de entidade. Ele oferece suporte à projeção de filtro, pois uma expressão de filtro pode ser enviada ao armazenamento de tabela como parte da consulta e executada no armazenamento de tabela). O armazenamento de tabela não fornece índices secundários, de modo que qualquer pesquisa baseada na chave de partição ou na chave de entidade requer uma verificação de tabela e/ou partição. Para partições que contêm um número de entidades incomum, geralmente, isso tem um impacto expressivo no desempenho.

Qualquer processamento de consulta que leve mais que 5 segundos retornará um token de continuação que o aplicativo poderá usar para continuar recebendo os resultados da consulta. As consultas que recuperam mais de 1.000 entidades devem usar um modelo de paginação para devolver os dados em partes de 1.000 entidades (que, originalmente, têm suporte da API de armazenamento de tabela).

As únicas expressões de consulta com suporte atualmente no armazenamento de tabela são filtragem e seleção (selecionando propriedades específicas); o armazenamento de tabela não fornece uma semântica de agrupamento ou agregação do servidor. Para criar aplicativos que exigem recursos avançados de agregação ou analítica, muitas vezes a melhor opção é armazenar os dados na forma agregada ou usar um mecanismo relacional, como o Banco de dados SQL do Azure. Alguns aplicativos empregam uma abordagem híbrida, agregando dados do armazenamento de tabela em um Banco de dados SQL auxiliar, que é então usado para fins de consulta e relatórios.

A escolha de uma função adequada de particionamento é essencial para o uso eficaz e efetivo do armazenamento de tabela. Há duas opções básicas para o tipo de função de particionamento:

  • Tempo. Usadas frequentemente para armazenar dados de série temporal, como os contadores de desempenho do Diagnóstico do Azure (um uso abordado na seção de telemetria deste documento), as funções de particionamento baseadas no tempo convertem a hora atual em um valor que representa uma janela de tempo (o minuto, a hora atual, etc.)

    Essas funções permitem a pesquisa e a localização eficientes de uma partição específica (pois a cláusula de filtro do armazenamento de tabela oferece suporte a >=, <=, etc.), mas podem estar propensas à limitação se a janela de tempo escolhida for muito estreita e um evento de pico ocorrer. Por exemplo, se a função de partição escolhida for o minuto atual, e ocorrer um evento de pico, muitos clientes podem estar tentando gravar na mesma partição simultaneamente. Isso não afeta apenas a taxa de transferência na inserção, mas também a taxa de transferência na consulta.

  • Dados. As funções de particionamento centradas nos dados calculam o valor da partição com base em uma ou mais propriedades dos dados a serem armazenados (ou recuperados). A escolha de uma função adequada de particionamento orientada por dados depende de vários fatores - padrões de consulta, densidade das chaves de partição (quantas entidades terminarão em uma partição) e crescimento imprevisível (isso pode ser desafiador no rebalanceamento de tabelas muito grandes). Os padrões comuns incluem:

    • Campo único. A chave de partição é um campo único nos dados de origem (como uma ID de cliente para informações do pedido).

    • Vários campos. A chave de partição ou a chave de linha é uma composição (normalmente, uma concatenação) de vários campos nos dados de origem. Ao selecionar chaves de partição, observe que as operações em lote exigem que todas as entidades estejam na mesma partição (isto é, tenham a mesma chave de partição).

    • Campo calculado. A chave de partição é calculada a partir de um ou mais campos, com base em uma função determinista. Um exemplo comum disso seria distribuir perfis de usuário em várias partições. A ID de usuário teria hash usando uma função de hash desenvolvida para distribuição relativamente uniforme e, em seguida, o módulo seria obtido em relação ao número de partições desejadas.

Qualquer aplicativo incomum exigirá o uso de várias partições. Mesmo para tabelas com um pequeno número total de entidades (p. ex., duzentas), se o aplicativo fizer milhares de solicitações por segundo, várias partições serão necessárias para a taxa de transferência:

  • Tabela única/partição única. A opção mais simples (valor da chave de partição constante), adequada para cargas de trabalho de pequena escala com quantidades limitadas de requisitos de taxa de transferência de dados e solicitação (< 500 entidades/s).

  • Tabela única/várias partições. A opção comum para a maioria das implantações; escolha cuidadosamente as chaves de partição para alinhar com os padrões de consulta de destino.

  • Conta de vários armazenamentos/várias partições. Se a carga foi projetada para ultrapassar 5.000 operações por segundo, será necessário que o uso das tabelas se espalhe pelas várias contas de armazenamento.

Uma vez escolhido, o rebalanceamento (reparticionamento) de dados pode ser um exercício caro que envolve leitura e cópia de todas as entidades com novas chaves de partição, depois excluindo os dados antigos. Observe que não há restrição de tamanho mínimo em uma partição; as partições podem consistir em uma única entidade (isto é, linha).

Se seu aplicativo exigir mais taxa de transferência que a fornecida por uma única tabela (após cuidadosa seleção da partição), utilize várias tabelas simultâneas em contas diferentes de armazenamento. Nesse contexto, seu aplicativo precisará implementar a lógica adequada de roteamento para selecionar a conta de armazenamento apropriada.

A CDN (Rede de Distribuição de Conteúdo) do Azure é uma maneira eficiente de armazenar em cache o conteúdo estático (das saídas de blobs ou aplicativo) em uma rede de cache distribuída globalmente. Isso alivia a pressão sobre o aplicativo para fornecimento de conteúdo estático e melhora a experiência geral do usuário final.

O SLA de disponibilidade da Rede de Distribuição de Conteúdo garante que os objetos em cache sejam fornecidos com 99,9% de disponibilidade mensalmente.

O uso da CDN exige que o recurso seja ativado para sua assinatura. Nesse recurso, o conteúdo do blob (de contêineres de acesso anônimo/disponíveis publicamente) e o conteúdo da saída do aplicativo anônimo (por exemplo, http://www.myapp.com/cdn/somepage.aspx) podem ser armazenados em cache.

Em geral, para qualquer aplicativo de larga escala, todo o conteúdo estático frequentemente acessado (imagens, css, etc.) deve ser fornecido via CDN com as políticas apropriadas de vencimento de cache.

Por exemplo, considere uma loja online de livros eletrônicos com 1 milhão de títulos. A inclusão de conteúdo (imagens, etc.) para todos os títulos na CDN será bastante cara (pois a maioria do conteúdo não será acessada com frequência e expirará constantemente), enquanto a inclusão apenas do conteúdo principal (por exemplo, os 50 títulos principais) fornece a combinação certa de cache comparada ao preço.

Um dos principais elementos de fornecimento bem-sucedido de um serviço em larga escala é a telemetria - visão direta da operação, experiência de usuário final e desempenho do seu aplicativo. As abordagens de telemetria para aplicativos do Azure precisam levar em conta a natureza distribuída/de expansão da plataforma, bem como os serviços de plataforma disponíveis para coleta, análise e consumo de telemetria.

O componente tecnológico de linha de base para coleta e compreensão da telemetria no Azure é o WAD (Diagnóstico do Azure). O WAD consiste em um agente que coleta dados de instâncias individuais e os encaminha a um ponto de coleta central (conta de armazenamento), bem como um conjunto de convenções e estruturas padrão para armazenar e acessar dados. O agente oferece suporte a várias abordagens de configuração, incluindo código (.NET), um arquivo de configuração inserido no código do projeto implantado ou um arquivo de configuração centralizado implantado no armazenamento de blob. A configuração é parcialmente dinâmica na última instância, permitindo que os arquivos de diagnóstico atualizados sejam transmitidos para o armazenamento de blob e depois movidos para os agentes destinados.

Diagnóstico do Windows Azure

A configuração WAD fornece várias fontes de dados; cada uma delas é periodicamente coletada, enviada em lotes por meio de uma sessão ETW (Rastreamento de Eventos para Windows) e publicada na conta de armazenamento de destino. O agente cuida dos problemas transitórios de conexão, repetições, etc. As fontes de dados disponíveis são:

  • Contadores de Desempenho. Um subconjunto de valores de Contadores de Desempenho (CPU, memória, etc.) capturados em uma sessão ETW local e, periodicamente, apresentados no armazenamento de tabela.

  • Logs de eventos do Windows. Um subconjunto de registros de Eventos do Windows (aplicativos, sistema, etc.) capturados em uma sessão ETW local e, periodicamente, apresentados no armazenamento de tabela.

  • Logs do Azure. Os logs de aplicativos (mensagens de rastreamento) publicados pelo código do aplicativo (via System.Diagnostics.Trace) e capturados por um ouvinte de rastreamento DiagnosticMonitorTraceListener. Os logs são publicados na tabela de Contadores de Desempenho do WAD na conta de armazenamento de destino.

  • Logs do IIS 7.0. Informações padrão do log do IIS sobre solicitações registradas pelo IIS (apenas funções Web). Os logs são coletados em arquivos locais e, periodicamente, apresentados no armazenamento de blob.

  • Logs de solicitação com falha do IIS. Informações do IIS sobre solicitações com falha, coletadas em arquivos locais e, periodicamente, apresentadas no armazenamento de blob.

  • Despejos de memória. No caso de uma falha no sistema, os logs sobre o estado do sistema operacional são capturados e publicados no armazenamento de blob.

  • Fonte de dados. O WAD pode monitorar diretórios locais adicionais (como diretórios de log no armazenamento local) e copiar os dados periodicamente em um contêiner personalizado no armazenamento de blob.

Cada uma dessas fontes de dados é configurada com o subconjunto de dados para coleta (por exemplo, a lista de contadores de desempenho) e o intervalo de coleta/publicação. Também há vários scripts do PowerShell disponíveis para alterar a configuração do tempo de execução ou forçar uma publicação imediata dos dados dos agente na conta de armazenamento de destino.

ImportantImportante
Telemetria do log em uma conta de armazenamento separada. O registro em log dos dados de telemetria e aplicativos na mesma conta de armazenamento causará sérios problemas de contenção na escala.

O Banco de dados SQL do Azure fornece banco de dados como serviço, permitindo que os aplicativos provisionem, insiram dados e consultem bancos de dados relacionais rapidamente. Ele apresenta muitos dos recursos e funcionalidades conhecidos do SQL Server, ao mesmo tempo que abstrai a carga do hardware, da configuração, da aplicação de patches e da resiliência.

noteObservação
O Banco de dados SQL não fornece paridade de recursos 1:1 com o SQL Server e foi desenvolvido para atender a um conjunto diferente de requisitos, exclusivamente adequados para aplicativos em nuvem (escala elástica, banco de dados como serviço para reduzir os custos de manutenção e assim por diante). Para obter mais informações, consulte http://blogs.msdn.com/b/windowsazure/archive/2012/06/26/data-series-sql-server-in-windows-azure-virtual-machine-vs-sql-database.aspx.

O serviço é executado em um ambiente compartilhado multilocatário, com bancos de dados de vários usuários e assinaturas em execução na infraestrutura criada no hardware de mercadoria (expansão horizontal, não vertical).

Os bancos de dados são provisionados nos servidores lógicos; cada servidor lógico contém, por padrão, até 150 bancos de dados (incluindo o banco de dados mestre). Por padrão, cada assinatura pode provisionar cinco (5) servidores lógicos, com capacidade para aumentar essa cota, bem como o número máximo de bancos de dados por servidor lógico, ligando para o suporte.

Cada servidor lógico recebe um nome DNS gerado público e exclusivo (no formato [nome_do_servidor].database.windows.net), com cada servidor lógico em uma assinatura que compartilha o mesmo endereço IP público. Os servidores (e bancos de dados) lógicos são acessados pela porta SQL padrão (TCP/1433), com uma API de gerenciamento baseada em REST acessando na porta TCP/833.

Por padrão, o acesso ao servidor lógico (e a todos os seus bancos de dados) é restrito pelas regras de firewall baseadas em IP para o portal de gerenciamento do Azure (as regras podem ser definidas em relação ao servidor lógico ou aos bancos de dados individuais). Habilitar o acesso aos aplicativos do Azure e a conectividade direta de aplicativos fora do Azure (por exemplo, para conectar o SQL Server Management Studio) requer a configuração das regras de firewall. Essas regras podem ser configuradas pelo portal de gerenciamento do Azure usando uma chamada à API do serviço de gerenciamento.

O Banco de dados SQL oferece suporte à maioria dos principais recursos presentes no SQL Server, com várias exceções importantes que incluem:

  • Todas as tabelas devem conter um ÍNDICE CLUSTERIZADO; os dados não podem ser INSERIDOS em uma tabela no Banco de dados SQL até que um ÍNDICE CLUSTERIZADO tenha sido definido.

  • Não há suporte inserido para CLR (Common Language Runtime), espelhamento de banco de dados, service broker, compactação de dados nem particionamento de tabela.

  • Não há suporte para tipos de dados XML; índices XML.

  • Não há suporte para TDE (criptografia de dados transparente) ou auditoria de dados

  • Não há suporte para pesquisa de texto completo

Cada banco de dados, quando criado, é configurado com um limite de tamanho superior. Atualmente, as capacidades de tamanho disponíveis são 1 GB, 5 GB, 10 GB, 20 GB, 30 GB, 40 GB, 50 GB, 100 GB e 150 GB (o tamanho máximo disponível atualmente). Quando um banco de dados atinge o limite de tamanho superior, ele rejeita os comandos adicionais INSERT ou UPDATE (mas ainda é possível consultar e excluir dados). O tamanho de um banco de dados também pode ser criado (aumentado ou diminuído) ao emitir um comando ALTER DATABASE.

À medida que os bancos de dados são faturados com base no tamanho médio usado por dia, os aplicativos que esperam o crescimento rápido ou imprevisível podem optar por definir inicialmente o tamanho máximo do banco de dados para 150 GB. Dimensionar um banco de dados para além de 150 GB requer a utilização de uma abordagem de expansão, descrita mais detalhadamente na seção abaixo.

O Banco de dados SQL fornece resiliência interna a falhas no nível de nó. Todas as gravações em um banco de dados são replicadas automaticamente em dois ou mais nós em segundo plano usando uma técnica de confirmação de quorum (o primário e, pelo menos, um secundário devem confirmar que a atividade foi gravada no log de transações antes que a transação seja considerada bem-sucedida e retorne). Em caso de falha do nó, o banco de dados faz failover automaticamente em uma das réplicas secundárias Isso provoca uma interrupção transitória na conexão para os aplicativos cliente - um dos principais motivos pelos quais todos os clientes do Banco de dados SQL devem implementar alguma forma de manipulação de conexão transitória (veja abaixo os detalhes sobre como implementar a manipulação de conexão transitória).

O SLA mensal de disponibilidade é de 99,9% de tempo de ativação, definido como a capacidade de se conectar ao Banco de dados SQL em 30 segundos em um intervalo de 5 minutos. Os eventos de failover descritos no parágrafo anterior geralmente ocorrem em menos de 30 segundos, reforçando a necessidade de que seu aplicativo manipule as falhas de conexão transitórias.

O Banco de dados SQL fornece visão direta da integridade e do desempenho por meio de DMVs (exibições de gerenciamento dinâmico); essas DMVs contêm informações sobre os principais aspectos do sistema, como desempenho da consulta, tamanho do banco de dados e da tabela, e assim por diante. Os aplicativos são responsáveis pela coleta e análise periódica das informações das principais DMVs, bem como pela integração na estrutura mais ampla de telemetria e percepção.

Há várias opções de continuidade de negócios (backup, restauração) disponíveis para o Banco de dados SQL. Os bancos de dados podem ser copiados por meio da funcionalidade Cópia de Banco de Dados ou pelo Serviço de Importação/Exportação do DAC. A Cópia de Banco de Dados fornece resultados transacionais e consistentes, enquanto um bacpac (por meio do serviço de importação/exportação), não. Essas duas opções são executadas como serviços baseados em fila no data center e, atualmente, não fornecem um SLA de tempo para conclusão.

Observe que o serviço de cópia e de importação/exportação do banco de dados impõe um grau significativo de carga no banco de dados de origem, podendo disparar a contenção de recursos ou eventos de limitação (descritos abaixo na seção Recursos compartilhados e limitação). Quando nenhuma dessas abordagens fornecer o mesmo grau de backup incremental suportado pelo SQL Server, um novo recurso está atualmente na visualização para permitir a capacidade de restauração pontual. O recurso de restauração pontual permite que os usuários restaurem o respectivo banco de dados em um ponto arbitrário dentro das últimas duas semanas.

Atualmente, a única técnica de autenticação com suporte é a autenticação SQL, logons de nome de usuário/senha de único fator com base nos usuários registrados no banco de dados. O Active Directory ou os recursos de autenticação de dois fatores ainda não estão disponíveis. A criptografia da conexão com o Banco de dados SQL é altamente recomendada, usando o suporte interno de criptografia presente no ADO.NET, ODBC, etc. As permissões no nível de banco de dados são consistentes com o SQL Server. Consulte Gerenciando bancos de dados e logons no Banco de dados SQL do Azure para obter uma discussão detalhada sobre a configuração da segurança do Banco de Dados SQL do Azure.

O Banco de dados SQL fornece um rico conjunto de exibições de gerenciamento dinâmico para observação da integridade do banco de dados e do desempenho da consulta. No entanto, não é fornecida nenhuma infraestrutura automatizada para coleta e análise desses dados (e ferramentas conhecidas como o criador de perfil anexado diretamente e os contadores de desempenho no nível de sistema operacional, não estão disponíveis). As abordagens para coleta e a análise são descritas na seção Telemetria deste documento.

Conforme descrito acima, o Banco de dados SQL é um serviço multilocatário executado em uma infraestrutura compartilhada. Os bancos de dados de diferentes locatários compartilham os nós físicos subjacentes, criados no hardware de mercadoria. Outros usuários do sistema podem consumir recursos principais (threads de trabalho, log de transações, E/S, e assim por diante) na mesma infraestrutura subjacente. O uso de recursos é controlado e desenvolvido para manter os bancos de dados dentro dos limites de recurso do conjunto. Quando esses limites são excedidos, seja em um nível de nó locatário ou físico, o Banco de dados SQL responde limitando o uso ou interrompendo conexões. Esses limites são listados na tabela abaixo.

 

Recurso Valor máximo por transação/sessão Valor máximo por nó físico Limitação de software Limitação de hardware

Threads de trabalho

N/D

512

305

410

Tamanho do banco de dados

Tamanho máximo configurado até 150 GB por banco de dados

N/D

Nenhum

100%; não são aceitas inserções nem atualizações quando se atinge o limite

Crescimento do log de transações

2 GB por transação

500 GB

N/D

N/D

Comprimento do log de transações

20% do espaço total de log (100 GB)

500 GB

N/D

N/D

Contagem de bloqueio

1 milhão por transação

N/D

N/D

N/D

Tarefas do sistema de bloqueio

20 segundos

N/D

N/D

N/D

Espaço de banco de dados temporário

5 GB

N/D

N/D

5 GB

Memória

N/D

N/D

N/D

16 MB por 20 segundos

Máximo de solicitações simultâneas

400 por banco de dados

N/D

N/D

N/D

Quando um limite de transação é atingido, o sistema responde cancelando a transação. Quando um banco de dados atinge a limitação de software, a velocidade das transações e conexões é reduzida ou anulada. Atingir uma limitação de hardware afeta todos os bancos de dados (e usuários) no nó físico subjacente, fazendo com que operações existentes sejam encerradas e impedindo novas operações ou conexões até que o recurso fique abaixo da limitação.

Algumas dessas limitações resultam em limites potencialmente não intuitivos do design e desempenho de um aplicativo. Por exemplo, limitar o crescimento do log de transações a um máximo de 2 GB por transação evita a criação de um índice em uma tabela grande (onde criar o índice vai gerar mais de 2 GB de log de transações). As técnicas para executar essas operações são discutidas na seção Práticas recomendadas deste documento.

Manipular esses tipos de condição de limitação e erros transitórios exige design e implementação cuidadosos do código do cliente; abordá-los exige expansão da camada do banco de dados para aproveitar vários bancos de dados simultaneamente (a expansão é abordada na próxima seção).

O código do aplicativo cliente do SQL deve:

  • Implementar o código de repetição que reconheça os códigos de erro do SQL relacionados à limitação e forneça a lógica de retirada apropriada. Sem alguma forma de lógica de retirada no aplicativo, você pode bloquear o banco de dados em um estado de limitação contínua transmitindo constantemente o pico de carga ao banco de dados.

  • Registrar em log erros de limitação usando o código de repetição para distinguir entre a conexão transitória, a limitação e a falha de hardware - sintaxe, ausência de sprocs, e assim por diante. Isso ajudará no rastreamento e na solução de problemas de disponibilidade do aplicativo.

  • Implementar um padrão de disjuntor. Quando uma política de repetição escolhida adequadamente expira (latência de balanceamento e resposta do sistema em relação à frequência de repetição do aplicativo), invoque um caminho de código para tratar o erro não transitório (isto é, ative o disjuntor). O código do aplicativo poderá então:

    • Fazer fallback para outro serviço ou abordagem. Se houver falha no aplicativo ao inserir novos dados no Banco de dados SQL (e os dados não precisarem estar imediatamente disponíveis), os dados podem ser serializados em um DataTable (ou outro formato XML/JSON) e gravados em um arquivo no armazenamento de blob. Desse modo, o aplicativo pode retornar um código de êxito ao usuário (ou chamada à API) e inserir os dados no banco de dados posteriormente.

    • Falhar no modo silencioso retornando um valor nulo, caso os dados ou o fluxo de trabalho fossem opcionais (não afetará a experiência do usuário final).

    • Falhar no modo rápido retornando um código de erro, caso nenhum mecanismo de fallback útil/adequado esteja disponível.

O Banco de dados SQL pode fornecer um grande número de unidades de escala (bancos de dados) relativamente pequenas com facilidade. A implementação de aplicativos altamente escalonáveis no Azure utilizando o Banco de dados SQL requer uma abordagem de expansão, compondo os recursos de vários bancos de dados para atender à demanda variável. Com os aplicativos sendo direcionados para a abordagem de "casca de ovo de titânio" de um único servidor de banco de dados altamente resiliente de expansão vertical, é preciso considerar um design coerente para que eles utilizem com eficiência um serviço de banco de dados de expansão horizontal.

Com o Banco de dados SQL, assim como nos outros serviços principais do Azure, a expansão e a composição são as chaves para a escala (tamanho do banco de dados, taxa de transferência) e os recursos (threads de trabalho, etc.) adicionais. Há duas abordagens principais para implementar o particionamento/a fragmentação (e, portanto, a expansão) para o Banco de dados SQL; essas abordagens não são mutuamente exclusivas em um aplicativo:

  • Particionamento horizontal. Em uma abordagem de particionamento horizontal, tabelas intactas e conjuntos de dados são separados em bancos de dados individuais. Por exemplo, para um aplicativo multilocatário que atende a diferentes conjuntos de clientes, o aplicativo pode criar um banco de dados por cliente. Para um aplicativo de único locatário maior, a tabela do cliente pode residir em um banco de dados diferente da tabela de pedidos. A chave de particionamento geralmente é o identificador do locatário (p. ex., ID do cliente). No diagrama abaixo, o conjunto de dados é particionado horizontalmente em três bancos de dados diferentes, usando um hash de email como um valor de partição (isto é, a chave de partição é o email, a função de partição usa um hash da chave para mapear para um banco de dados de destino).

    Particionamento horizontal.
  • Particionamento vertical. Em uma abordagem de particionamento vertical, um conjunto de dados é distribuído por várias tabelas ou vários bancos de dados físicos, com base no particionamento do esquema. Por exemplo, os dados do cliente e os dados do pedido podem ser distribuídos pelos diferentes bancos de dados físicos. No diagrama abaixo, o conjunto de dados é particionado verticalmente em dois bancos de dados diferentes. As principais informações do usuário (nome, email) são armazenadas no DB1, com informações do perfil do usuário (como o URI da imagem do avatar) armazenadas no DB2.

    Particionamento vertical.

Muitos aplicativos usarão uma combinação de particionamento horizontal e vertical (particionamento híbrido), bem como incorporar os serviços de armazenamento adicionais. Por exemplo, no exemplo acima, as imagens de avatar para usuários foram armazenadas como IDs no banco de dados, que o aplicativo expande para uma URL completa. Essa URL então é mapeada para uma imagem armazenada em um blob.

Ao trabalhar com um repositório de dados relacional expandido, o cálculo de disponibilidade é bastante diferente. Os sistemas com números maiores de fragmentos têm uma probabilidade maior de alguns segmentos de dados individuais estarem offline, com uma probabilidade muito menor de o aplicativo inteiro estar indisponível. Os aplicativos precisam levar em consideração a disponibilidade parcial de repositórios de dados de back-end. Com um modelo de dados de expansão, a disponibilidade de dados não é mais uma condição do tipo "tudo ou nada".

O reparticionamento de dados pode se mostrar desafiador, especialmente se o modelo de uso ou a distribuição de dados mudar ao longo do tempo. As chaves de particionamento baseadas no intervalo, se baseadas em um número fixo (usando um módulo do hash dos valores de particionamento), ou a distribuição dos valores de particionamento, exigem rebalanceamento de dados entre os fragmentos individuais. Os esquemas de partição baseados em intervalo costumam aproveitar divisões binárias ou mesclagens para simplificar as operações de rebalanceamento.

Por exemplo, os métodos de particionamento por intervalos fixos (como a primeira letra do sobrenome) podem ser iniciados como uma distribuição balanceada, mas podem mudar rapidamente tornando-se altamente desbalanceados à medidas que novos usuários são mostrados (cada um trazendo seu próprio sobrenome, que podem ou não ser igualmente distribuídos pelo alfabeto). Lembre-se da necessidade de horas extras para ajustar o mecanismos de particionamento e o custo do rebalanceamento dos dados.

Os esquemas de particionamento baseados na pesquisa são mais difíceis de implementar, exigindo um mecanismo de pesquisa de alto desempenho para cada locatário ou partição de dados, mas são mais receptivos ao rebalanceamento granular, pois permitem que um único locatário seja individualmente rebalanceado para uma nova partição. Eles também permitem que capacidade adicional (novos bancos de dados, etc.) seja incluída no sistema sem exigir cópia de dados.

Independentemente da combinação de abordagens de fragmentação, a transferência para uma expansão de bancos de dados relacionais fragmentados carrega consigo determinadas restrições que exigem uma abordagem diferente para gerenciamento e consulta de dados:

  • Os designs tradicionais "bons" de consulta e armazenamento de dados SQL foram otimizados para armazenamento e consistência ao utilizar modelos de dados totalmente normalizados. Essa abordagem supõe um espaço de dados globalmente coerente, que aproveita referências cruzadas e JOINs entre tabelas. Com a difusão de dados por nós fisicamente distribuídos, JOINs e referências cruzadas são possíveis apenas em um único fragmento. O Banco de dados SQL não oferece suporte a consultas distribuídas entre vários bancos de dados; a mescla de dados precisa ser manipulada na camada do cliente e a desnormalização e replicação dos dados nos fragmentos.

  • Geralmente, os dados de referência e os metadados são centralizados em tabelas de referência. Em uma abordagem de expansão, essas tabelas de referência (e todos os dados que não podem ser separados pela chave de particionamento comum) precisam ser replicadas pelos fragmentos e mantidas consistentes.

  • Não há uma maneira prática de fornecer transações distribuídas escalonáveis e de alto desempenho pelos fragmentos; os dados (e atualizações de esquema!) nunca serão consistentes e transacionais entre os fragmentos. O código do aplicativo precisa supor, e se responsabilizar por, um grau de consistência flexível entre fragmentos.

  • O código do aplicativo precisa entender o mecanismo de fragmentação (horizontal, vertical, tipo de particionamento) para poder se conectar ao(s) fragmento(s) apropriado(s).

  • Os ORMs comuns (como o Entity Framework) não entendem originalmente os modelos de dados de expansão; os aplicativos que fazem uso extensivo de ORMs grandes podem exigir significativa reestruturação para compatibilidade com a fragmentação horizontal. Os designs que isolam locatários (conjuntos de clientes) em uma abordagem fragmentada verticalmente em um único banco de dados geralmente requerem menos reestruturação na camada de acesso a dados. O lago negativo do modelo de fragmento puramente vertical é que cada fragmento, individualmente, é limitado pela capacidade de um único banco de dados.

  • As consultas que precisam tocar (leitura ou gravação) vários fragmentos precisam ser implementadas com um padrão scatter/gather, onde consultas individuais são executadas nos fragmentos de destino e o conjunto de resultados agregado na camada de acesso a dados do cliente.

A expansão com o Banco de dados SQL é feita através do particionamento manual ou da fragmentação de dados em vários Bancos de dados SQL. Essa abordagem de expansão fornece a oportunidade de atingir o crescimento de custo quase linear com a escala. O crescimento elástico ou a capacidade sob demanda pode aumentar com custos incrementais, conforme a necessidade. Nem todos os aplicativos podem oferecer suporte a esse modelo de expansão sem reestruturação significativa.

  • Não há garantia de que as atualizações de esquema sejam consistentes e transacionais, especialmente ao atualizar um grande número de fragmentos. Os aplicativos precisam aceitar períodos de tempo de inatividade planejados ou se responsabilizarem por várias versões simultâneas do esquema implantado.

  • Os processos de continuidade de negócios (backup/restauração, etc.) precisam se responsabilizar por vários fragmentos de dados.

As recomendações de design e as práticas recomendadas para abordar esses desafios são discutidas na seção Práticas recomendadas deste documento.

O restante deste documento se concentra em ilustrar as práticas recomendadas para fornecer aplicativos altamente escalonáveis com o Banco de dados SQL e o Azure, com base nas experiências reais e no treinamento que eles fornecem. Cada prática recomendada aborda a otimização e os componentes visados, a abordagem de implementação e os prós e contras inerentes. Assim como em qualquer prática recomendada, essas recomendações são altamente dependentes do contexto em que são aplicadas. Avalie cada prática recomendada para ajuste com base nos recursos de plataforma discutidos na seção anterior.

noteObservação
Essas experiências foram extraídas de várias relações com clientes que não seguem o padrão OLTP (processamento de transações online) clássico. É importante perceber que algumas dessas práticas recomendadas podem não ser diretamente aplicáveis a aplicativos que exigem consistência de dados estrita e sólida; somente você conhece os requisitos precisos de negócios do seu aplicativo e do respectivo ambiente.

Cada prática recomendada se relacionará a um ou mais aspectos da otimização:

  • Taxa de transferência. Como aumentar o número de operações (transações, chamadas de serviço, etc.) pelo sistema e reduzir a contenção.

  • Latência. Como reduzir a latência, em operações individuais e agregadas.

  • Densidade. Como reduzir os pontos de contenção ao compor serviços em contextos diretos (por exemplo, código do aplicativo para o Banco de dados SQL) e contextos agregados (utilizando várias contas de armazenamento para aumento da escala).

  • Capacidade de gerenciamento. Diagnóstico, telemetria e visão – como entender a integridade e o desempenho dos serviços implantados na escala.

  • Disponibilidade. Como aumentar a disponibilidade geral do aplicativo reduzindo o impacto dos modos e pontos de falha (disponibilidade sob carga é abordada na taxa de transferência/latência/densidade).

Como a unidade de escala da linha de base no Azure, o design e a implantação cuidadosos dos serviços hospedados é essencial para fornecer serviços disponíveis e altamente escalonáveis.

  • O número de instâncias e domínios de atualização em um serviço hospedado pode ter um impacto significativo no tempo exigido para implantar, configurar e atualizar o serviço hospedado. Equilibre os benefícios de desempenho e escalabilidade com o aumento da complexidade exigida para obter esses benefícios. Geralmente, melhorar a escalabilidade e a flexibilidade aumenta os custos de desenvolvimento e gerenciamento de uma solução.

  • Evite funções de única instância; essa configuração não atende aos requisitos do SLA do Azure. Durante um evento de atualização ou falha de nó, uma função de única instância fica offline. Limite seu uso às tarefas de "manutenção" de prioridade baixa.

  • Cada data center tem uma capacidade finita (embora grande) e pode atuar como um ponto único de falha sob raras circunstâncias. Os serviços que exigem o nível mais alto de escala e disponibilidade devem implementar uma topologia de vários data centers com vários serviços hospedados. Porém:

  • Quando o nível mais alto de disponibilidade não for exigido (veja o item anterior), assegure-se de que os aplicativos e serviços dependentes sejam inteiramente contidos em um único data center. Em situações em que a solução deve usar vários data centers, use as seguintes diretrizes:

    • Evite chamadas de rede entre data centers para operações dinâmicas (fora da sincronização deliberada entre sites). A latência por longos períodos entre data centers pode ser altamente variável e produzir características de desempenho de aplicativo indesejáveis ou inesperadas.

    • Tenha apenas a funcionalidade mínima necessária que acessa os serviços em outro data center. Geralmente, essas atividades se relacionam à continuidade de negócios e à replicação de dados.

Para aplicativos distribuídos em grande escala, o acesso aos dados de aplicativo com estado é essencial. A latência e a taxa de transferência geral do aplicativo geralmente são associadas pela rapidez com que o contexto e os dados exigidos podem ser recuperados, compartilhados e atualizados. Os serviços de cache distribuído, como o Azure Caching e memcached, evoluíram em resposta a essa necessidade. Os aplicativos devem usar uma plataforma de cache distribuído. Considere as seguintes diretrizes:

  • Use uma plataforma de cache distribuído como uma função de trabalho no seu serviço hospedado. Essa proximidade com os clientes do cache reduz as barreiras de latência e taxa de transferência apresentadas pela passagem do balanceador de carga O Cache na Função no Cache do Azure hospeda o cache em funções de trabalho no seu serviço de nuvem.

  • Use a plataforma de cache distribuído como o repositório primário para acessar dados e objetos comuns do aplicativo (por exemplo, perfil de usuário e estado de sessão), com o apoio do Banco de dados SQL ou outro repositório durável em uma abordagem cache-aside ou por leitura.

  • Os objetos do cache têm uma vida útil que afeta quanto tempo eles ficam ativos no cache distribuído. Os aplicativos definem explicitamente a vida útil nos objetos em cache ou configuram uma vida útil padrão para o contêiner do cache. Equilibre a opção de vida útil entre a disponibilidade (acertos de cache) vs. pressão de memória e deterioração dos dados.

  • Os caches apresentam uma semântica key->byte[]; esteja ciente do potencial para gravações sobrepostas para criar dados inconsistentes no cache. Os caches distribuídos, normalmente, não fornecem uma API para atualizações atômicas de dados armazenados, pois eles não estão cientes da estrutura dos dados armazenados.

    • Para aplicativos que exigem consistência estrita de gravações simultâneas, use uma plataforma de cache de distribuição que forneça um mecanismo de bloqueio para atualização de entidades. No caso do Azure Caching, isso pode ser implementado via GetAndLock/ PutAndUnlock. Observação: isso terá um impacto negativo na taxa de transferência.

  • O desempenho do cache é associado na camada do aplicativo pelo tempo exigido para serializar e desserializar objetos. Para otimizar esse processo, utilize um serializador binário altamente eficiente e relativamente simétrico (mesmo tempo exigido para codificar/decodificar dados), como o protobuf.

    • Para usar a serialização personalizada com êxito, faça design de DTOs (objetos de transferências de dados) para serialização no cache, use anotações apropriadas para serialização, evite dependências cíclicas e utilize testes de unidade para rastrear a serialização eficiente.

Por padrão, as conexões entre as camadas de serviço (incluindo conexões de entrada por meio do balanceador de carga) estão sujeitas a um esquema de alocação round robin, com fixação de conexão limitada. O diagrama a seguir ilustra a malha de conexão típica que resulta entre camadas e serviços externos (o lado esquerdo ilustra um aplicativo típico apenas de camada da Web). Embora essa malha não apresente nenhum problema de desempenho substancial para protocolos de conexão lightweight (como HTTP), determinadas conexões são caras para conectar/inicializar ou são um recurso controlado (limitado). Por exemplo, as conexões do Banco de dados SQL pertencem a essa categoria. Para otimizar o uso desses serviços e componentes externos, é altamente recomendável relacionar chamadas de recurso em instâncias específicas.

Afinidade de conexão

No diagrama acima, a topologia à direita tem camadas de trabalho e Web separadas (funções) dentro do mesmo serviço hospedado. Essa topologia também implementou afinidade entre as camadas da Web e do aplicativo para fixar chamadas das instâncias de aplicativo específicas a bancos de dados específicos. Por exemplo, para solicitar dados do Banco de Dados Um (DB1), as instâncias da Web devem solicitar os dados via instâncias do aplicativo Um ou Dois. Como, atualmente, o balanceador de carga do Azure apenas implementa uma técnica round robin, o fornecimento de afinidade no seu aplicativo exige design e implementação cuidadosos.

  • Faça o design do aplicativo com camadas da Web e do aplicativo separadas, fornecendo afinidade habilitada para recursos ou partição entre a camada da Web e do aplicativo.

  • Implemente a lógica de roteamento que roteia de modo transparente as chamadas intrasserviço para uma instância do aplicativo de destino. Use o conhecimento do mecanismo de particionamento usado pelos recursos externos ou de downstream (como o Banco de dados SQL).

A implementação prática dessa arquitetura de várias camadas requer comunicação de serviço altamente eficiente entre as camadas do aplicativo e da Web que usam protocolos lightweight e eficientes.

As técnicas de desenvolvimento de um aplicativo do Azure não são significativamente diferentes das técnicas de desenvolvimento do Windows Server. No entanto, a malha elástica realça a necessidade e o benefício de implantar o código eficiente que usa com mais eficácia os recursos de computação.

  • Suponha que todos os serviços, chamadas de rede e recursos dependentes são potencialmente não confiáveis e suscetíveis aos modos de falha transitórios e contínuos (por exemplo, como implementar lógica de repetição para o Banco de dados SQL será abordado abaixo neste tópico):

    • Implemente políticas apropriadas de repetição em todas as chamadas de serviço (Banco de dados SQL, armazenamento, etc.) para manipular condições de falha e perda de conectividade transitórias.

    • Implemente políticas de retirada na lógica de repetição para evitar efeitos de "comboio" (repetições que se empilham no serviço que prolonga interrupções).

    • Implemente a telemetria sofisticada do cliente para registrar mensagens de erro e eventos de falha em log com informações contextuais (serviço de destino, contexto de usuário/conta, atividade, etc.).

  • Não crie threads diretamente para agendar o trabalho; em vez disso, utilize uma estrutura de programação e simultaneidade como a Biblioteca Paralela de Tarefas do .NET. Os threads são objetos relativamente pesados e não são comuns para criação e descarte. Os agendadores que trabalham em um pool de threads compartilhado podem agendar e executar o trabalho de maneira mais eficiente. Essa arquitetura também fornece um semântico de alto nível para descrever a continuação e o tratamento de erros.

  • Otimize os DTOs (objetos de transferência de dados) para serialização e transmissão de rede. Dada a natureza altamente distribuída dos aplicativos do Azure, a escalabilidade é associada pelo nível de eficiência com que os componentes individuais do sistema podem se comunicar pela rede. Todos os dados transmitidos pela rede para comunicação ou armazenamento devem implementar a serialização de texto JSON ou um formato binário mais eficiente com dicas apropriadas para minimizar a quantidade de metadados transferidos pela rede (como nomes de campo mais curtos "no fio").

    • Se a interoperação for importante, use um formato textual eficiente, como JSON, para interoperabilidade e metadados na faixa.

    • Se uma taxa de transferência mais alta for importante, como a comunicação entre serviços, na qual é possível controlar ambas as extremidades, considere um formato binário em pacote altamente eficiente, como bson ou protobuf.

      • Evite a transferência de dados "verborrágica" (frequente) de objetos pequenos. A comunicação verborrágica entre serviços desperdiça recursos importantes do sistema nas tarefas de sobrecarga e é suscetível a respostas de latência variável.

      • Os testes para serializar e desserializar objetos devem ser um componente principal da sua estrutura de teste automatizada. O teste de funcionalidade garante que as classes de dados sejam serializáveis e que não haja dependências cíclicas. O teste de desempenho verifica os tempos de latência e os tamanhos de codificação necessários.

  • Onde for prático, aproveite estruturas leves para comunicação entre componentes e serviços. Muitas tecnologias tradicionais na pilha .NET fornecem um conjunto completo de recursos que pode não estar alinhado com a natureza distribuída do Azure. Os componentes que fornecem um nível alto de abstração entre a intenção e a execução, frequentemente, trazem um custo de alto desempenho.

    • Onde a interoperação de protocolo ou o suporte ao protocolo avançado não formem necessários, investigue usando a API da Web do ASP.NET, em vez do WCF para implementar serviços Web.

    • Onde os recursos sofisticados do Entity Framework não forem necessários, investigue usando um micro-ORM, como Dapper, para implementar a camada do cliente SQL.

  • Reduza a quantidade de dados fornecidos fora do data center habilitando a compactação HTTP no IIS para dados de saída.

  • Relacione conexões entre camadas para reduzir a tagarelice e a alteração das conexões de contexto.

  • Para reduzir a carga no aplicativo, use o armazenamento de blob para atender ao conteúdo estático maior (> 100 KB).

  • Para reduzir a carga no aplicativo, use a CDN (Rede de Distribuição de Conteúdo) por meio do armazenamento de blob para atender ao conteúdo estático, como imagens ou CSS.

  • Evite usar o Banco de dados SQL para dados da sessão. Em vez disso, use o cache distribuído ou cookies.

O Armazenamento do Azure é o backbone de dados durável em um aplicativo do Azure. Embora forneçam uma experiência imediata altamente confiável e escalonável, os aplicativos de larga escala exigem diretrizes apropriadas de design e uso.

  • Utilize várias contas de armazenamento para obter maior escalabilidade, seja para aumento de tamanho (> 100 TB) ou para mais taxa de transferência (> 5.000 operações por segundo). Assegure-se de que o código do aplicativo pode ser configurado para usar várias contas de armazenamento, com funções de particionamento apropriadas para rotear o trabalho para as contas de armazenamento. Faça o design da capacidade de adicionar mais contas de armazenamento como uma alteração de configuração, e não como uma alteração de código.

  • Selecione cuidadosamente as funções de particionamento do armazenamento de tabela para habilitar a escala desejada em termos de desempenho de inserção e consulta. Examine a abordagem de particionamento baseada no tempo para dados de telemetria, com chaves compostas que se baseiam em dados de linha para dados não temporais. Mantenha as partições em um intervalo apropriado para excelente desempenho; partições muito pequenas limitam a capacidade de executar operações em lote (incluindo consultas), enquanto a consulta nas partições muito grandes são caras (e podem afunilar em inserções simultâneas de alto volume).

    • A escolha da função de particionamento também terá um impacto fundamental no desempenho da consulta; o armazenamento de tabela fornece pesquisa eficiente por {chave de partição, chave de linha}, com processamento menos eficiente de {chave de partição, linha compatível com filtro) e {chave de partição compatível com filtro, chave de linha compatível com filtro}. As consultas exigem uma verificação de tabela global ({chave de linha compatível com filtro}).

    • As partições podem ser tão pequenas quanto uma única entidade; isso fornece desempenho altamente otimizado para cargas de trabalho de pesquisa pura, como gerenciamento de carrinhos de compras.

  • Quando possível, envie as operações em lote ao armazenamento. As gravações na tabela devem ser colocadas em lote, geralmente pelo uso do método SaveChanges na API do cliente .NET. Insira uma série de linhas em uma tabela e, em seguida, confirme as alterações em único lote com o método SaveChanges. As atualizações no armazenamento de blob também devem ser confirmadas em lote, usando o método PutBlockList. Assim como na API do armazenamento de tabela, chame PutBlockList em um intervalo de blocos, e não o bloco individual.

  • Escolha nomes de coluna curtos para as propriedades da tabela, pois os metadados (nomes da propriedade) são armazenados na faixa. Os nomes de coluna também são considerados para o tamanho máximo da linha de 1 MB. Os nomes de propriedade excessivamente longos são desperdiçadores de recursos do sistema.

Conforme abordado na seção Explorando o Azure, o Banco de dados SQL fornece o recurso de banco de dados relacional como serviço pronto para uso, permitindo acesso escalonável ao armazenamento de dados no modo de expansão. O uso bem-sucedido do Banco de dados SQL em aplicativos de larga escala exige várias opções criteriosas de design e implementação; os principais pontos de design e as práticas recomendadas são abordados nesta seção.

Muitos aplicativos exigem uma tabela de metadados para armazenar detalhes, como roteamento, particionamento e informações de locatário. O armazenamento desses metadados em um único banco de dados cria um ponto único de falha, bem como um afunilamento de escalabilidade. Os repositórios de metadados centrais devem ser expandidos por meio de uma combinação de:

  • Cache agressivo. As informações no banco de dados de configuração devem ser armazenadas agressivamente em um cache distribuído (como o memcached ou o Azure Caching).

    • Tenha cuidado com o efeito da tentativa de pré-carregar o cache de forma agressiva de vários threads de trabalho na inicialização do aplicativo que, normalmente, leva à carga excessiva e à limitação do banco de dados. Se seu aplicativo exige um cache pré-carregado, delegue a responsabilidade do carregamento de dados a uma função de trabalho dedicada (ou à tarefa agendada) com uma taxa de carregamento configurável.

    • Se o desempenho ou a confiabilidade do aplicativo dependerem de um determinado segmento de dados disponível no cache, seu aplicativo deverá recusar as solicitações de entrada até que o cache tenha sido pré-populado. Até que os dados sejam populados, o aplicativo deverá retornar um código ou uma mensagem de erro adequada.

  • Expansão. Particione os dados vertical (por tabela) ou horizontalmente (tabela de segmento por vários fragmentos) para distribuir a carga entre vários bancos de dados.

A escalabilidade geral de um Banco de dados SQL particionado é associada pela escala de um banco de dados individual (fragmento) e pelo nível de eficiência e efetividade com que esses fragmentos podem ser compostos em conjunto para aumento da escala:

  • Como os limites de log de transações restringem transações maiores, como a recriação de índices, as tabelas individuais não devem exceder, a grosso modo, 10 GB (observe que esse limite prático depende do tamanho dos índices da tabela de destino, de modo que elas podem ter mais ou menos 10 GB para seu banco de dados). Para tabelas grandes individuais, divida a tabela em tabelas individuais menores e use uma exibição particionada para fornecer uma sobreposição uniforme.

    • Manter pequenas as tabelas individuais reduz o impacto de uma alteração no esquema ou da recriação de um índice durante uma atualização em fases. As alterações em várias tabelas menores minimiza o tempo de inatividade e a latência devido às operações de bloqueio.

    • Esse particionamento complica as técnicas de gerenciamento. As operações, como a recriação de índice, devem ser executadas de maneira iterativa em todas as tabelas de componente.

  • Mantenha bancos de dados (fragmentos) individuais razoavelmente pequenos. As operações de continuidade, como Cópia ou exportações de BD em bancos de dados com mais de 50 GB, podem levar horas para serem concluídas (o serviço cancela operações que são executadas por mais de 24 horas).

Em um mundo de fornecimento de serviços contínuo, gerenciar atualizações de banco de dados distribuídos ou modificações de esquema exige devido cuidado e atenção por um caminho suave de atualização. Todas as práticas recomendadas tradicionais para controle de esquema e operações de metadados em um repositório de dados de produção se tornaram mais importantes do que nunca. Por exemplo, é uma tarefa muito mais complexa depurar e resolver um procedimento armazenado acidentalmente interrompido em 1 dentre 100 banco de dados.

Como as atualizações de esquema e as modificações de dados não são consistentes de modo transacional pelos fragmentos, as atualizações de aplicativo devem ser compatíveis com esquemas novos e antigos durante o período de transição. Geralmente, esse requisito significa que cada versão do aplicativo deve ser, pelo menos, compatível com a versão atual e a versão anterior do esquema.

Mudar para uma coleta de expansão de bancos de dados cria desafios em torno do gerenciamento de conexão. Cada conexão de banco de dados SQL é um recurso relativamente caro, evidenciado pelo amplo uso do pool de conexão em APIs de cliente (ADO.NET, ODBC, PHP, etc.). Em vez de cada instância de aplicativo manter várias conexões com um SQL Server central, cada instância de aplicativo deve potencialmente manter conexões com vários servidores de banco de dados.

Com conexões como um recurso caro e potencialmente escasso, os aplicativos devem gerenciar conexões corretamente retornando conexões em pool em tempo hábil. O código do aplicativo deve usar a eliminação de conexão automática; no .NET, a prática recomendada é encapsular todo o uso do SqlConnection em uma instrução using, como:

using (var conn = new SqlConnection(connStr))
{
    // SQL client calls here
}

Como descrito anteriormente, as conexões em um Banco de dados SQL estão sujeitas a falhas de conexão transitórias. A lógica de repetição deve ser usada em todos os comandos e conexões para proteger contra essas falhas transitórias (veja abaixo detalhes adicionais).

O Banco de dados SQL oferece suporte apenas a conexões TCP (não a pipes nomeados) e é altamente recomendável criptografar a conexão entre o código do aplicativo e o Banco de Dados SQL. Para impedir tentativas de conexão acidentais (como a tentativa de usar pipes nomeados), os aplicativos devem formatar suas cadeias de conexão SQL como se segue:

Server=tcp:{servername}.database.windows.net,1433;Database={database};User ID={userid}@{database};Password={password};Trusted_Connection=False;Encrypt=True;Connection Timeout=30;

Para implantações de aplicativo em larga escala, o número padrão de conexões potenciais pode aumentar exponencialmente entre uma implantação de serviço hospedado e os servidores lógicos do Banco de dados SQL em um cluster do Banco de Dados SQL (cada um deles tem um único endereço IP externo). Tome como exemplo um serviço hospedado com 100 instâncias, 50 bancos de dados e o número padrão de conexões, que são 100 conexões por padrão no ADO.NET.

MaxConnections=DatabaseCount*Instance Count*MaxConnectionPoolSize

Consulte a topologia de rede de um serviço hospedado; cada lado da conexão (o serviço hospedado e os servidores lógicos do Banco de dados SQL) reside atrás de um balanceador de carga do Azure. . Cada balanceador de carga do Azure tem um limite superior de 64 mil conexões entre qualquer um dos dois endereços IPv4. A combinação dessa topologia de rede com o número padrão de conexões disponíveis resulta em falhas severas de rede para aplicativos maiores.

  • Implante aplicativos maiores por vários serviços hospedados.

  • Implante bancos de dados em várias assinaturas (não apenas vários servidores lógicos na mesma assinatura) para ganhar mais endereços IP exclusivos.

  • Implemente aplicativos de multicamadas para relacionar operações de saída para uma instância do aplicativo de destino (consulte a seção anterior em serviços hospedados).

  • Lembre-se de que os pools de conexão são mantidos por cadeia de conexão exclusiva e correspondem a cada servidor de banco de dados, banco de dados e combinação de logon exclusivos. Use o pool de conexão de cliente SQL e limite explicitamente o tamanho máximo do pool de conexões do SQL. As bibliotecas de cliente do SQL reutilizam conexões conforme o necessário; o impacto de um pool de conexão pequeno é o potencial para o aumento de latência enquanto os aplicativos aguardam conexões para se tornarem disponíveis.

A lista a seguir fornece recomendações para reduzir o número de conexões ativas necessárias:

O deslocamento para um modelo de dados distribuído pode exigir alterações no projeto do esquema de banco de dados, assim como modificações a determinados tipos de consultas. Os aplicativos que exigem o uso de transações distribuídas em vários bancos de dados potencialmente têm um modelo de dados ou uma implementação inadequada (por exemplo, tentando impor a consistência global). Esses projetos devem ser refatorados.

O uso de uma instalação central de geração de sequência deve ser evitado para qualquer aspecto não trivial do aplicativo, devido às restrições de disponibilidade e escalabilidade. Muitos aplicativos aproveitam sequências para fornecer identificadores globais exclusivos, usando um mecanismo central de rastreamento para incrementar a sequência sob demanda. Essa arquitetura cria um ponto de contenção global e um afunilamento com o qual cada componente do sistema precisa interagir. Esse afunilamento é especialmente problemático para aplicativos móveis potencialmente desconectados.

Em vez disso, os aplicativos devem usar as funções que podem gerar identificadores globais exclusivos, como GUIDs, em um sistema distribuído. Por design, GUIDs não são sequenciais e, portanto, podem causar fragmentação quando são usados como um CLUSTERED INDEX em uma tabela grande. Para reduzir o impacto da fragmentação de GUIDs em um modelo de dados grandes, fragmente o banco de dados, mantendo os fragmentos individuais relativamente pequenos. Isso permite que o Banco de dados SQL desfragmente seus bancos de dados automaticamente durante o failover da réplica.

O código do aplicativo cliente deve considerar vários aspectos de entregar um modelo de dados distribuído:

  • Chaves da partição. As chaves da partição devem fazer parte de todas as classes ou modelos de dados e potencialmente ser decoradas com um atributo para permitir a descoberta da chave de partição.

  • Telemetria. A camada de acesso a dados deve automaticamente registrar informações sobre cada chamada do SQL, inclusive o destino, a partição, o contexto, a latência e os códigos de erro ou repetições

  • Consultas distribuídas. Executar consultas entre fragmentos apresenta vários novos desafios, incluindo o roteamento, a seleção de partição e o conceito de êxito parcial (alguns fragmentos individuais conseguem retornar dados, enquanto outros não). A camada de acesso a dados deve fornecer delegados para executar consultas distribuídas de uma maneira de dispersão e coleta assíncrona (paralela), retornando um resultado composto. As consultas distribuídas também precisam considerar a restrição de recursos subjacentes:

    • O grau máximo de paralelismo (para impedir a pressão excessiva de thread e conexão).

    • O tempo máximo de consulta (para reduzir a ocorrência geral de latência de uma consulta longa ou de um fragmento lento).

Também há várias tarefas de gerenciamento em andamento que devem ser realizadas:

  • Tabelas de referência. Sem um espaço de consulta coerente globalmente, os dados de referência para junções em consultas devem ser copiados para cada fragmento individual. É necessário manter e replicar dados para as tabelas de referência em fragmentos individuais para fornecer dados de referência locais bem consistentes.

  • Rebalanceando partições. As partições individuais podem se tornar desbalanceadas, consumindo muitos recursos e tornando-se um gargalo, ou apresentando utilização insuficiente e desperdiçando recursos. Nessas situações, as partições devem ser rebalanceadas para realocar recursos. O mecanismo de rebalanceamento é altamente dependente da estratégia de particionamento e da implementação. Na maioria dos cenários, o rebalanceamento costuma envolver:

    • Copiar os dados existentes em um fragmento para um ou mais novos fragmentos, por meio do mecanismo de divisão/mesclagem (para particionamento baseado em intervalos) ou de uma cópia de nível de entidade e remapeamento (para o particionamento baseado em pesquisa).

    • Atualizar o mapa do fragmento para apontar para o novo fragmento e, em seguida, compensar os dados gravados em fragmentos antigos durante a transição.

  • Diminuição de dados. À medida que um aplicativo cresce e coleta dados, considere a diminuição periódica de dados não usados mais antigos para aumentar a reserva dinâmica e a capacidade disponíveis no sistema primário. Os dados diminuídos (excluídos) não são excluídos de forma síncrona do Banco de dados SQL; em vez disso, são sinalizados para exclusão e limpos por um processo em segundo plano. Um método comum de sinalizar dados para exclusão é uma coluna de sinalizador que pode sinalizar uma linha como ativa, inativa ou sinalizada para exclusão. Isso permite mascarar os dados das consultas por um período de tempo, permitindo que os dados voltem com facilidade para produção se os usuários indicarem que os dados ainda são necessários.

    Excluir dados também pode disparar a fragmentação, exigindo potencialmente uma recriação de índice para operações de consulta eficientes. Os dados antigos podem ser arquivados para:

    • Um repositório online (Banco de dados SQL secundário); isso aumenta a reserva dinâmica no sistema primário, mas não reduz o custo.

    • Um repositório offline, como um arquivo bcp ou bacpac no armazenamento de blob; isso aumenta a reserva dinâmica no sistema primário e reduz o custo.

    • O bloco de bits. Você pode escolher excluir dados permanentemente do sistema primário para aumentar a reserva dinâmica.

Várias ocorrências comuns no Banco de dados SQL podem disparar falhas transitórias de conexão, como o failover da réplica. Os aplicativos devem implementar o código apropriado para tratar falhas transitórias e responder corretamente ao esgotamento e à limitação de recursos:

  • Tratamento de falha transitória de conexão com repetição. O código de acesso a dados deve usar um mecanismo de repetição orientado por política para compensar falhas transitórias de conexão. O mecanismo de repetição deve detectar falhas transitórias de conexão, reconectá-las ao Banco de dados SQL de destino e emitir o comando novamente.

  • Tratamento de limitação com repetição e lógica de retirada. O código de acesso a dados deve utilizar uma repetição orientada por política e um mecanismo de retirada para lidar com condições de limitação. O mecanismo de repetição deve detectar a limitação e gradualmente retirar as tentativas de emitir o comando novamente (para evitar efeitos de comboio que poderiam prolongar a condição de limitação).

    • O código de acesso a dados também deve implementar a capacidade de retirar para um repositório de dados, como o armazenamento de blob. Esse repositório alternativo fornece um mecanismo durável para capturar atividades, dados e estado, impedindo a perda de dados no caso de uma limitação prolongada ou evento de disponibilidade.

Os aplicativos .NET podem utilizar estruturas de lógica de retirada e repetição com reconhecimento de Banco de dados SQL, como, por exemplo, a Estrutura de aplicativo de nuvem (CloudFx) ou o Manipulador de falha transitória do Enterprise Library. Essas estruturas fornecem wrappers para classes comuns de acesso a dados (como SqlConnection e SqlCommand), bem como políticas que podem ser invocadas diretamente.

var retryPolicy = RetryPolicy.Create<SqlAzureTransientErrorDetectionStrategy>(
    retryCount: 3, 
    initialInterval: TimeSpan.FromSeconds(5),
    increment: TimeSpan.FromSeconds(2));
                
using (var conn = new ReliableSqlConnection(connStr))
{
    conn.Open();
    using (var cmd = conn.CreateCommand())
    {

    }
    conn.Close();
}

O trecho de código anterior demonstra o uso da classe ReliableSqlConnection do CloudFx para tratar erros transitórios de conexão ao trabalhar no Banco de dados SQL.

Tenha cuidado e atenção específica para registrar em log as chamadas de API para os serviços, como o Banco de dados SQL, com modos de falha potencialmente complicados. Tente capturar informações importantes de contexto e desempenho. Por exemplo, todas as sessões do Banco de dados SQL levam um identificador de sessão, que pode ser usado em chamadas de suporte para ajudar diretamente a isolar o problema subjacente. É altamente recomendado que todas as chamadas, comandos e consultas no Banco de dados SQL registrem:

  • Nome do servidor e banco de dados. Com potencialmente centenas de bancos de dados, o servidor de destino é crítico para rastrear e isolar problemas.

  • Conforme for apropriado, o procedimento armazenado do SQL ou o texto do comando. Tenha cuidado para não vazar informações confidenciais no arquivo de log – em geral, evite registrar em log o texto do comando.

  • A latência de ponta a ponta da chamada. Encapsule a chamada em um representante de tempo usando cronômetro ou outro temporizador leve.

  • O código de resultado da chamada (sucesso ou falha) bem como o número de repetições e a causa da falha (conexão descartada, limitada e assim por diante).

  • A ID de sessão da conexão, acessível através da propriedade CONTEXT_INFO() (ou a propriedade SessionTracingId se estiver usando ReliableSqlConnection). No entanto, não recupere a propriedade da ID de sessão de CONTEXT_INFO() em todas as chamadas de cliente, porque esse comando dispara outra viagem de ida e volta ao servidor.

Para facilitar o uso mais eficiente do Banco de dados SQL, as consultas e as inserções de dados no Banco de dados SQL devem ser processadas em lotes e executadas de forma assíncrona quando for possível. Isso não apenas melhora a eficiência do aplicativo, mas reduz a carga total do sistema no Banco de dados SQL (permitindo mais produtividade).

  • Inserção em lotes. Para operações contínuas de inserção de dados, como, por exemplo, registrar novos usuários, os dados devem ser processados em lotes e alinhados com os fragmentos de destino. Esses lotes devem ser gravados periodicamente (de forma assíncrona) no Banco de dados SQL com base nos gatilhos, como uma janela de tempo ou tamanho de lote de destino. Para tamanhos de lote até 100 linhas, as funções com valor de tabela são normalmente mais eficientes do que uma operação de cópia em massa.

  • Evite interfaces verborrágicas. Reduza o número de viagens de ida e volta necessárias para o banco de dados executar uma consulta ou um conjunto de operações. As interfaces verborrágicas têm um alto nível de sobrecarga, o que aumenta a carga no sistema, reduzindo a produtividade e a eficiência. Tente mesclar as operações relacionadas, normalmente usando um procedimento armazenado para reduzir as viagens de ida e volta.

Como parte de uma abordagem total para a continuidade de negócios, os dados armazenados no Banco de dados SQL devem ser periodicamente exportados para o armazenamento de blob. O armazenamento de blob dá suporte à disponibilidade e à replicação geográfica por padrão.

Implemente uma tarefa agendada que periodicamente exporte os bancos de dados em armazenamento de blob. Use uma conta de armazenamento dedicada. Essa tarefa deve ser executada no mesmo data center que os bancos de dados de destino, não em áreas de trabalho ou servidores locais.

Agende a tarefa de exportação para tempos de baixa atividade para minimizar o impacto na experiência do usuário final. Ao exportar vários bancos de dados, limite o grau de exportação paralela para reduzir o impacto no sistema.

Conhecer em profundidade a integridade, o desempenho e a reserva dinâmica de seus Bancos de dados SQL é um componente crítico da prestação de serviços em geral. O Banco de dados SQL fornece as informações não processadas necessárias por meio das exibições de gerenciamento dinâmico, mas não há no momento uma infraestrutura preparada para capturar, analisar e relatar a métrica chave. Para entregar esse recurso para o Banco de dados SQL, considere as seguintes práticas:

  • Implementar uma tarefa periódica para coletar os dados de desempenho chave relacionados à carga do sistema, aos recursos usados (threads de trabalho, espaço de armazenamento) e aos dados em um repositório comum. Por exemplo, considere as tabelas usadas pelo Diagnóstico do Azure. Essa tarefa deve coletar dados de todos os bancos de dados que pertencem ao seu aplicativo. Essa coleta normalmente ocorre de uma maneira dimensionada (reunindo dados de vários bancos de dados simultaneamente.)

  • Implementar uma tarefa periódica para agregar essas informações para criar indicadores chave de desempenho da integridade e da capacidade de seus bancos de dados implantados.

O Diagnóstico do Azure fornece uma linha de base para coletar a telemetria do nível do aplicativo e da instância. Embora forneça uma perspectiva em aplicativos em grande escala que são executados no Azure, ele exige uma configuração cuidadosa e o gerenciamento de fluxos de dados. Diferentemente dos aplicativos centralizados de expansão que podem aproveitar utilitários de diagnóstico avançados do Windows, o diagnóstico em um sistema de expansão distribuído deve ser implementado antes de o sistema ficar ativo –não pode ser feito posteriormente.

O tratamento de erro, o rastreamento contextual e a captura de telemetria são essenciais para fornecer uma compreensão de eventos de erro, causas raiz e resoluções.

  • Não publique dados de site dinâmico e telemetria na mesma conta de armazenamento. Use uma conta de armazenamento dedicada para diagnóstico.

  • Crie canais separados para telemetria volumosa (alto volume, alta latência, dados granulares) e verborrágica (baixo volume, baixa latência, dados de alto valor).

    • Use fontes padrão de Diagnóstico do Azure, como contadores de desempenho e rastreamentos, para informações verborrágicas.

    • Use as bibliotecas de log comum, como a Biblioteca da Estrutura de Aplicativo Empresarial, o log4net ou o NLog, para implementar o log em massa em arquivos locais. Use uma fonte de dados personalizada na configuração do monitor de diagnóstico para copiar periodicamente essas informações para o armazenamento de blob.

  • Registre em log todas as chamadas de API para os serviços externos com informações de contexto, destino, método e tempo (latência) e o resultado (êxito/reprovado/repetições). Use o canal robusto de log para evitar sobrecarregar o sistema de diagnóstico com as informações de telemetria.

  • Os dados gravados no armazenamento da tabela (contadores de desempenho, logs de eventos, eventos de rastreamento) são gravados em uma partição temporal com 60 segundos de largura. Tentar gravar dados demais (origens de ponto demais, intervalo de coleta baixo demais) pode sobrecarregar esta partição. Verifique se os picos de erro não disparam uma tentativa de inserção de alto volume no armazenamento da tabela, porque isto pode disparar um evento de limitação.

    • Escolha dados de alto valor para coletar dessas fontes, incluindo contadores chave de desempenho, eventos críticos/erro ou registros de rastreamento.

    • Escolha um intervalo apropriado de coleta (5 – 15 minutos) para reduzir a quantidade de dados que devem ser transferidos e analisados.

  • Verifique se a configuração de registro pode ser modificada em tempo de execução sem impor redefinições de instância. Verifique também se a configuração é suficientemente granular para habilitar o registro em log para aspectos específicos do sistema, como o banco de dados, o cache ou outros serviços.

O Diagnóstico do Azure não fornecem a coleta de dados para serviços dependentes, como o Banco de dados SQL ou um cache distribuído. Para fornecer uma visão abrangente do aplicativo e de suas características de desempenho, adicione a infraestrutura para coletar dados para serviços dependentes:

  • Colete os principais dados de utilização e de desempenho dos serviços dependentes, e publique-os no repositório de WAD como registros do contador de desempenho.

    • Armazenamento do Azure por meio da Análise de Armazenamento do Azure.

    • O Banco de dados SQL por meio das exibições de gerenciamento dinâmico.

    • Cache distribuído por meio dos contadores de desempenho ou APIs de monitoramento de integridade.

  • Analise periodicamente os dados brutos de telemetria para criar agregações e rollups (como uma tarefa agendada).

Mostrar: