कैसे azure तालिका संग्रहण में पार्टीशन के साथ क्वेरी को गति दें


10

हम इस क्वेरी की गति कैसे बढ़ाते हैं?

निम्नलिखित क्वेरी को निष्पादित करने की अवधि के भीतर हमारे पास लगभग 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 मिनट से अधिक समय लगता है:

यहां छवि विवरण दर्ज करें

हम इस क्वेरी की गति कैसे बढ़ाते हैं?


5000 परिणाम ऐसा लगता है जैसे आप क्वेरी में पर्याप्त रूप से फ़िल्टर नहीं कर रहे हैं। बस 5000 परिणामों को कोड में स्थानांतरित करने पर एक टन नेटवर्क समय खर्च होगा। कोई बात नहीं आप अभी भी बाद में फ़िल्टरिंग करने जा रहे हैं। | हमेशा क्वेरी में एक प्रसंस्करण के रूप में ज्यादा फाइलरिंग करें। आदर्श रूप से उन पंक्तियों पर जिन्हें एक सूचकांक मिला और / या एक गणना दृश्य का परिणाम है।
क्रिस्टोफर

क्या वे "अनुवाद" वस्तुएं बड़ी हैं? क्यों आप पूरे db की तरह gettin` के बजाय कुछ मापदंडों को प्राप्त नहीं करते?
हिरासावा यूई

@ हिरासवाईयुई नहीं वे छोटे हैं
l --''''''--------- '' '' '' '' '' '' '' ''

आपको अधिक फ़िल्टरिंग करना चाहिए, 5000 परिणाम खींचना अर्थहीन लगता है। आपके डेटा को जाने बिना यह बताना असंभव है, लेकिन मैं कहूंगा कि आपको इसे अधिक सार्थक तरीके से विभाजित करने या क्वेरी में किसी प्रकार के फ़िल्टरिंग को लागू करने का एक तरीका निकालने की आवश्यकता होगी
4c74356b41

कितने अलग-अलग विभाजन हैं?
पीटर बॉन्स

जवाबों:


3
  var over1000Results = table.ExecuteQueryAsync(treanslationsQuery).Result.Cast<Translation>();
        return over1000Results.Where(x => x.expireAt > DateTime.Now)
                           .Where(x => x.effectiveAt < DateTime.Now);

यहाँ समस्याओं में से एक है, आप क्वेरी को चला रहे हैं और फिर इन "व्हाट्स" का उपयोग करके इसे मेमोरी से फ़िल्टर कर रहे हैं। क्वेरी को चलाने से पहले फ़िल्टर को ले जाएं जिससे बहुत मदद मिलनी चाहिए।

दूसरा आपको डेटाबेस से पुनर्प्राप्त करने के लिए पंक्तियों की कुछ सीमा प्रदान करनी होगी



3

वहाँ 3 चीजें आप पर विचार कर सकते हैं:

1 है । सबसे पहले, आप अपने Whereक्लॉस से छुटकारा पाएं जो आप क्वेरी परिणाम पर करते हैं। जितना संभव हो सके क्‍लॉज को क्‍लोज में शामिल करना बेहतर है (भले ही आपके टेबल पर कोई इंडेक्स हो तो उन्‍हें भी शामिल करें)। अभी के लिए, आप नीचे दिए अनुसार अपनी क्वेरी बदल सकते हैं:

var translationsQuery = new TableQuery<T>()
.Where(TableQuery.CombineFilters(
TableQuery.CombineFilters(
    TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, sourceDestinationPartitionKey),
    TableOperators.Or,
    TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, anySourceDestinationPartitionKey)
    ),
TableOperators.And,
TableQuery.CombineFilters(
    TableQuery.GenerateFilterConditionForDate("affectiveAt", QueryComparisons.LessThan, DateTime.Now),
    TableOperators.And,
    TableQuery.GenerateFilterConditionForDate("expireAt", QueryComparisons.GreaterThan, DateTime.Now))
));

क्योंकि आपके पास बड़ी मात्रा में डेटा है जिससे यह पता चलता है कि समानांतर में अपने प्रश्नों को चलाना बेहतर है। तो, आप बदलना चाहिए do whileअंदर पाश ExecuteQueryAsyncके साथ विधि Parallel.ForEachमैं के आधार पर लिखा है स्टीफन Toub Parallel.While ; इस तरह यह क्वेरी निष्पादन समय को कम कर देगा। यह एक अच्छा विकल्प है क्योंकि आप Resultइस पद्धति पर कॉल करते समय निकाल सकते हैं , लेकिन इसकी थोड़ी सी सीमा है कि मैं इस कोड के बाद इस बारे में बात करूंगा:

public static IEnumerable<T> ExecuteQueryAsync<T>(this CloudTable table, TableQuery<T> query) where T : ITableEntity, new()
{
    var items = new List<T>();
    TableContinuationToken token = null;

    Parallel.ForEach(new InfinitePartitioner(), (ignored, loopState) =>
    {
        TableQuerySegment<T> seg = table.ExecuteQuerySegmented(query, token);
        token = seg.ContinuationToken;
        items.AddRange(seg);

        if (token == null) // It's better to change this constraint by looking at https://www.vivien-chevallier.com/Articles/executing-an-async-query-with-azure-table-storage-and-retrieve-all-the-results-in-a-single-operation
            loopState.Stop();
    });

    return items;
}

और फिर आप इसे अपनी Getविधि में कह सकते हैं :

return table.ExecuteQueryAsync(translationsQuery).Cast<Translation>();

जैसा कि आप देख सकते हैं कि विधि ही async नहीं है (आपको इसका नाम बदलना चाहिए) और Parallel.ForEachasync विधि में पास होने के साथ संगत नहीं है। यही कारण है कि मैंने ExecuteQuerySegmentedइसके बजाय उपयोग किया है। लेकिन, यह अधिक performant बनाने के लिए और अतुल्यकालिक विधि के सभी लाभों का उपयोग आप ऊपर की जगह ले सकता करने के लिए ForEachके साथ पाश ActionBlockमें विधि Dataflow या ParallelForEachAsyncसे विस्तार विधि AsyncEnumerator Nuget पैकेज

2। यह स्वतंत्र समानांतर प्रश्नों को निष्पादित करने और फिर परिणामों को मर्ज करने का एक अच्छा विकल्प है, भले ही इसका प्रदर्शन सुधार अधिकतम 10 प्रतिशत हो। यह आपको सबसे अच्छा प्रदर्शन अनुकूल क्वेरी खोजने में सक्षम होने का समय देता है। लेकिन, अपने सभी अवरोधों को इसमें शामिल करना कभी न भूलें, और यह जानने के लिए दोनों तरीकों का परीक्षण करें कि कौन सा आपकी समस्या को हल करता है।

। मुझे यकीन नहीं है कि यह एक अच्छा सुझाव है या नहीं, लेकिन इसे करें और परिणाम देखें। MSDN में वर्णित के रूप में :

तालिका सेवा निम्नानुसार सर्वर टाइमआउट को लागू करती है:

  • क्वेरी ऑपरेशन: टाइमआउट अंतराल के दौरान, एक क्वेरी अधिकतम पांच सेकंड तक निष्पादित हो सकती है। यदि क्वेरी पाँच-सेकंड के अंतराल के भीतर पूरी नहीं होती है, तो प्रतिक्रिया में बाद के अनुरोध पर शेष वस्तुओं को पुनः प्राप्त करने के लिए निरंतर टोकन शामिल हैं। अधिक जानकारी के लिए क्वेरी टाइमआउट और पेजेशन देखें।

  • सम्मिलित करें, अद्यतन करें, और ऑपरेशन हटाएँ: अधिकतम समय अंतराल 30 सेकंड है। तीस सेकंड भी सभी सम्मिलित करने, अद्यतन करने और हटाने के कार्यों के लिए डिफ़ॉल्ट अंतराल है।

यदि आप एक टाइमआउट निर्दिष्ट करते हैं जो सेवा के डिफ़ॉल्ट टाइमआउट से कम है, तो आपके टाइमआउट अंतराल का उपयोग किया जाएगा।

इसलिए आप टाइमआउट के साथ खेल सकते हैं और जांच सकते हैं कि कोई प्रदर्शन सुधार हुआ है या नहीं।


2

दुर्भाग्य से, नीचे क्वेरी एक पूर्ण तालिका स्कैन का परिचय देती है :

    TableQuery<T> treanslationsQuery = new TableQuery<T>()
     .Where(
      TableQuery.CombineFilters(
        TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, sourceDestinationPartitionKey)
       , TableOperators.Or,
        TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, anySourceDestinationPartitionKey)
      )
     );

आपको इसे दो विभाजन कुंजी फ़िल्टर में विभाजित करना चाहिए और उन्हें अलग से क्वेरी करना चाहिए, जो दो विभाजन स्कैन बन जाएंगे और अधिक कुशलता से प्रदर्शन करेंगे।


हमने शायद इसके साथ 10% सुधार देखा, लेकिन यह पर्याप्त नहीं है
l

1

इसलिए रहस्य न केवल कोड में है, बल्कि आपके एज़्योर स्टोरेज टेबल को स्थापित करने में भी है।

a) अज़्योर में अपने प्रश्नों का अनुकूलन करने के लिए प्रमुख विकल्पों में से एक है कैशिंग शुरू करना। यह आपके समग्र प्रतिक्रिया समय को बहुत कम कर देगा और इस प्रकार आपके द्वारा उल्लिखित पीक ऑवर के दौरान अड़चन से बचा जा सकता है।

बी) इसके अलावा, जब एज़्योर के बाहर संस्थाओं को क्वेरी करते हैं, तो ऐसा करने का सबसे तेज़ संभव तरीका पार्टिशनके और रोवे दोनों के साथ है। तालिका संग्रहण में ये एकमात्र अनुक्रमित फ़ील्ड हैं और इन दोनों का उपयोग करने वाली कोई भी क्वेरी कुछ मिलीसेकेंड के मामले में वापस आ जाएगी। इसलिए सुनिश्चित करें कि आप PartitionKey & RowKey दोनों का उपयोग करते हैं।

अधिक विवरण यहां देखें: https://docs.microsoft.com/en-us/azure/storage/tables/table-storage-design-for-query

उम्मीद है की यह मदद करेगा।


-1

नोट: यह सामान्य DB क्वेरी ऑप्टिमाइज़ेशन सलाह है।

यह संभव है कि ओआरएम कुछ बेवकूफी कर रहा हो। अनुकूलन करते समय एक अमूर्त परत को नीचे गिराना ठीक है। इसलिए मैं क्वेरी भाषा (एसक्यूएल?) में क्वेरी को फिर से लिखना सुझाव देता हूं कि यह देखना आसान है कि क्या चल रहा है, और ऑप्टिमाइज़ करना भी आसान है।

लुकअप को अनुकूलित करने की कुंजी छंटनी है! हर तालिका पर संपूर्ण तालिका को स्कैन करने की तुलना में एक टेबल को क्रमबद्ध रखना आमतौर पर बहुत सस्ता होता है! इसलिए यदि संभव हो, तो क्वेरी में प्रयुक्त कुंजी द्वारा तालिका को क्रमबद्ध रखें। अधिकांश डेटाबेस समाधान में यह एक इंडेक्स कुंजी बनाकर हासिल किया जाता है।

एक और रणनीति जो कुछ संयोजनों के साथ अच्छी तरह से काम करती है, प्रत्येक क्वेरी को एक अलग (मेमोरी में अस्थायी) तालिका के रूप में रखना है जो हमेशा अद्यतित रहती है। इसलिए जब कुछ डाला जाता है, तो इसे "व्यू" टेबल में भी डाला जाता है। कुछ डेटाबेस समाधान इसे "विचार" कहते हैं।

लोड को वितरित करने के लिए केवल-पढ़ने के लिए प्रतिकृतियां बनाने के लिए एक अधिक क्रूर रणनीति है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.