Bagaimana cara meningkatkan kecepatan kueri ini?
Kami memiliki sekitar 100 konsumen dalam rentang 1-2 minutes
pelaksanaan kueri berikut. Masing-masing menjalankan ini mewakili 1 menjalankan fungsi konsumsi.
TableQuery<T> treanslationsQuery = new TableQuery<T>()
.Where(
TableQuery.CombineFilters(
TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, sourceDestinationPartitionKey)
, TableOperators.Or,
TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, anySourceDestinationPartitionKey)
)
);
Kueri ini akan menghasilkan sekitar 5.000 hasil.
Kode lengkap:
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);
}
Selama eksekusi ini, ketika ada 100 konsumen, seperti yang Anda lihat permintaan akan mengelompok dan membentuk lonjakan:
Selama lonjakan ini, permintaan seringkali memakan waktu lebih dari 1 menit:
Bagaimana cara meningkatkan kecepatan kueri ini?
c#
azure
azure-table-storage
azure-virtual-network
azure-tablequery
l --''''''---------------- '' '' '' '' '' '' '
sumber
sumber
Jawaban:
Berikut adalah salah satu masalah, Anda menjalankan kueri dan kemudian menyaringnya dari memori menggunakan "wheres" ini. Pindahkan filter sebelum query berjalan yang seharusnya banyak membantu.
Kedua, Anda harus memberikan beberapa batasan baris untuk diambil dari basis data
sumber
Ada 3 hal yang dapat Anda pertimbangkan:
1 . Pertama-tama, singkirkan
Where
klausa yang Anda lakukan pada hasil kueri. Lebih baik untuk memasukkan klausa dalam permintaan sebanyak mungkin (bahkan lebih baik jika Anda memiliki indeks pada tabel Anda memasukkannya juga). Untuk saat ini, Anda dapat mengubah kueri Anda seperti di bawah ini:Karena Anda memiliki sejumlah besar data untuk diambil, lebih baik menjalankan kueri Anda secara paralel. Jadi, Anda harus mengganti metode
do while
loop di dalamExecuteQueryAsync
denganParallel.ForEach
saya menulis berdasarkan Stephen Toub Parallel.While ; Dengan cara ini akan mengurangi waktu eksekusi permintaan. Ini adalah pilihan yang baik karena Anda dapat menghapusResult
ketika Anda melakukan panggilan pada metode ini, Tetapi ada sedikit batasan yang akan saya bicarakan setelah bagian kode ini:Dan kemudian Anda bisa menyebutnya dalam
Get
metode Anda :Seperti yang Anda lihat metode itselft bukan async (Anda harus mengubah namanya) dan
Parallel.ForEach
tidak kompatibel dengan meneruskan metode async. Inilah mengapa saya menggunakanExecuteQuerySegmented
sebagai gantinya. Tetapi, untuk membuatnya lebih berkinerja dan menggunakan semua manfaat metode asinkron Anda dapat menggantiForEach
loop di atas denganActionBlock
metode dalam Dataflow atauParallelForEachAsync
metode ekstensi dari paket AsyncEnumerator Nuget .2. Ini adalah pilihan yang baik untuk menjalankan kueri paralel independen dan kemudian menggabungkan hasilnya, bahkan jika peningkatan kinerjanya paling banyak 10 persen. Ini memberi Anda waktu untuk dapat menemukan kueri ramah kinerja terbaik. Tapi, jangan pernah lupa untuk memasukkan semua kendala Anda di dalamnya, dan uji kedua cara untuk mengetahui mana yang lebih cocok untuk masalah Anda.
3 . Saya tidak yakin itu saran yang bagus atau tidak, tapi lakukan dan lihat hasilnya. Seperti yang dijelaskan dalam MSDN :
Jadi, Anda dapat bermain dengan batas waktu dan memeriksa apakah ada peningkatan kinerja.
sumber
Sayangnya, kueri di bawah ini memperkenalkan pemindaian tabel lengkap :
Anda harus membaginya menjadi dua filter Partition Key dan meminta mereka secara terpisah, yang akan menjadi dua scan partisi dan melakukan lebih efisien.
sumber
Jadi rahasianya tidak hanya dalam kode tetapi juga dalam menyiapkan tabel penyimpanan Azure Anda.
a) Salah satu opsi utama untuk mengoptimalkan kueri Anda di Azure adalah memperkenalkan caching. Ini akan secara drastis mengurangi waktu respons keseluruhan Anda dan dengan demikian menghindari kemacetan selama jam sibuk yang telah Anda sebutkan.
b) Juga, Saat meminta entitas keluar dari Azure, cara tercepat yang mungkin untuk melakukannya adalah dengan PartitionKey dan RowKey. Ini adalah satu-satunya bidang yang diindeks di Penyimpanan Tabel dan permintaan apa pun yang menggunakan keduanya akan dikembalikan dalam hitungan beberapa milidetik. Jadi pastikan Anda menggunakan PartitionKey & RowKey.
Lihat detail lebih lanjut di sini: https://docs.microsoft.com/en-us/azure/storage/tables/table-storage-design-for-query
Semoga ini membantu.
sumber
Catatan: Ini adalah saran umum optimasi permintaan DB.
Mungkin saja ORM melakukan sesuatu yang bodoh. Ketika melakukan optimasi, tidak apa-apa untuk mengecilkan lapisan abstraksi. Jadi saya sarankan menulis ulang permintaan dalam bahasa query (SQL?) Untuk membuatnya lebih mudah untuk melihat apa yang terjadi, dan juga lebih mudah untuk mengoptimalkan.
Kunci untuk mengoptimalkan pencarian adalah penyortiran! Menjaga tabel diurutkan biasanya jauh lebih murah dibandingkan dengan memindai seluruh tabel pada setiap permintaan! Jadi jika memungkinkan, biarkan tabel diurutkan berdasarkan kunci yang digunakan dalam kueri. Dalam sebagian besar solusi basis data, ini dicapai dengan membuat kunci indeks.
Strategi lain yang berfungsi dengan baik jika ada beberapa kombinasi, adalah membuat setiap kueri sebagai tabel terpisah (sementara dalam memori) yang selalu terkini. Jadi ketika sesuatu dimasukkan, itu juga "dimasukkan" ke dalam tabel "view". Beberapa solusi basis data menyebut ini "tampilan".
Strategi yang lebih kasar adalah membuat replika read-only untuk mendistribusikan beban.
sumber