Como aumentamos a velocidade dessa consulta?
Temos aproximadamente 100 consumidores no período de 1-2 minutes
execução da consulta a seguir. Cada uma dessas execuções representa 1 execução de uma função de consumo.
TableQuery<T> treanslationsQuery = new TableQuery<T>()
.Where(
TableQuery.CombineFilters(
TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, sourceDestinationPartitionKey)
, TableOperators.Or,
TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, anySourceDestinationPartitionKey)
)
);
Esta consulta produzirá aproximadamente 5000 resultados.
Código completo:
public static async Task<IEnumerable<T>> ExecuteQueryAsync<T>(this CloudTable table, TableQuery<T> query) where T : ITableEntity, new()
{
var items = new List<T>();
TableContinuationToken token = null;
do
{
TableQuerySegment<T> seg = await table.ExecuteQuerySegmentedAsync(query, token);
token = seg.ContinuationToken;
items.AddRange(seg);
} while (token != null);
return items;
}
public static IEnumerable<Translation> Get<T>(string sourceParty, string destinationParty, string wildcardSourceParty, string tableName) where T : ITableEntity, new()
{
var acc = CloudStorageAccount.Parse(Environment.GetEnvironmentVariable("conn"));
var tableClient = acc.CreateCloudTableClient();
var table = tableClient.GetTableReference(Environment.GetEnvironmentVariable("TableCache"));
var sourceDestinationPartitionKey = $"{sourceParty.ToLowerTrim()}-{destinationParty.ToLowerTrim()}";
var anySourceDestinationPartitionKey = $"{wildcardSourceParty}-{destinationParty.ToLowerTrim()}";
TableQuery<T> treanslationsQuery = new TableQuery<T>()
.Where(
TableQuery.CombineFilters(
TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, sourceDestinationPartitionKey)
, TableOperators.Or,
TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, anySourceDestinationPartitionKey)
)
);
var over1000Results = table.ExecuteQueryAsync(treanslationsQuery).Result.Cast<Translation>();
return over1000Results.Where(x => x.expireAt > DateTime.Now)
.Where(x => x.effectiveAt < DateTime.Now);
}
Durante essas execuções, quando houver 100 consumidores, como você pode ver, as solicitações serão agrupadas e formarão picos:
Durante esses picos, as solicitações geralmente levam mais de um minuto:
Como aumentamos a velocidade dessa consulta?
c#
azure
azure-table-storage
azure-virtual-network
azure-tablequery
eu--''''''---------''''''''''''
fonte
fonte
Respostas:
Aqui está um dos problemas: você está executando a consulta e, em seguida, filtrando-a da memória usando esses "onde". Mova os filtros para antes da consulta ser executada, o que deve ajudar bastante.
Segundo, você deve fornecer algum limite de linhas para recuperar do banco de dados
fonte
Há três coisas que você pode considerar:
1 . Primeiro, livre-se das
Where
cláusulas que você executa no resultado da consulta. É melhor incluir cláusulas na consulta o máximo possível (ainda melhor se você tiver algum índice em suas tabelas, inclua-as também). Por enquanto, você pode alterar sua consulta como abaixo:Como você tem uma grande quantidade de dados para recuperar, é melhor executar suas consultas em paralelo. Então, você deve substituir o
do while
loop dentro doExecuteQueryAsync
método peloParallel.ForEach
que escrevi com base em Stephen Toub Parallel.While ; Dessa forma, reduzirá o tempo de execução da consulta. Essa é uma boa opção, pois você pode removerResult
quando fizer uma chamada nesse método, mas há uma pequena limitação que falarei sobre isso após esta parte do código:E então você pode chamá-lo no seu
Get
método:Como você pode ver, o método em si não é assíncrono (você deve alterar o nome) e
Parallel.ForEach
não é compatível com a passagem de um método assíncrono. É por isso que eu useiExecuteQuerySegmented
. Mas, para torná-lo mais eficiente e usar todos os benefícios do método assíncrono, você pode substituir oForEach
loop acima peloActionBlock
método Dataflow ouParallelForEachAsync
método de extensão do pacote AsyncEnumerator Nuget .2. É uma boa opção para executar consultas paralelas independentes e mesclar os resultados, mesmo que sua melhoria de desempenho seja no máximo 10%. Isso lhe dá tempo para encontrar a melhor consulta amigável ao desempenho. Mas nunca se esqueça de incluir todas as suas restrições e teste as duas maneiras de saber qual deles melhor se adapta ao seu problema.
3 . Não tenho certeza se é uma boa sugestão ou não, mas faça-o e veja os resultados. Conforme descrito no MSDN :
Assim, você pode jogar com o tempo limite e verificar se há alguma melhoria no desempenho.
fonte
Infelizmente, a consulta abaixo apresenta uma verificação completa da tabela :
Você deve dividi-lo em dois filtros de chave de partição e consultá-los separadamente, que se tornarão duas varreduras de partição e terão um desempenho mais eficiente.
fonte
Portanto, o segredo não está apenas no código, mas também na configuração de suas tabelas de armazenamento do Azure.
a) Uma das opções importantes para otimizar suas consultas no Azure é introduzir o cache. Isso reduzirá drasticamente seus tempos de resposta gerais e, assim, evitará gargalos durante o horário de pico que você mencionou.
b) Além disso, ao consultar entidades fora do Azure, a maneira mais rápida possível de fazer isso é com o PartitionKey e o RowKey. Esses são os únicos campos indexados no armazenamento de tabelas e qualquer consulta que utilize os dois será retornada em questão de alguns milissegundos. Portanto, verifique se você usa o PartitionKey e o RowKey.
Veja mais detalhes aqui: https://docs.microsoft.com/en-us/azure/storage/tables/table-storage-design-for-query
Espero que isto ajude.
fonte
Nota: Este é um conselho geral de otimização de consulta ao banco de dados.
É possível que o ORM esteja fazendo algo estúpido. Ao fazer otimizações, não há problema em renunciar a uma camada de abstração. Portanto, sugiro reescrever a consulta na linguagem de consulta (SQL?) Para facilitar a visualização do que está acontecendo e também a otimização.
A chave para otimizar pesquisas é a classificação! Manter uma tabela classificada geralmente é muito mais barata em comparação com a verificação de toda a tabela em todas as consultas! Portanto, se possível, mantenha a tabela classificada pela chave usada na consulta. Na maioria das soluções de banco de dados, isso é alcançado através da criação de uma chave de índice.
Outra estratégia que funciona bem se houver poucas combinações é ter cada consulta como uma tabela separada (temporária na memória) que esteja sempre atualizada. Portanto, quando algo é inserido, ele também é "inserido" nas tabelas "visualizar". Algumas soluções de banco de dados chamam isso de "visualizações".
Uma estratégia mais bruta é criar réplicas somente leitura para distribuir a carga.
fonte