Как мы можем увеличить скорость этого запроса?
У нас есть около 100 потребителей в течение 1-2 minutes
выполнения следующего запроса. Каждый из этих прогонов представляет собой 1 прогон функции потребления.
TableQuery<T> treanslationsQuery = new TableQuery<T>()
.Where(
TableQuery.CombineFilters(
TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, sourceDestinationPartitionKey)
, TableOperators.Or,
TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, anySourceDestinationPartitionKey)
)
);
Этот запрос даст около 5000 результатов.
Полный код:
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);
}
Во время этих исполнений, когда есть 100 потребителей, как вы видите, запросы будут кластеризоваться и сформировать пики:
Во время этих всплесков запросы часто занимают более 1 минуты:
Как мы можем увеличить скорость этого запроса?
c#
azure
azure-table-storage
azure-virtual-network
azure-tablequery
л --''''''--------- «» «» «» «» «» «»
источник
источник
Ответы:
Вот одна из проблем: вы запускаете запрос, а затем фильтруете его из памяти, используя эти «источники». Переместите фильтры до выполнения запроса, что должно помочь.
Во-вторых, вы должны указать некоторое количество строк для извлечения из базы данных.
источник
Есть 3 вещи, которые вы можете рассмотреть:
1 . Прежде всего, избавьтесь от своих
Where
предложений, которые вы выполняете для результата запроса. Лучше включать в запрос как можно больше предложений (даже лучше, если у вас есть какие-либо индексы в таблицах, включая их). Сейчас вы можете изменить свой запрос, как показано ниже:Поскольку у вас есть большой объем данных для извлечения, лучше выполнять ваши запросы параллельно. Итак, вы должны заменить
do while
цикл внутриExecuteQueryAsync
метода на «Parallel.ForEach
Я написал» на основе Stephen Toub Parallel.While ; Таким образом, это сократит время выполнения запроса. Это хороший выбор, потому что вы можете удалить егоResult
при вызове этого метода, но у него есть небольшое ограничение, о котором я расскажу после этой части кода:И тогда вы можете вызвать это в вашем
Get
методе:Как видите, метод itselft не является асинхронным (вы должны изменить его имя) и
Parallel.ForEach
не совместим с передачей асинхронного метода. Вот почему я использовалExecuteQuerySegmented
вместо этого. Но, чтобы сделать его более производительным и использовать все преимущества асинхронного метода, вы можете заменить вышеуказанныйForEach
цикл наActionBlock
метод в потоке данных илиParallelForEachAsync
метод расширения из пакета Nuget AsyncEnumerator .2. Это хороший выбор для выполнения независимых параллельных запросов, а затем объединения результатов, даже если его повышение производительности составляет не более 10 процентов. Это дает вам время, чтобы найти наиболее удобный для пользователя запрос. Но никогда не забывайте включать в него все свои ограничения и тестируйте оба способа, чтобы узнать, какой из них лучше подходит вашей проблеме.
3 . Я не уверен, что это хорошее предложение или нет, но сделайте это и посмотрите результаты. Как описано в MSDN :
Таким образом, вы можете поиграть с таймаутом и проверить, есть ли улучшения производительности.
источник
К сожалению, приведенный ниже запрос вводит полное сканирование таблицы :
Вы должны разделить его на два фильтра ключей разделов и запросить их по отдельности, что станет двумя проверками разделов и будет работать более эффективно.
источник
Так что секрет кроется не только в коде, но и в настройке таблиц хранения Azure.
а) Одним из важных способов оптимизации ваших запросов в Azure является внедрение кэширования. Это значительно сократит общее время отклика и, таким образом, позволит избежать узких мест в течение часа пик, который вы упомянули.
б) Кроме того, при запросе сущностей из Azure самый быстрый способ сделать это - и с PartitionKey, и с RowKey. Это единственные проиндексированные поля в Table Storage, и любой запрос, который использует оба из них, будет возвращен в течение нескольких миллисекунд. Поэтому убедитесь, что вы используете и PartitionKey & RowKey.
Подробнее смотрите здесь: https://docs.microsoft.com/en-us/azure/storage/tables/table-storage-design-for-query
Надеюсь это поможет.
источник
примечание: это общий совет по оптимизации запросов к БД.
Возможно, что ORM делает что-то глупое. При выполнении оптимизаций можно перейти на уровень абстракции. Поэтому я предлагаю переписать запрос на языке запросов (SQL?), Чтобы было легче увидеть, что происходит, а также легче оптимизировать.
Ключ к оптимизации поиска - сортировка! Хранение отсортированной таблицы обычно намного дешевле, чем сканирование всей таблицы при каждом запросе! Поэтому, если возможно, сохраняйте таблицу отсортированной по ключу, используемому в запросе. В большинстве решений для баз данных это достигается путем создания ключа индекса.
Другая стратегия, которая хорошо работает, если комбинаций немного, состоит в том, чтобы каждый запрос был отдельной (временной в памяти) таблицей, которая всегда актуальна. Поэтому, когда что-то вставляется, оно также «вставляется» в таблицы «представления». Некоторые решения для баз данных называют это «представлениями».
Более грубой стратегией является создание реплик только для чтения для распределения нагрузки.
источник