Conceber uma estratégia de criação de partições dimensionável para o armazenamento de Tabelas do Azure

Este artigo aborda a criação de partições de uma tabela no armazenamento de Tabelas do Azure e as estratégias que pode utilizar para garantir uma escalabilidade eficiente.

O Azure fornece armazenamento na cloud altamente disponível e altamente dimensionável. O sistema de armazenamento subjacente para o Azure é fornecido através de um conjunto de serviços, incluindo o armazenamento de Blobs do Azure, o armazenamento de Tabelas do Azure, o armazenamento de Filas do Azure e Ficheiros do Azure.

O armazenamento de Tabelas do Azure foi concebido para armazenar dados estruturados. O serviço de Armazenamento do Azure suporta um número ilimitado de tabelas. Cada tabela pode ser dimensionada para níveis maciços e fornecer terabytes de armazenamento físico. Para tirar melhor partido das tabelas, tem de particionar os seus dados de forma ideal. Este artigo explora estratégias que pode utilizar para criar partições eficientes de dados para o armazenamento de Tabelas do Azure.

Entidades de tabela

As entidades de tabela representam as unidades de dados que são armazenadas numa tabela. As entidades de tabela são semelhantes às linhas numa tabela de base de dados relacional típica. Cada entidade define uma coleção de propriedades. Cada propriedade é definida como um par chave/valor pelo respetivo nome, valor e tipo de dados do valor. As entidades têm de definir as seguintes três propriedades do sistema como parte da coleção de propriedades:

  • PartitionKey: a propriedade PartitionKey armazena valores de cadeia que identificam a partição à qual uma entidade pertence. As partições, como discutimos mais tarde, são parte integrante da escalabilidade da tabela. As entidades que têm o mesmo valor PartitionKey são armazenadas na mesma partição.

  • RowKey: a propriedade RowKey armazena valores de cadeia que identificam exclusivamente entidades em cada partição. A PartitionKey e a RowKey juntos formam a chave primária para a entidade.

  • Carimbo de data/hora: a propriedade Carimbo de Data/Hora fornece rastreabilidade para uma entidade. Um carimbo de data/hora é um valor de data/hora que indica a última vez que a entidade foi modificada. Por vezes, um carimbo de data/hora é referido como a versão da entidade. As modificações aos carimbos de data/hora são ignoradas porque o serviço de tabelas mantém o valor desta propriedade durante todas as operações de inserção e atualização.

Chave primária da tabela

A chave primária de uma entidade do Azure consiste nas propriedades combinadas PartitionKey e RowKey . As duas propriedades formam um único índice agrupado na tabela. As propriedades PartitionKey e RowKey podem armazenar até 1 KiB de valores de cadeia. As cadeias vazias também são permitidas; no entanto, não são permitidos valores nulos.

O índice agrupado ordena por PartitionKey por ordem ascendente e, em seguida, por RowKey por ordem ascendente. A sequência de ordenação é observada em todas as respostas de consulta. As comparações lexical são utilizadas durante a operação de ordenação. É apresentado um valor de cadeia de "111" antes de um valor de cadeia de carateres de "2". Em alguns casos, poderá querer que a sequência de ordenação seja numérica. Para ordenar por ordem numérica e ascendente, tem de utilizar cadeias de carateres de comprimento fixo e sem almofadas. No exemplo anterior, "002" é apresentado antes de "111".

Partições de tabela

As partições representam uma coleção de entidades com os mesmos valores PartitionKey . As partições são sempre servidas a partir de um servidor de partições. Cada servidor de partições pode servir uma ou mais partições. Um servidor de partições tem um limite de taxa do número de entidades que pode servir de uma partição ao longo do tempo. Especificamente, uma partição tem um destino de escalabilidade de 2000 entidades por segundo. Este débito pode ser superior durante a carga mínima no nó de armazenamento, mas é limitado quando o nó fica quente ou ativo.

Para ilustrar melhor o conceito de criação de partições, a figura seguinte mostra uma tabela que contém um pequeno subconjunto de dados para registos de eventos de corrida a pé. A figura apresenta uma vista conceptual da criação de partições onde a PartitionKey contém três valores diferentes: o nome do evento combinado com três distâncias (maratona completa, meia maratona e 10 km). Este exemplo utiliza dois servidores de partição. O Servidor A contém registos para a meia maratona e distâncias de 10 km. O servidor B contém apenas as distâncias completas da maratona. Os valores RowKey são mostrados para fornecer contexto, mas os valores não são significativos para este exemplo.

Um diagrama que mostra uma tabela com três partições
Uma tabela com três partições

Escalabilidade

Uma vez que uma partição é sempre servida a partir de um único servidor de partição e cada servidor de partição pode servir uma ou mais partições, a eficiência de servir entidades está correlacionada com o estado de funcionamento do servidor. Os servidores que se deparam com tráfego elevado para as suas partições poderão não conseguir suportar um débito elevado. Por exemplo, na figura anterior, se forem recebidos muitos pedidos de "Marathon__Half de Nova Iorque de 2011", o servidor A poderá tornar-se demasiado frequente. Para aumentar o débito do servidor, o sistema de armazenamento balancea as partições para outros servidores. O resultado é que o tráfego é distribuído por muitos outros servidores. Para um balanceamento de carga ideal do tráfego, deve utilizar mais partições para que o armazenamento de Tabelas do Azure possa distribuir as partições por mais servidores de partição.

Transações de grupo de entidades

Uma transação de grupo de entidades é um conjunto de operações de armazenamento que são implementadas atomicamente em entidades que têm o mesmo valor PartitionKey . Se alguma operação de armazenamento no grupo de entidades falhar, todas as operações de armazenamento na entidade serão revertidas. Uma transação de grupo de entidades não consiste em mais de 100 operações de armazenamento e pode não ter mais de 4 MiB de tamanho. As transações de grupo de entidades fornecem armazenamento de Tabelas do Azure com uma forma limitada da semântica atomicidade, consistência, isolamento e durabilidade (ACID) fornecida pelas bases de dados relacionais.

As transações de grupos de entidades melhoram o débito porque reduzem o número de operações de armazenamento individuais que têm de ser submetidas para o armazenamento de Tabelas do Azure. As transações de grupos de entidades também proporcionam um benefício económico. Uma transação de grupo de entidades é faturada como uma única operação de armazenamento, independentemente do número de operações de armazenamento que contém. Uma vez que todas as operações de armazenamento numa transação de grupo de entidades afetam entidades que têm o mesmo valor PartitionKey , a necessidade de utilizar transações de grupos de entidades pode impulsionar a seleção do valor PartitionKey .

Intervalo de partições

Se utilizar valores PartitionKey exclusivos para as suas entidades, cada entidade pertence à sua própria partição. Se os valores exclusivos que utiliza aumentarem ou diminuirem o valor, é possível que o Azure crie partições de intervalo. As partições de intervalo agrupam entidades que têm valores partitionKey sequenciais e exclusivos para melhorar o desempenho das consultas de intervalo. Sem partições de intervalo, uma consulta de intervalo tem de ultrapassar os limites da partição ou os limites do servidor, o que pode diminuir o desempenho das consultas. Considere uma aplicação que utiliza a seguinte tabela, que tem um valor de sequência crescente para PartitionKey:

PartitionKey RowKey
"0001" -
"0002" -
"0003" -
"0004" -
"0005" -
"0006" -

O Azure pode agrupar as três primeiras entidades numa partição de intervalo. Se aplicar uma consulta de intervalo à tabela que utiliza a PartitionKey como critérios e pede entidades de "0001" a "0003", a consulta poderá ter um desempenho eficiente porque as entidades são servidas a partir de um único servidor de partição. Não há garantias de quando e como será criada uma partição de intervalo.

A existência de partições de intervalo para a sua tabela pode afetar o desempenho das operações de inserção se inserir entidades que tenham valores PartitionKey crescentes ou decrescentes. A inserção de entidades com valores PartitionKey crescentes é denominada padrão só de acréscimo. A inserção de entidades com valores decrescentes é denominada padrão só de pré-e-end. Considere não utilizar estes tipos de padrões porque o débito geral dos seus pedidos de inserção é limitado por um único servidor de partição. Isto acontece porque, se existirem partições de intervalo, as primeiras e últimas partições (intervalo) contêm os valores PartitionKey menores e maiores, respetivamente. Por conseguinte, a inserção de uma nova entidade, que tem um valor PartitionKey sequencialmente inferior ou superior, destina-se a uma das partições finais. A figura seguinte mostra um possível conjunto de partições de intervalo que se baseiam no exemplo anterior. Se fosse inserido um conjunto de entidades "0007", "0008" e "0009", seriam atribuídas à última partição (laranja).

Diagrama que mostra um conjunto de partições de intervalo
Um conjunto de partições de intervalo

É importante ter em atenção que não existe nenhum efeito negativo no desempenho se as operações de inserção utilizarem valores PartitionKey mais dispersos.

Analisar dados

Ao contrário de uma tabela numa base de dados relacional que pode utilizar para gerir índices, as tabelas no armazenamento de Tabelas do Azure só podem ter um índice. Um índice no armazenamento de Tabelas do Azure consiste sempre nas propriedades PartitionKey e RowKey .

Numa tabela do Azure, não tem o luxo de otimizar o desempenho da tabela ao adicionar mais índices ou ao alterar uma tabela existente depois de a implementar. Tem de analisar os dados à medida que cria a tabela. Os aspetos mais importantes a considerar para uma escalabilidade ideal e para eficiência de consulta e inserção são os valores PartitionKey e RowKey . Este artigo realça como escolher PartitionKey porque está diretamente relacionado com a forma como as tabelas são particionadas.

Dimensionamento de partições

O dimensionamento da partição refere-se ao número de entidades que uma partição contém. Como discutimos em Escalabilidade, ter mais partições significa que obtém um melhor balanceamento de carga. A granularidade do valor PartitionKey afeta o tamanho das partições. No nível mais grossário, se um único valor for utilizado como PartitionKey, todas as entidades estão numa única partição que é muito grande. No melhor nível de granularidade, a PartitionKey pode conter valores exclusivos para cada entidade. O resultado é que existe uma partição para cada entidade. A tabela seguinte mostra as vantagens e desvantagens do intervalo de granularidades:

Granularidade partitionKey Tamanho da partição Vantagens Desvantagens
Valor único Pequeno número de entidades As transações em lote são possíveis com qualquer entidade.

Todas as entidades são locais e são servidas a partir do mesmo nó de armazenamento.
Valor único Grande número de entidades As transações de grupos de entidades podem ser possíveis com qualquer entidade. Para obter mais informações sobre os limites das transações de grupos de entidades, veja Executar transações de grupos de entidades. O dimensionamento é limitado.

O débito está limitado ao desempenho de um único servidor.
Vários valores Várias partições

Os tamanhos das partições dependem da distribuição de entidades.
As transações em lote são possíveis em algumas entidades.

A criação de partições dinâmicas é possível.

As consultas de pedido único são possíveis (sem tokens de continuação).

É possível fazer o balanceamento de carga em mais servidores de partição.
Uma distribuição altamente desigual de entidades entre partições pode limitar o desempenho das partições maiores e mais ativas.
Valores exclusivos Muitas partições pequenas A tabela é altamente dimensionável.

As partições de intervalo podem melhorar o desempenho das consultas entre partições.
As consultas que envolvem intervalos podem exigir visitas a mais do que um servidor.

As transações em lote não são possíveis.

Os padrões apenas de acréscimo ou de pré-fim podem afetar o débito de inserção.

A tabela mostra como o dimensionamento é afetado pelos valores partitionKey . É uma melhor prática favorecer partições mais pequenas porque oferecem um melhor balanceamento de carga. As partições maiores podem ser adequadas em alguns cenários e não são necessariamente desfavorecidas. Por exemplo, se a sua aplicação não precisar de escalabilidade, uma única partição grande poderá ser adequada.

Determinar consultas

As consultas obtêm dados de tabelas. Quando analisa os dados de uma tabela no armazenamento de Tabelas do Azure, é importante considerar quais as consultas que a aplicação irá utilizar. Se uma aplicação tiver várias consultas, poderá ter de priorizá-las, embora as suas decisões possam ser subjetivas. Em muitos casos, as consultas dominantes são discerníveis a partir de outras consultas. Em termos de desempenho, as consultas enquadram-se em categorias diferentes. Uma vez que uma tabela tem apenas um índice, o desempenho das consultas está normalmente relacionado com as propriedades PartitionKey e RowKey . A tabela seguinte mostra os diferentes tipos de consultas e as respetivas classificações de desempenho:

Tipo de consulta Correspondência partitionKey Correspondência de RowKey Classificação de desempenho
Análise do intervalo de linhas Exact Parcial Melhor com partições de tamanho mais pequeno.

Incorreto com partições que são muito grandes.
Análise do intervalo de partições Parcial Parcial Bom com um pequeno número de servidores de partição a serem tocados.

Pior com mais servidores a serem tocados.
Análise completa da tabela Parcial, nenhum Parcial, nenhum Pior com um subconjunto de partições a serem analisadas.

Pior, com todas as partições a serem analisadas.

Nota

A tabela define classificações de desempenho relativamente umas às outras. O número e o tamanho das partições podem, em última análise, determinar o desempenho da consulta. Por exemplo, uma análise do intervalo de partições de uma tabela que tenha muitas partições grandes pode ter um mau desempenho em comparação com uma análise de tabela completa de uma tabela com algumas partições pequenas.

Os tipos de consulta listados na tabela anterior mostram uma progressão dos melhores tipos de consultas a utilizar para os piores tipos, com base nas respetivas classificações de desempenho. As consultas de pontos são os melhores tipos de consultas a utilizar porque utilizam totalmente o índice em cluster da tabela. A seguinte consulta de ponto utiliza os dados da tabela de registo de corridas a pé:

http://<account>.windows.core.net/registrations(PartitionKey=”2011 New York City Marathon__Full”,RowKey=”1234__John__M__55”)  
  

Se a aplicação utilizar várias consultas, nem todas podem ser consultas pontuais. Em termos de desempenho, as consultas de intervalo seguem consultas de pontos. Existem dois tipos de consultas de intervalo: a análise do intervalo de linhas e a análise do intervalo de partições. A análise do intervalo de linhas especifica uma única partição. Uma vez que a operação ocorre num único servidor de partição, as análises do intervalo de linhas são geralmente mais eficientes do que as análises do intervalo de partições. No entanto, um fator chave no desempenho das análises do intervalo de linhas é a seleção de uma consulta. A seletividade de consulta dita quantas linhas têm de ser iteradas para encontrar as linhas correspondentes. As consultas mais seletivas são mais eficientes durante as análises do intervalo de linhas.

Para avaliar as prioridades das suas consultas, considere os requisitos de frequência e tempo de resposta para cada consulta. As consultas executadas frequentemente podem ter prioridades superiores. No entanto, uma consulta importante, mas raramente utilizada, pode ter requisitos de baixa latência que podem classificá-la mais alto na lista de prioridades.

Escolha o valor PartitionKey

O núcleo da estrutura de qualquer tabela é a escalabilidade, as consultas utilizadas para aceder à mesma e os requisitos de operações de armazenamento. Os valores partitionKey que escolher ditam a forma como uma tabela é particionada e o tipo de consultas que pode utilizar. As operações de armazenamento, e especialmente as inserções, também podem afetar a sua escolha de valores PartitionKey . Os valores partitionKey podem variar de valores únicos a valores exclusivos. Também podem ser criados com vários valores. Pode utilizar as propriedades da entidade para formar o valor PartitionKey . Em alternativa, a aplicação pode calcular o valor. As secções seguintes abordam considerações importantes.

Transações de grupo de entidades

Os programadores devem primeiro considerar se a aplicação irá utilizar transações de grupo de entidades (atualizações em lote). As transações de grupos de entidades requerem que as entidades tenham o mesmo valor PartitionKey . Além disso, como as atualizações em lote se destinam a um grupo inteiro, as escolhas dos valores partitionKey podem ser limitadas. Por exemplo, uma aplicação bancária que mantém transações de caixa tem de inserir transações de caixa na tabela atomicamente. As transações de caixa representam as partes de débito e crédito e têm de ser líquidas a zero. Este requisito significa que o número da conta não pode ser utilizado como qualquer parte do valor PartitionKey porque cada lado da transação utiliza números de conta diferentes. Em vez disso, um ID de transação pode ser uma escolha melhor.

Partições

Os números e tamanhos das partições afetam a escalabilidade de uma tabela que está sob carga. Também são controlados pela granularidade dos valores partitionKey . Pode ser difícil determinar a PartitionKey com base no tamanho da partição, especialmente se a distribuição de valores for difícil de prever. Uma boa regra é utilizar várias partições mais pequenas. Muitas partições de tabela facilitam a gestão dos nós de armazenamento a partir dos nós de armazenamento dos mesmos.

Escolher valores exclusivos ou mais finos para PartitionKey resulta em partições mais pequenas, mas mais. Geralmente, isto é favorável porque o sistema pode fazer o balanceamento de carga das muitas partições para distribuir a carga por muitas partições. No entanto, deve considerar o efeito de ter muitas partições em consultas entre intervalos de partições. Estes tipos de consultas têm de visitar várias partições para satisfazer uma consulta. É possível que as partições sejam distribuídas por vários servidores de partições. Se uma consulta atravessar um limite do servidor, os tokens de continuação têm de ser devolvidos. Os tokens de continuação especificam os próximos valores partitionKey ou RowKey para obter o próximo conjunto de dados para a consulta. Por outras palavras, os tokens de continuação representam pelo menos mais um pedido para o serviço, o que pode degradar o desempenho geral da consulta.

A seletividade de consultas é outro fator que pode afetar o desempenho da consulta. A seletividade de consultas é uma medida do número de linhas que têm de ser iteradas para cada partição. Quanto mais seletiva for a consulta, mais eficiente será a consulta ao devolver as linhas pretendidas. O desempenho geral das consultas de intervalo pode depender do número de servidores de partição que têm de ser tocados ou da seleção da consulta. Também deve evitar utilizar os padrões apenas de acréscimo ou de pré-acréscimo quando insere dados na sua tabela. Se utilizar estes padrões, apesar de criar partições pequenas e muitas, poderá limitar o débito das operações de inserção. Os padrões só de acréscimo e apenas de pré-acréscimo são discutidos em partições de Intervalo.

Consultas

Conhecer as consultas que irá utilizar pode ajudá-lo a determinar que propriedades são importantes considerar para o valor PartitionKey . As propriedades que utiliza nas consultas são candidatas ao valor PartitionKey . A tabela seguinte fornece uma orientação geral sobre como determinar o valor PartitionKey :

Se a entidade... Ação
Tem uma propriedade de chave Utilize-a como PartitionKey.
Tem duas propriedades principais Utilize uma como PartitionKey e a outra como RowKey.
Tem mais de duas propriedades principais Utilize uma chave composta de valores concatenados.

Se existir mais do que uma consulta igualmente dominante, pode inserir as informações várias vezes com valores RowKey diferentes de que precisa. A sua aplicação irá gerir linhas secundárias (ou terciárias, etc.). Pode utilizar este tipo de padrão para satisfazer os requisitos de desempenho das suas consultas. O exemplo seguinte utiliza os dados do exemplo de registo de corrida a pé. Tem duas consultas dominantes:

  • Consultar por número bíb
  • Consulta por idade

Para servir ambas as consultas dominantes, insira duas linhas como uma transação de grupo de entidades. A tabela seguinte mostra as propriedades PartitionKey e RowKey para este cenário. Os valores RowKey fornecem um prefixo para o bíb e a idade para que a aplicação possa distinguir entre os dois valores.

PartitionKey RowKey
2011 New York City Marathon__Full BIB:01234__John__M__55
2011 New York City Marathon__Full IDADE:055__1234__John__M

Neste exemplo, uma transação de grupo de entidades é possível porque os valores partitionKey são os mesmos. A transação de grupo fornece atomicidade da operação de inserção. Embora seja possível utilizar este padrão com diferentes valores partitionKey , recomendamos que utilize os mesmos valores para obter este benefício. Caso contrário, poderá ter de escrever lógica extra para garantir transações atómicas que utilizam valores PartitionKey diferentes.

Operações de armazenamento

As tabelas no armazenamento de Tabelas do Azure podem encontrar carga não só a partir de consultas. Também podem encontrar carga a partir de operações de armazenamento, como inserções, atualizações e eliminações. Considere o tipo de operações de armazenamento que irá efetuar na tabela e a que taxa. Se efetuar estas operações com pouca frequência, poderá não ter de se preocupar com as mesmas. No entanto, para operações frequentes como efetuar muitas inserções num curto espaço de tempo, tem de considerar como essas operações são servidas como resultado dos valores partitionKey que escolher. Exemplos importantes são os padrões só de acréscimo e só de pré-acréscimo. Os padrões só de acréscimo e apenas de pré-acréscimo são discutidos em partições de Intervalo.

Quando utiliza um padrão só de acréscimo ou apenas de pré-fim, utiliza valores exclusivos ascendentes ou descendentes para a PartitionKey em inserções subsequentes. Se combinar este padrão com operações de inserção frequentes, a tabela não conseguirá servir as operações de inserção com grande escalabilidade. A escalabilidade da tabela é afetada porque o Azure não consegue fazer o balanceamento de carga dos pedidos de operação para outros servidores de partição. Nesse caso, poderá considerar a utilização de valores aleatórios, como valores GUID. Em seguida, os tamanhos das partições podem permanecer pequenos e manter o balanceamento de carga durante as operações de armazenamento.

Teste de stress da partição da tabela

Quando o valor PartitionKey é complexo ou requer comparações com outros mapeamentos partitionKey , poderá ter de testar o desempenho da tabela. O teste deve examinar o desempenho de uma partição em picos de carga.

Para realizar um teste de stress

  1. Criar uma tabela de teste.
  2. Carregue a tabela de teste com dados para que contenha entidades que tenham o valor PartitionKey que irá direcionar.
  3. Utilize a aplicação para simular o pico de carga para a tabela. Segmente uma única partição com o valor PartitionKey do passo 2. Este passo é diferente para cada aplicação, mas a simulação deve incluir todas as consultas e operações de armazenamento necessárias. Poderá ter de ajustar a aplicação para que tenha como destino uma única partição.
  4. Examine o débito das operações GET ou PUT na tabela.

Para examinar o débito, compare os valores reais com o limite especificado de uma única partição num único servidor. As partições estão limitadas a 2000 entidades por segundo. Se o débito exceder 2000 entidades por segundo para uma partição, o servidor poderá ser executado demasiado frequentemente numa definição de produção. Neste caso, os valores partitionKey podem ser demasiado grossários, para que não existam partições suficientes ou as partições sejam demasiado grandes. Poderá ter de modificar o valor PartitionKey para que as partições sejam distribuídas por mais servidores.

Balanceamento de carga

O balanceamento de carga na camada de partição ocorre quando uma partição fica demasiado quente. Quando uma partição está demasiado quente, a partição, especificamente o servidor de partição, funciona para além da escalabilidade de destino. Para o armazenamento do Azure, cada partição tem um objetivo de escalabilidade de 2000 entidades por segundo. O balanceamento de carga também ocorre na camada de Sistema de Ficheiros Distribuído (DFS).

O balanceamento de carga na camada DFS lida com a carga de E/S e está fora do âmbito deste artigo. O balanceamento de carga na camada de partição não ocorre imediatamente após o destino de escalabilidade ser excedido. Em vez disso, o sistema aguarda alguns minutos antes de iniciar o processo de balanceamento de carga. Isto garante que uma partição se tornou verdadeiramente frequente. Não é necessário criar partições com carga gerada que acione o balanceamento de carga porque o sistema executa automaticamente a tarefa.

Se uma tabela tiver sido preparada com uma determinada carga, o sistema poderá conseguir equilibrar as partições com base na carga real, o que resulta numa distribuição significativamente diferente das partições. Em vez de criar partições de preparação, considere escrever código que processe os erros de tempo limite e Servidor Ocupado. Os erros são devolvidos quando o sistema está a fazer o balanceamento de carga. Ao processar esses erros através de uma estratégia de repetição, a sua aplicação pode lidar melhor com as cargas de pico. As estratégias de repetição são abordadas mais detalhadamente na secção seguinte.

Quando ocorre o balanceamento de carga, a partição fica offline durante alguns segundos. Durante o período offline, o sistema reatribui a partição a um servidor de partição diferente. É importante ter em atenção que os seus dados não são armazenados pelos servidores de partição. Em vez disso, os servidores de partição servem entidades da camada DFS. Uma vez que os seus dados não estão armazenados na camada de partição, mover partições para servidores diferentes é um processo rápido. Esta flexibilidade limita consideravelmente o tempo de inatividade, caso exista, que a sua aplicação possa encontrar.

Estratégia de repetição

É importante que a sua aplicação lide com falhas de operações de armazenamento para ajudar a garantir que não perde atualizações de dados. Algumas falhas não requerem uma estratégia de repetição. Por exemplo, as atualizações que devolvem um erro 401 Não autorizado não beneficiam da repetição da operação porque é provável que o estado da aplicação não se altere entre as repetições que resolvem o erro 401. No entanto, erros como Servidor Ocupado ou Tempo Limite estão relacionados com as funcionalidades de balanceamento de carga do Azure que proporcionam escalabilidade da tabela. Quando os nós de armazenamento que servem as suas entidades ficam quentes, o Azure equilibra a carga ao mover partições para outros nós. Durante este período, a partição pode estar inacessível, o que resulta em erros de Servidor Ocupado ou Tempo Limite. Eventualmente, a partição é reativada e as atualizações são retomadas.

Uma estratégia de repetição é adequada para erros de Servidor Ocupado ou Tempo Limite. Na maioria dos casos, pode excluir erros de 400 níveis e alguns erros de 500 níveis da lógica de repetição. Os erros que podem ser excluídos incluem 501 Não Implementado e 505 Versão HTTP Não Suportada. Em seguida, pode implementar uma estratégia de repetição para erros de até 500 níveis, como Servidor Ocupado (503) e Tempo Limite (504).

Pode escolher entre três estratégias de repetição comuns para a sua aplicação:

  • Sem Repetição: não é efetuada nenhuma tentativa de repetição.
  • Backoff Fixo: a operação é repetida N vezes com um valor de recuo constante.
  • Trás Exponencial: a operação é repetida N vezes com um valor de recuo exponencial.

A estratégia Sem Repetição é uma forma simples (e evasiva) de lidar com falhas de operação. No entanto, uma estratégia Sem Repetição não é muito útil. Não impor quaisquer tentativas de repetição representa riscos óbvios, uma vez que os dados não são armazenados corretamente após operações falhadas. Uma estratégia melhor é utilizar a estratégia de Backoff Fixo. que fornece a capacidade de repetir operações com a mesma duração de backoff.

No entanto, a estratégia não está otimizada para processar tabelas altamente dimensionáveis. Se muitos threads ou processos estiverem à espera da mesma duração, podem ocorrer colisões. Uma estratégia de repetição recomendada é aquela que utiliza um recuo exponencial em que cada tentativa de repetição é maior do que a última tentativa. É semelhante ao algoritmo de prevenção de colisão utilizado em redes de computadores, como o Ethernet. O recuo exponencial utiliza um fator aleatório para fornecer uma variância adicional ao intervalo resultante. Em seguida, o valor de backoff é limitado a limites mínimos e máximos. A fórmula seguinte pode ser utilizada para calcular o próximo valor de backoff com um algoritmo exponencial:

y = Rand(0,8z, 1,2z)(2x-1

y = Min(zmin + y, zmax

Em que:

z = recuo predefinido em milissegundos

zmin = recuo mínimo predefinido em milissegundos

zmax = recuo máximo predefinido em milissegundos

x = o número de repetições

y = o valor de recuo em milissegundos

Os multiplicadores de 0,8 e 1,2 utilizados na função Rand (aleatório) produzem uma variância aleatória do recuo predefinido dentro de ±20% do valor original. O intervalo de ±20% é aceitável para a maioria das estratégias de repetição e evita novas colisões. A fórmula pode ser implementada com o seguinte código:

int retries = 1;  
  
// Initialize variables with default values  
var defaultBackoff = TimeSpan.FromSeconds(30);  
var backoffMin = TimeSpan.FromSeconds(3);  
var backoffMax = TimeSpan.FromSeconds(90);  
  
var random = new Random();  
  
double backoff = random.Next(  
    (int)(0.8D * defaultBackoff.TotalMilliseconds),   
    (int)(1.2D * defaultBackoff.TotalMilliseconds));  
backoff *= (Math.Pow(2, retries) - 1);  
backoff = Math.Min(  
    backoffMin.TotalMilliseconds + backoff,   
    backoffMax.TotalMilliseconds);  
  

Resumo

Uma aplicação no armazenamento de Tabelas do Azure pode armazenar uma grande quantidade de dados porque o Armazenamento de tabelas gere e reatribui partições em muitos nós de armazenamento. Pode utilizar a criação de partições de dados para controlar a escalabilidade da tabela. Planeie com antecedência quando definir um esquema de tabela para garantir que implementa estratégias de criação de partições eficientes. Especificamente, analise os requisitos, os dados e as consultas da aplicação antes de selecionar valores PartitionKey . Cada partição pode ser reatribuída a diferentes nós de armazenamento à medida que o sistema responde ao tráfego. Utilize um teste de esforço de partição para garantir que a tabela tem os valores partitionKey corretos. Este teste ajuda-o a determinar quando as partições estão demasiado quentes e ajuda-o a fazer os ajustes de partição necessários.

Para garantir que a sua aplicação processa erros intermitentes e que os seus dados persistem, utilize uma estratégia de repetição com o backoff. A política de repetição predefinida que a Biblioteca de Cliente do Armazenamento do Azure utiliza tem um recuo exponencial que evita colisões e maximiza o débito da sua aplicação.