5

Ich habe eine ziemlich große (30 Millionen Zeilen, bis zu 5-100Kb) Tabelle auf Azure.
Jeder RowKey ist eine GUID und PartitionKey ist ein erster Teil Guid, zum Beispiel:Wie kann die Azure Table Storage-Latenz verringert werden?

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

Tabelle hat 600 liest und schreibt, 600 (Updates) pro Sekunde mit einer durchschnittlichen Latenzzeit von 60ms. Alle Abfragen verwenden beide PartitionKey und RowKey.
ABER, einige liest nehmen bis zu 3000ms (!). Im Durchschnitt benötigen> 1% aller Lesevorgänge mehr als 500 ms und es gibt keine Korrelation mit der Entitätsgröße (100 KB-Zeilen können in 25 ms und 10 KB in 1.500 ms zurückgegeben werden).

Meine Anwendung ist eine ASP.Net MVC 4-Website, die auf 4-5 großen Instanzen ausgeführt wird.

Ich habe alle MSDN Artikel über Azure Tabellen Speicher Leistungsziele gelesen und hat bereits die folgenden:

  • UseNagle Off
  • Expect100Continue auch
  • gedreht wird
  • MaxConnections für Tisch-Client deaktiviert ist auf 250 gesetzt (Einstellung 1000-5000 macht keinen Sinn)

Ich habe auch überprüft, dass:

  • Storage-Konto Überwachungszähler haben keine Drosselung Fehler
  • Es gibt eine Art von „Wellen“ in der Leistung, obwohl sie nicht auf Last

abhängt Was ist der Grund für solche Performance-Probleme sein könnte und wie man es verbessert?

+0

Ist Ihr Speicherkonto in der Region Ihrer Website liegt –

+0

Für einen bestimmten PartitionKey, ungefähr wie viele Zeilen haben Sie? Findet diese bei den 600 Lesevorgängen und 600 Schreibvorgängen im selben PartitionKey statt oder ist sie auf mehrere Partitionen verteilt? –

+0

@ Zain-Rizvi, ja, aus Gründen, zwischen den Regionen werde ich nicht durchschnittlich 60ms bekommen. –

Antwort

0

Wenn eine bestimmte Abfrage das Scannen einer großen Anzahl von Zeilen erfordert, dauert das normalerweise länger. Ist das Verhalten, das Sie gerade sehen, eine Abfrage/Daten? Oder sehen Sie, dass die Leistung für dieselben Daten und dieselbe Abfrage variiert?

+0

Es gibt überhaupt kein Scannen, alle Abfragen enthalten sowohl PartitionKey als auch RowKey. Die Leistung hängt auch nicht von der Größe des Eintrags ab. –

1

Ich verwende die MergeOption.NoTracking Einstellung auf der DataServiceContext.MergeOption Eigenschaft für zusätzliche Leistung, wenn ich nicht die Absicht habe, die Entität in absehbarer Zeit zu aktualisieren. Hier ein Beispiel:

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(); 

Ein weiteres Problem könnte sein, dass Sie die gesamten enity abrufen mit all seinen Eigenschaften, auch wenn Sie nur eine oder zwei Eigenschaften verwendet werden soll - dies natürlich ist verschwenderisch, aber nicht leicht vermieden werden kann . Wenn Sie jedoch Slazure verwenden, können Sie Abfrageprojektionen verwenden, um nur die Entitätseigenschaften abzurufen, an denen Sie interessiert sind, aus dem Tabellenspeicher und nicht mehr, was zu einer besseren Abfrageleistung führt. Hier ein Beispiel:

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; 
     } 
    } 
} 

Volle Enthüllung: Ich kodierte Slazure.

Sie könnten auch Paginierung betrachten, wenn Sie große Datenmengen abrufen, zB:

// Retrieve 50 members but also skip the first 50 members 
var memberQuery = membersTable.Where("Salary > 60000").Take(50).Skip(50); 
Verwandte Themen