5

У меня довольно огромный (30 миллионов строк, до 5-100Kb каждый) Таблица на Azure.
Каждый RowKey является Guid и PartitionKey является первым Guid часть, например:Как уменьшить латентность хранения лазурных таблиц?

PartitionKey = "1bbe3d4b" 
RowKey = "1bbe3d4b-2230-4b4f-8f5f-fe5fe1d4d006" 

Таблица содержит 600 считывает и 600 записи (обновления) в секунду с средней латентности 60ms. Все запросы используют как PartitionKey, так и RowKey.
НО, некоторые чтения принимают до 3000ms (!). В среднем,> 1% от всех прочитанных занимает более 500 мс, и нет никакой корреляции с размером сущности (строка 100 Кбит может быть возвращена в 25 мс и 10 Кб один - в 1500 мс).

Мое приложение представляет собой веб-сайт ASP.Net MVC 4, работающий на 4-5 больших экземплярах.

Я прочитал все статьи MSDN относительно целей производительности Azure Table Storage и уже сделали следующее:

  • UseNagle выключенном
  • Expect100Continue также отключается
  • MaxConnections для настольного клиента устанавливается в 250 (установка 1000-5000 не имеет никакого смысла)

Также я проверил, что:

  • хранение счетчиков мониторинг счета никогда никаких ошибок дроссельных
  • Есть своего рода «волна» в исполнении, хотя они не зависят от нагрузки

Что может быть причиной таких проблем с производительностью и как его улучшить?

+0

расположен Ваш счет хранения в том же регионе, что и ваш сайт? –

+0

Для данного PartitionKey примерно столько строк у вас есть? Для 600 записей и 600 записей, происходят ли они в одном и том же PartitionKey или распространяется на несколько разделов? –

+0

@ zain-rizvi, да, из-за причин, между регионами я не смогу получить в среднем 60 мс. –

ответ

0

Обычно, если конкретный запрос требует сканирования большого количества строк, это займет больше времени. Является ли поведение конкретным запросом/данными? Или вы видите, что производительность варьируется для одних и тех же данных и запросов?

+0

нет никакого сканирования вообще, все запросы содержат как PartitionKey, так и RowKey. Производительность также не зависит от размера записи. –

1

Я использую параметр MergeOption.NoTracking для свойства DataServiceContext.MergeOption для дополнительной производительности, если я не собираюсь обновлять объект в ближайшее время. Вот пример:

var account = CloudStorageAccount.Parse(RoleEnvironment.GetConfigurationSettingValue("DataConnectionString")); 
var tableStorageServiceContext = new AzureTableStorageServiceContext(account.TableEndpoint.ToString(), account.Credentials); 
tableStorageServiceContext.RetryPolicy = RetryPolicies.Retry(3, TimeSpan.FromSeconds(1)); 
tableStorageServiceContext.MergeOption = MergeOption.NoTracking; 
tableStorageServiceContext.AddObject(AzureTableStorageServiceContext.CloudLogEntityName, newItem); 
tableStorageServiceContext.SaveChangesWithRetries(); 

Еще одна проблема может быть, что вы извлекаете весь enity со всеми его свойствами, даже если вы собираетесь использовать только один или два свойства - это, конечно, расточительно, но не может быть легко избежать , Однако, если вы используете Slazure, вы можете использовать прогнозы прогнозов , чтобы получить только свойства объектов, которые вас интересуют, из хранилища таблиц и не более того, что даст вам лучшую производительность запросов. Вот пример:

using SysSurge.Slazure; 
using SysSurge.Slazure.Linq; 
using SysSurge.Slazure.Linq.QueryParser; 

namespace TableOperations 
{ 
    public class MemberInfo 
    { 
     public string GetRichMembers() 
     { 
      // Get a reference to the table storage 
      dynamic storage = new QueryableStorage<DynEntity>("UseDevelopmentStorage=true"); 

      // Build table query and make sure it only return members that earn more than $60k/yr 
      // by using a "Where" query filter, and make sure that only the "Name" and 
      // "Salary" entity properties are retrieved from the table storage to make the 
      // query quicker. 
      QueryableTable<DynEntity> membersTable = storage.WebsiteMembers; 
      var memberQuery = membersTable.Where("Salary > 60000").Select("new(Name, Salary)"); 

      var result = ""; 

      // Cast the query result to a dynamic so that we can get access its dynamic properties 
      foreach (dynamic member in memberQuery) 
      { 
       // Show some information about the member 
       result += "LINQ query result: Name=" + member.Name + ", Salary=" + member.Salary + "<br>"; 
      } 

      return result; 
     } 
    } 
} 

Полное раскрытие информации: Я закодировал Slazure.

Вы также могли бы рассмотреть возможность разбиения на страницы, если вы запрашиваете большие наборы данных, например:

// Retrieve 50 members but also skip the first 50 members 
var memberQuery = membersTable.Where("Salary > 60000").Take(50).Skip(50); 
Смежные вопросы