Criar uma estratégia de particionamento escalonável para o armazenamento de Tabelas do Azure
Este artigo discute o particionamento de uma tabela no armazenamento de Tabelas do Azure e as estratégias que você pode usar para garantir uma escalabilidade eficiente.
O Azure fornece armazenamento em nuvem altamente disponível e altamente escalonável. O sistema de armazenamento subjacente para o Azure é fornecido por meio de um conjunto de serviços, incluindo Armazenamento de Blobs do Azure, Armazenamento de Tabelas do Azure, Armazenamento de Filas do Azure e Arquivos do Azure.
O Armazenamento de Tabelas do Azure foi projetado para armazenar dados estruturados. O serviço de Armazenamento do Azure dá suporte a um número ilimitado de tabelas. Cada tabela pode ser dimensionada para níveis massivos e fornecer terabytes de armazenamento físico. Para aproveitar melhor as tabelas, você deve particionar seus dados de maneira ideal. Este artigo explora estratégias que você pode usar para particionar dados com eficiência para o Armazenamento de Tabelas do Azure.
Entidades de tabela
As entidades de tabela representam as unidades de dados armazenadas em uma tabela. As entidades de tabela são semelhantes às linhas em uma tabela de banco de dados relacional típica. Cada entidade define um conjunto de propriedades. Cada propriedade é definida como um par chave/valor por seu nome, valor e tipo de dados do valor. As entidades devem definir as três propriedades de sistema a seguir como parte da coleção de propriedades:
PartitionKey: a propriedade PartitionKey armazena valores de cadeia de caracteres que identificam a partição à qual uma entidade pertence. As partições, como discutimos posteriormente, 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 de caracteres que identificam entidades exclusivamente em cada partição. PartitionKey e RowKey juntos formam a chave primária para a entidade.
Carimbo de data/hora: a propriedade Timestamp fornece rastreabilidade para uma entidade. Um carimbo de data/hora é um valor de data/hora que informa a última vez em que a entidade foi modificada. Às vezes, um carimbo de data/hora é chamado de versão da entidade. As modificações em carimbos de data/hora são ignoradas porque o serviço de tabela mantém o valor dessa 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 PartitionKey e RowKey combinadas. As duas propriedades formam um único índice clusterizado dentro da tabela. Os valores PartitionKey e RowKey podem ter até 1024 caracteres de tamanho. Cadeias de caracteres vazias também são permitidas; no entanto, valores nulos não são permitidos.
O índice clusterizado classifica por PartitionKey em ordem crescente e, em seguida, por RowKey em ordem crescente. A ordem de classificação é observada em todas as respostas de consultas. Durante a operação de classificação são usadas comparações lexicais. Um valor de cadeia de caracteres de "111" aparece antes de um valor de cadeia de caracteres de "2". Em alguns casos, talvez você queira que a ordem de classificação seja numérica. Para classificar em uma ordem numérica e crescente, você deve usar cadeias de caracteres de comprimento fixo e sem adição. No exemplo anterior, "002" aparece 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 de um servidor de partição. Cada servidor de partição pode atender a uma ou mais partições. Um servidor de partições possui um limite de taxa do número de entidades que ele pode atender a partir de uma partição ao longo do tempo. Especificamente, uma partição tem uma meta de escalabilidade de 2.000 entidades por segundo. Essa taxa de transferência pode ser maior durante a carga mínima no nó de armazenamento, mas é limitada quando o nó fica ativo ou ativo.
Para ilustrar melhor o conceito de particionamento, a figura a seguir mostra uma tabela que contém um pequeno subconjunto de dados para registros de eventos de corrida de pé. A figura apresenta uma exibição conceitual do particionamento em que 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 usa dois servidores de partição. O servidor A contém registros para as distâncias de meia maratona e 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.
Uma tabela com três partições
Escalabilidade
Como uma partição é sempre atendida a partir de um servidor de partições único, e dado que cada servidor de partições pode atender uma ou mais partições, a eficiência das entidades de atendimento está correlacionada com a integridade do servidor. Os servidores que encontram alto tráfego para suas partições podem não conseguir sustentar uma alta taxa de transferência. Por exemplo, na figura anterior, se muitas solicitações para "2011 New York City Marathon__Half" forem recebidas, o servidor A poderá ficar muito quente. Para aumentar a taxa de transferência do servidor, o sistema de armazenamento fornece balanceamento de carga das partições para outros servidores. O resultado é que o tráfego é distribuído através de vários outros servidores. Para o balanceamento de carga ideal do tráfego, você deve usar mais partições para que o Armazenamento de Tabelas do Azure possa distribuir as partições para mais servidores de partição.
Transações do 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 qualquer 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 consiste em no máximo 100 operações de armazenamento e pode ter no máximo 4 MiB de tamanho. As transações de grupo de entidades fornecem ao Armazenamento de Tabelas do Azure uma forma limitada de semântica acid (atomicidade, consistência, isolamento e durabilidade) fornecida por bancos de dados relacionais.
As transações de grupo de entidades melhoram a taxa de transferência porque reduzem o número de operações de armazenamento individuais que devem ser enviadas ao Armazenamento de Tabelas do Azure. As transações de grupo de entidades também fornecem um benefício econômico. Uma transação de grupo de entidades é cobrada como uma única operação de armazenamento, independentemente de quantas operações de armazenamento ela contém. Como todas as operações de armazenamento em uma transação de grupo de entidades afetam entidades que têm o mesmo valor PartitionKey , a necessidade de usar transações de grupo de entidades pode impulsionar a seleção do valor partitionKey .
Partições de intervalo
Se você usar valores exclusivos de PartitionKey para suas entidades, cada entidade pertencerá à sua própria partição. Se os valores exclusivos usados aumentarem ou diminuirem de 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 de consultas de intervalo. Sem partições de intervalo, uma consulta de intervalo deve cruzar limites de partição ou limites de servidor, o que pode diminuir o desempenho da consulta. Considere um aplicativo que usa a tabela a seguir, 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 em uma partição de intervalo. Se você aplicar uma consulta de intervalo à tabela que usa PartitionKey como critérios e solicitar entidades de "0001" a "0003", a consulta poderá ser executada com eficiência porque as entidades são atendidas de um único servidor de partição. Não há garantia de quando e como uma partição de intervalo será criada.
A existência de partições de intervalo para sua tabela poderá afetar o desempenho de suas operações de inserção se você inserir entidades que têm valores partitionKey crescentes ou decrescentes. Inserir entidades que têm valores de PartitionKey crescentes é chamado de padrão somente acréscimo. Inserir entidades que têm valores decrescentes é chamado de padrão somente de prefixo. Considere não usar esses tipos de padrões porque a taxa de transferência geral de suas solicitações de inserção é limitada por um único servidor de partição. Isso ocorre porque, se houver partições de intervalo, a primeira e a última partições (intervalo) contêm os valores partitionKey mínimos e maiores, respectivamente. Portanto, a inserção de uma nova entidade, que tem um valor PartitionKey sequencialmente menor ou mais alto, tem como destino uma das partições finais. A figura a seguir mostra um possível conjunto de partições de intervalo baseadas no exemplo anterior. Se um conjunto de entidades "0007", "0008" e "0009" fosse inserido, eles seriam atribuídos à última partição (laranja).
Um conjunto de partições de intervalo
É importante observar que não haverá nenhum efeito negativo no desempenho se as operações de inserção usarem valores PartitionKey mais dispersos.
Analisar dados
Ao contrário de uma tabela em um banco de dados relacional que você pode usar para gerenciar índices, as tabelas no Armazenamento de Tabelas do Azure só podem ter um índice. Um índice no armazenamento de Tabelas do Azure sempre consiste nas propriedades PartitionKey e RowKey .
Em uma tabela do Azure, você não tem o luxo de ajustar o desempenho da tabela adicionando mais índices ou alterando uma tabela existente depois de implantá-la. Você deve analisar os dados ao projetar sua tabela. Os aspectos mais importantes a serem considerados para a escalabilidade ideal e para a eficiência de consulta e inserção são os valores PartitionKey e RowKey . Este artigo enfatiza como escolher o PartitionKey porque ele se relaciona diretamente com a forma como as tabelas são particionadas.
Dimensionamento da partição
O dimensionamento da partição refere-se ao número de entidades que uma partição contém. Conforme discutimos em Escalabilidade, ter mais partições significa que você obtém um melhor balanceamento de carga. A granularidade do valor PartitionKey afeta o tamanho das partições. No nível mais grosseiro, se um único valor for usado como PartitionKey, todas as entidades estarão em uma única partição muito grande. No melhor nível de granularidade, a PartitionKey pode conter valores exclusivos para cada entidade. O resultado é que há uma partição para cada entidade. A tabela a seguir mostra as vantagens e desvantagens para o intervalo de granularidades:
Granularidade PartitionKey | Tamanho da partição | Vantagens | Desvantagens |
---|---|---|---|
Um único valor | Pequeno número de entidades | Transações em lote são possíveis com qualquer entidade. Todas as entidades são locais e atendidas do mesmo nó de armazenamento. |
|
Um único valor | Grande número de entidades | As transações de grupo de entidades podem ser possíveis com qualquer entidade. Para obter mais informações sobre os limites de transações de grupo de entidades, consulte Executando transações de grupo de entidades. | O escalonamento é limitado. A taxa de transferência é limitada ao desempenho de um único servidor. |
Vários valores | Várias partições Os tamanhos de partição dependem da distribuição da entidade. |
Transações em lote são possíveis em algumas entidades. O particionamento dinâmico é possível. Consultas de solicitação única são possíveis (sem tokens de continuação). O balanceamento de carga entre mais servidores de partição é possível. |
Uma distribuição altamente desigual de entidades entre partições pode limitar o desempenho das partições maiores e mais ativas. |
Valores únicos | Muitas partições pequenas | A tabela é altamente escalonável. Partições de intervalo podem melhorar o desempenho de consultas entre intervalos de partições. |
Consultas que envolvem intervalos podem exigir visitas a mais de um servidor. As transações em lotes não são possíveis. Padrões somente de acréscimo ou somente de pré-fim podem afetar a taxa de transferência de inserção. |
A tabela mostra como o dimensionamento é afetado pelos valores partitionKey . É uma prática recomendada favorecer partições menores porque elas oferecem melhor balanceamento de carga. Partições maiores podem ser apropriadas em alguns cenários e não são necessariamente desvantajosas. Por exemplo, se o aplicativo não exigir escalabilidade, uma única partição grande poderá ser apropriada.
Determinar consultas
As consultas recuperam dados de tabelas. Ao analisar os dados de uma tabela no Armazenamento de Tabelas do Azure, é importante considerar quais consultas o aplicativo usará. Se um aplicativo tiver várias consultas, talvez seja necessário priorizá-las, embora suas decisões possam ser subjetivas. Em muitos casos, as consultas dominantes são perceptíveis de outras consultas. Em termos de desempenho, as consultas se encaixam em diferentes categorias. Como uma tabela tem apenas um índice, o desempenho da consulta geralmente está relacionado às propriedades PartitionKey e RowKey . A tabela a seguir mostra os diferentes tipos de consultas e suas classificações de desempenho:
Tipo de consulta | Correspondência partitionKey | Correspondência rowKey | Classificação de desempenho |
---|---|---|---|
Exame de intervalo de linhas | Exato | Parcial | Melhor com partições de tamanho menor. Ruim com partições muito grandes. |
Exame de intervalo de partições | Parcial | Parcial | Bom com um pequeno número de servidores de partição sendo tocados. Pior com mais servidores sendo tocados. |
Verificação de tabela completa | Parcial, nenhum | Parcial, nenhum | Pior com um subconjunto de partições sendo verificado. Pior com todas as partições sendo examinadas. |
Observação
A tabela define classificações de desempenho relativas 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 verificação de intervalo de partições para uma tabela que tem muitas partições grandes pode ter um desempenho ruim em comparação com uma verificação de tabela completa para uma tabela que tem algumas partições pequenas.
Os tipos de consulta listados na tabela anterior mostram uma progressão dos melhores tipos de consultas a serem usados para os piores tipos, com base em suas classificações de desempenho. As consultas de faixas são os melhores tipos de consultas, porque usam totalmente o índice clusterizado da tabela. A consulta de ponto a seguir usa os dados da tabela de registro de corridas de pé:
http://<account>.windows.core.net/registrations(PartitionKey=”2011 New York City Marathon__Full”,RowKey=”1234__John__M__55”)
Se o aplicativo usar várias consultas, elas não podem ser todas consultas de faixas. Em termos de desempenho, as consultas de intervalo seguem as consultas de faixa. Há dois tipos de consultas de intervalo: a verificação do intervalo de linhas e a verificação do intervalo de partição. O exame de intervalo de linhas especifica uma única partição. Como a operação ocorre em um único servidor de partição, as verificações de intervalo de linhas geralmente são mais eficientes do que as verificações de intervalo de partição. No entanto, um fator-chave no desempenho das verificações de intervalo de linhas é o quão seletiva é uma consulta. A seletividade da consulta determina quantas linhas devem ser iteradas para encontrar linhas correspondentes. As consultas mais seletivas são mais eficientes durante os exames de intervalo de linhas.
Para avaliar as prioridades de suas consultas, considere os requisitos de frequência e tempo de resposta para cada consulta. As consultas executadas com frequência podem ser priorizadas mais alto. No entanto, uma consulta importante, mas raramente usada, pode ter requisitos de baixa latência que poderiam classificá-la mais alto na lista de prioridades.
Escolha o valor PartitionKey
O núcleo do design de qualquer tabela é sua escalabilidade, as consultas usadas para acessá-la e os requisitos de operação de armazenamento. Os valores PartitionKey escolhidos determinam como uma tabela é particionada e o tipo de consultas que você pode usar. As operações de armazenamento e, especialmente, as inserções, também podem afetar sua escolha de valores PartitionKey . Os valores PartitionKey podem variar de valores únicos a valores exclusivos. Eles também podem ser criados usando vários valores. Você pode usar propriedades de entidade para formar o valor PartitionKey . Ou o aplicativo pode calcular o valor. As seções a seguir discutem considerações importantes.
Transações do grupo de entidades
Os desenvolvedores devem primeiro considerar se o aplicativo usará transações de grupo de entidades (atualizações em lote). As transações de grupo de entidades exigem que as entidades tenham o mesmo valor PartitionKey . Além disso, como as atualizações em lote são para um grupo inteiro, as opções de valores partitionKey podem ser limitadas. Por exemplo, um aplicativo bancário que mantém transações monetárias deve inseri-las na tabela atomicamente. As transações em dinheiro representam os lados do débito e do crédito e devem ser líquidas a zero. Esse requisito significa que o número da conta não pode ser usado como parte do valor PartitionKey porque cada lado da transação usa números de conta diferentes. Em vez disso, uma ID de transação pode ser uma opção melhor.
Partições
Números e tamanhos de partição afetam a escalabilidade de uma tabela que está sob carga. Eles também são controlados pelo quão granulares são os valores partitionKey . Pode ser desafiador determinar PartitionKey com base no tamanho da partição, especialmente se a distribuição de valores for difícil de prever. Uma boa regra prática é usar várias partições menores. Muitas partições de tabela facilitam para o armazenamento de Tabelas do Azure gerenciar os nós de armazenamento dos quais as partições são atendidas.
Escolher valores exclusivos ou mais finos para PartitionKey resulta em partições menores, mas mais. Isso geralmente é favorável porque o sistema pode balancear a carga de várias partições para distribuir a carga entre várias partições. No entanto, você deve levar em consideração o efeito de ter várias partições em consultas de intervalo entre partições. Esses tipos de consultas devem visitar várias partições para atender a uma consulta. É possível que as partições sejam distribuídas entre muitos servidores de partição. Se uma consulta cruzar um limite de servidor, deverão ser retornados tokens de continuação. Tokens de continuação especificam os próximos valores PartitionKey ou RowKey para recuperar o próximo conjunto de dados para a consulta. Em outras palavras, os tokens de continuação representam pelo menos mais uma solicitação para o serviço, o que pode prejudicar o desempenho geral da consulta.
A seletividade da consulta é outro fator que pode afetar o desempenho da consulta. A seletividade da consulta é uma medida de quantas linhas devem ser iteradas para cada partição. Quanto mais seletiva for uma consulta, mais eficiente será a consulta ao retornar as linhas desejadas. O desempenho geral das consultas de intervalo pode depender do número de servidores de partição que devem ser tocados ou quão seletiva é a consulta. Você também deve evitar usar os padrões somente acréscimo ou somente prefixo ao inserir dados em sua tabela. Se você usar esses padrões, apesar de criar pequenas e muitas partições, poderá limitar a taxa de transferência de suas operações de inserção. Os padrões somente acréscimo e somente de acréscimo são discutidos em Partições de intervalo.
Consultas
Conhecer as consultas que você usará pode ajudá-lo a determinar quais propriedades são importantes considerar para o valor PartitionKey . As propriedades que você usa nas consultas são candidatas ao valor PartitionKey . A tabela a seguir fornece uma diretriz geral de como determinar o valor partitionKey :
Se a entidade... | Ação |
---|---|
tem uma propriedade chave | Use-o como PartitionKey. |
tem duas propriedades chave | Use um como PartitionKey e o outro como RowKey. |
tem mas de duas propriedades chave | Use uma chave composta de valores concatenados. |
Se houver mais de uma consulta igualmente dominante, você poderá inserir as informações várias vezes usando valores diferentes de RowKey necessários. Seu aplicativo gerenciará linhas secundárias (ou terciárias e assim por diante). Você pode usar esse tipo de padrão para atender aos requisitos de desempenho de suas consultas. O exemplo a seguir usa os dados do exemplo de registro de corrida de pé. Ele tem duas consultas dominantes:
- Consulta por número chave
- Consulta por idade
Para atender ambas as consultas dominantes, insira duas linhas como uma transação de grupo de entidades. A tabela a seguir mostra as propriedades PartitionKey e RowKey para esse cenário. Os valores RowKey fornecem um prefixo para o bib e a idade para que o aplicativo possa distinguir entre os dois valores.
PartitionKey | RowKey |
---|---|
2011 New York City Marathon__Full | CHAVE: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 usar esse padrão com valores de PartitionKey diferentes, recomendamos que você use os mesmos valores para obter esse benefício. Caso contrário, talvez seja necessário escrever uma lógica extra para garantir transações atômicas que usam valores de PartitionKey diferentes.
Operações de armazenamento
As tabelas no Armazenamento de Tabelas do Azure podem encontrar carga não apenas de consultas. Eles também podem encontrar carga de operações de armazenamento, como inserções, atualizações e exclusões. Considere o tipo de operações de armazenamento que você executará na tabela e a qual taxa. Se você executar essas operações com pouca frequência, talvez não precise se preocupar com elas. No entanto, para operações frequentes, como executar muitas inserções em um curto espaço de tempo, você deve considerar como essas operações são atendidas como resultado dos valores partitionKey que você escolher. Exemplos importantes são os padrões somente acréscimo e somente prefixo. Os padrões somente acréscimo e somente de acréscimo são discutidos em Partições de intervalo.
Ao usar um padrão somente acréscimo ou somente de precedência, você usa valores crescentes ou decrescentes exclusivos para PartitionKey em inserções subsequentes. Se você combinar esse padrão com operações de inserção frequentes, sua tabela não poderá atender às operações de inserção com grande escalabilidade. A escalabilidade da tabela é afetada porque o Azure não pode balancear a carga das solicitações de operação para outros servidores de partição. Nesse caso, convém considerar o uso de valores aleatórios, como valores GUID. Em seguida, os tamanhos de partição podem permanecer pequenos e ainda manter o balanceamento de carga durante as operações de armazenamento.
Teste de estresse de partição de tabela
Quando o valor PartitionKey é complexo ou requer comparações com outros mapeamentos PartitionKey , talvez seja necessário testar o desempenho da tabela. O teste deve examinar o desempenho da partição sob cargas de pico.
Para realizar um teste de estresse
- Crie uma tabela de teste.
- Carregue a tabela de teste com dados para que ela contenha entidades que têm o valor PartitionKey que você direcionará.
- Use o aplicativo para simular a carga de pico para a tabela. Direcione uma única partição usando o valor PartitionKey da etapa 2. Essa etapa é diferente para cada aplicativo, mas a simulação deve incluir todas as consultas e operações de armazenamento necessárias. Talvez seja necessário ajustar o aplicativo para que ele tenha como destino uma única partição.
- Examine a taxa de transferência das operações GET ou PUT na tabela.
Para examinar a taxa de transferência, compare os valores reais com o limite especificado de uma única partição em um único servidor. As partições são limitadas a 2.000 entidades por segundo. Se a taxa de transferência exceder 2.000 entidades por segundo para uma partição, o servidor poderá ser executado muito quente em uma configuração de produção. Nesse caso, os valores partitionKey podem ser muito grosseiros, de modo que não haja partições suficientes ou as partições sejam muito grandes. Talvez seja necessário modificar o valor PartitionKey para que as partições sejam distribuídas entre mais servidores.
Balanceamento de carga
O balanceamento de carga na camada de partição ocorre quando uma partição fica muito quente. Quando uma partição está muito quente, a partição, especificamente o servidor de partição, opera além de sua escalabilidade de destino. Para o armazenamento do Azure, cada partição tem uma meta de escalabilidade de 2.000 entidades por segundo. O balanceamento de carga também ocorre na camada DFS (Sistema de Arquivos Distribuído).
O balanceamento de carga na camada DFS lida com a carga de E/S e está fora do escopo deste artigo. O balanceamento de carga na camada de partição não ocorre imediatamente depois que o destino de escalabilidade é excedido. Em vez disso, o sistema aguarda alguns minutos antes de iniciar o processo de balanceamento de carga. Isso garante que a partição tenha se tornado realmente muito solicitada. Não é necessário preparar partições com carga gerada que dispare o balanceamento de carga porque o sistema executa automaticamente a tarefa.
Se uma tabela foi preparada com uma determinada carga, o sistema pode ser capaz de equilibrar as partições com base na carga real, o que resulta em uma distribuição significativamente diferente das partições. Em vez de preparar partições, considere escrever código que manipula o tempo limite e os erros de Servidor Ocupado. Os erros são retornados quando o sistema está balanceando a carga. Ao lidar com esses erros usando uma estratégia de repetição, seu aplicativo pode lidar melhor com cargas de pico. As estratégias de repetição são discutidas mais detalhadamente na seção seguinte.
Quando ocorre o balanceamento de carga, a partição fica offline por alguns segundos. Durante o período offline, o sistema reatribui a partição a um servidor de partição diferente. É importante observar que seus dados não são armazenados pelos servidores de partição. Em vez disso, os servidores de partições atendem entidades a partr da camada DFS. Como seus dados não são armazenados na camada de partição, mover partições para servidores diferentes é um processo rápido. Essa flexibilidade limita muito o tempo de inatividade, se houver, que seu aplicativo pode encontrar.
Estratégia de repetição
É importante que seu aplicativo lide com falhas de operação de armazenamento para ajudar a garantir que você não perca nenhuma atualização de dados. Algumas falhas não exigem uma estratégia de repetição. Por exemplo, as atualizações que retornam um erro 401 Não autorizado não se beneficiam da repetição da operação porque é provável que o estado do aplicativo não mude entre as repetições que resolve o erro 401. No entanto, erros como Servidor Ocupado ou Tempo Limite estão relacionados aos recursos de balanceamento de carga do Azure que fornecem escalabilidade de tabela. Quando os nós de armazenamento que atendem suas entidades ficam ativos, o Azure equilibra a carga movendo partições para outros nós. Durante esse tempo, a partição pode estar inacessível, o que resulta em erros de Servidor Ocupado ou Tempo Limite. Eventualmente, a partição é reabilitada e as atualizações são retomadas.
Uma estratégia de repetição é apropriada para erros de Servidor Ocupado ou Tempo Limite. Na maioria dos casos, você pode excluir erros de nível 400 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 Sem Suporte. Em seguida, você pode implementar uma estratégia de repetição para erros de até 500 níveis, como Servidor Ocupado (503) e Tempo Limite (504).
Você pode escolher entre três estratégias de repetição comuns para seu aplicativo:
- Sem repetição: nenhuma tentativa de repetição é feita.
- Retirada corrigida: a operação é repetida N vezes com um valor de retirada constante.
- Retirada exponencial: a operação é repetida N vezes com um valor de retirada exponencial.
A estratégia Sem Repetição é uma forma simples (e evasiva) de lidar com falhas de operações. No entanto, uma estratégia Sem Repetição não é muito útil. Não impor nenhuma tentativa de repetição coloca riscos óbvios de que dados não sejam armazenados corretamente após operações que apresentarem falha. Uma estratégia melhor é usar a estratégia de Retirada Fixa. que fornece a capacidade de repetir operações com a mesma duração de retirada.
No entanto, a estratégia não é otimizada para lidar com tabelas altamente escalonáveis. Se muitos threads ou processos estiverem aguardando a mesma duração, podem ocorrer colisões. Uma estratégia de repetição recomendada é aquela que usa uma retirada exponencial em que cada tentativa de repetição é maior do que a última tentativa. É semelhante ao algoritmo de prevenção de colisão usado em redes de computador, como Ethernet. A retirada exponencial usa um fator aleatório para fornecer uma variação adicional ao intervalo resultante. O valor da retirada é então restrito aos limites mínimo e máximo. A fórmula seguinte pode ser usada para calcular o próximo valor de retirada usando um algoritmo exponencial:
y = Rand(0.8z, 1.2z)(2x-1
y = Min(zmin + y, zmax
Em que:
z= retirada padrão em milissegundos
zmin = retirada padrão mínima em milissegundos
zmax = retirada padrão máxima em milissegundos
x= número de repetições
y= valor da retirada em milissegundos
Os multiplicadores 0,8 e 1,2 usados na função Rand (aleatória) produzem uma variação aleatória da retirada padrão 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 usando 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
Um aplicativo no Armazenamento de Tabelas do Azure pode armazenar uma grande quantidade de dados porque o Armazenamento de tabelas gerencia e reatribui partições em muitos nós de armazenamento. Você pode usar o particionamento de dados para controlar a escalabilidade da tabela. Planeje com antecedência ao definir um esquema de tabela para garantir que você implemente estratégias de particionamento eficientes. Especificamente, analise os requisitos, os dados e as consultas do aplicativo 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. Use um teste de estresse de partição para garantir que a tabela tenha os valores de PartitionKey corretos. Esse teste ajuda você a determinar quando as partições estão muito quentes e ajuda você a fazer os ajustes de partição necessários.
Para garantir que seu aplicativo manipule erros intermitentes e que seus dados sejam persistentes, use uma estratégia de repetição com retirada. A política de repetição padrão usada pela Biblioteca de Clientes do Armazenamento do Azure tem uma retirada exponencial que evita colisões e maximiza a taxa de transferência do aplicativo.