2017-06-21 17 views
5

मैंने अपना नवीनतम अपडेट लिखा, और फिर स्टैक ओवरफ़्लो से निम्न त्रुटि मिली: "बॉडी 30000 वर्णों तक सीमित है; आपने 38676 दर्ज किया।"कॉसमॉस डीबी क्वेरी प्रदर्शन

यह कहना उचित है कि मैं अपने रोमांचों को दस्तावेज करने में बहुत वर्बोज़ रहा हूं, इसलिए मैंने यहां जो कुछ भी संक्षिप्त किया है, उसे मैंने फिर से लिखा है।

मैंने अपनी (लंबी) मूल पोस्ट और pastebin पर अपडेट संग्रहीत किए हैं। मुझे नहीं लगता कि बहुत से लोग उन्हें पढ़ेंगे, लेकिन मैंने उनसे बहुत मेहनत की है ताकि उन्हें खोना न अच्छा लगे।


मैं एक संग्रह है जो 100,000 CosmosDB उपयोग करने के लिए सीखने के लिए और निष्पादन परीक्षण जैसी चीजों के लिए दस्तावेज शामिल है।

इनमें से प्रत्येक दस्तावेज़ में Location संपत्ति है जो एक GeoJSON Point है।

documentation के अनुसार, एक GeoJSON बिंदु स्वचालित रूप से अनुक्रमित किया जाना चाहिए।

Azure ब्रह्मांड डीबी अंक, बहुभुज का स्वत: अनुक्रमण, और LineStrings

मैं अपने संग्रह के लिए अनुक्रमण नीति देख लिया है का समर्थन करता है, और यह स्वत: बिंदु अनुक्रमण के लिए प्रविष्टि है:

{ 
    "automatic":true, 
    "indexingMode":"Consistent", 
    "includedPaths":[ 
     { 
     "path":"/*", 
     "indexes":[ 
      ... 
      { 
       "kind":"Spatial", 
       "dataType":"Point" 
      }, 
      ...     
     ] 
     } 
    ], 
    "excludedPaths":[ ] 
} 

मैं सूचीबद्ध करने के लिए एक तरीका ढूंढ रहा हूं, या अन्यथा बनाई गई इंडेक्स से पूछताछ कर रहा हूं, लेकिन मुझे अभी तक ऐसी कोई चीज़ नहीं मिली है, इसलिए मैं यह पुष्टि करने में सक्षम नहीं हूं कि यह संपत्ति निश्चित रूप से हो रही है अनुक्रमित।

मैंने एक GeoJSON Polygon बनाया, और उसके बाद मेरे दस्तावेज़ों से पूछने के लिए उपयोग किया।

यह मेरा क्वेरी है:

var query = client 
    .CreateDocumentQuery<TestDocument>(documentCollectionUri) 
    .Where(document => document.Type == this.documentType && document.Location.Intersects(target.Area)); 

और मैं उसके बाद निम्न विधि के लिए कि क्वेरी वस्तु पारित तो मैं परिणाम का अनुरोध इकाइयों में इस्तेमाल किया ट्रैक करते समय प्राप्त कर सकते हैं:

protected async Task<IEnumerable<T>> QueryTrackingUsedRUsAsync(IQueryable<T> query) 
{ 
    var documentQuery = query.AsDocumentQuery(); 
    var documents = new List<T>(); 

    while (documentQuery.HasMoreResults) 
    { 
     var response = await documentQuery.ExecuteNextAsync<T>(); 

     this.AddUsedRUs(response.RequestCharge); 

     documents.AddRange(response); 
    } 

    return documents; 
} 

बिंदु स्थानों यूके पते के लाखों से यादृच्छिक रूप से चुने गए हैं, इसलिए उनके पास काफी यथार्थवादी फैलाव होना चाहिए।

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

170717.151 एमएस में, 3971.92 आरयू का उपयोग करते हुए, इस क्वेरी के एक उदाहरण चलाने ने 8728 दस्तावेज लौटाए, जो कि 171 सेकेंड के नीचे है, या केवल 3 मिनट से कम है।

3918 RU/171 एस = 22.91 RU/एस

मैं वर्तमान में है प्रवाह क्षमता (RU/s), सबसे कम मान पर सेट पर 400 RU/एस।

यह मेरी समझ थी कि यह आरक्षित स्तर है जिसे आप प्राप्त करने की गारंटी देते हैं। आप कभी-कभी उस स्तर से ऊपर "फट" कर सकते हैं, लेकिन बहुत बार ऐसा करते हैं और आपको अपने आरक्षित स्तर पर वापस ले जाया जाएगा।

23 आरयू/एस की "क्वेरी स्पीड" जाहिर है, 400 आरयू/एस की थ्रूपुट सेटिंग से काफी कम है।

मैं क्लाइंट को "स्थानीय रूप से" चला रहा हूं यानी मेरे कार्यालय में, और Azure डेटा सेंटर में नहीं।

प्रत्येक दस्तावेज़ आकार में लगभग 500 बाइट्स (0.5 केबी) है।

तो क्या हो रहा है?

क्या मैं कुछ गलत कर रहा हूं?

क्या मैं गलत समझ रहा हूं कि आरयू/एस के संबंध में मेरी क्वेरी को कैसे थकाया जा रहा है?

क्या यह गति है जिस पर जियोस्पेटियल इंडेक्स संचालित होते हैं, और इसलिए मुझे सबसे अच्छा प्रदर्शन मिलेगा?

भू-स्थानिक सूचकांक में इस्तेमाल नहीं किया जा रहा है?

वहाँ एक रास्ता मैं बनाया अनुक्रमण करता देख सकते हैं है?

क्या कोई तरीका है कि मैं जांच कर सकता हूं कि इंडेक्स का उपयोग किया जा रहा है या नहीं?

क्या कोई तरीका है कि मैं क्वेरी प्रोफाइल कर सकता हूं और कहां खर्च किया जा रहा है, इस बारे में मीट्रिक प्राप्त कर सकता हूं? जैसे रों उनके प्रकार द्वारा दस्तावेजों को देख इस्तेमाल किया गया था, रों उन्हें GeoSpatially छानने इस्तेमाल किया गया था, और एस डेटा स्थानांतरित करने के लिए इस्तेमाल किया गया था।

UPDATE 1

यहाँ बहुभुज मैं क्वेरी में उपयोग कर रहा हूँ है:

Area = new Polygon(new List<LinearRing>() 
{ 
    new LinearRing(new List<Position>() 
    { 
     new Position(1.8567 ,51.3814), 

     new Position(0.5329 ,51.4618), 
     new Position(0.2477 ,51.2588), 
     new Position(-0.5329 ,51.2579), 
     new Position(-1.17 ,51.2173), 
     new Position(-1.9062 ,51.1958), 
     new Position(-2.5434 ,51.1614), 
     new Position(-3.8672 ,51.139), 
     new Position(-4.1578 ,50.9137), 
     new Position(-4.5373 ,50.694), 
     new Position(-5.1496 ,50.3282), 
     new Position(-5.2212 ,49.9586), 
     new Position(-3.7049 ,50.142), 
     new Position(-2.1698 ,50.314), 
     new Position(0.4669 ,50.6976), 

     new Position(1.8567 ,51.3814) 
    }) 
}) 

मैं भी यह (के बाद से अंगूठी उन्मुखीकरण मामलों) पीछे की कोशिश की है, लेकिन उलट बहुभुज के साथ क्वेरी काफी समय लगा (मैं समय हाथ की जरूरत नहीं है) और 91,272 आइटम लौट आए।

इसके अलावा, समन्वय अक्षांश/अक्षांश के बोलते समय पारंपरिक क्रम के बजाए this is how GeoJSON expects them (यानी एक्स/वाई) के रूप में, अक्षांश/अक्षांश के रूप में निर्दिष्ट हैं।

जियोज़सन विनिर्देश पहले अक्षांश और अक्षांश दूसरे को निर्दिष्ट करता है।

अद्यतन 2

यहाँ मेरी दस्तावेजों में से एक के लिए JSON है:

{ 
    "GeoTrigger": null, 
    "SeverityTrigger": -1, 
    "TypeTrigger": -1, 
    "Name": "13, LONSDALE SQUARE, LONDON, N1 1EN", 
    "IsEnabled": true, 
    "Type": 2, 
    "Location": { 
     "$type": "Microsoft.Azure.Documents.Spatial.Point, Microsoft.Azure.Documents.Client", 
     "type": "Point", 
     "coordinates": [ 
      -0.1076407397346815, 
      51.53970315059827 
     ] 
    }, 
    "id": "0dc2c03e-082b-4aea-93a8-79d89546c12b", 
    "_rid": "EQttAMGhSQDWPwAAAAAAAA==", 
    "_self": "dbs/EQttAA==/colls/EQttAMGhSQA=/docs/EQttAMGhSQDWPwAAAAAAAA==/", 
    "_etag": "\"42001028-0000-0000-0000-594943fe0000\"", 
    "_attachments": "attachments/", 
    "_ts": 1497973747 
} 

अद्यतन 3

मैं इस मुद्दे का एक न्यूनतम प्रजनन बनाया है, और मैंने पाया मुद्दा नहीं रह गया है हुई।

यह इंगित करता है कि समस्या वास्तव में मेरे अपने कोड में थी।

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

एक बिंदु पर मैं एक कस्टम ContractResolver का उपयोग कर रहा था और इसकी आवश्यकता नहीं होने के बाद मैंने इसे हटा नहीं दिया था।

यहाँ हमलावर प्रजनन कोड है:

using System; 
using System.Collections.Generic; 
using System.Configuration; 
using System.Diagnostics; 
using System.Linq; 
using System.Runtime.CompilerServices; 
using System.Threading; 
using System.Threading.Tasks; 
using Microsoft.Azure.Documents; 
using Microsoft.Azure.Documents.Client; 
using Microsoft.Azure.Documents.Spatial; 
using Newtonsoft.Json; 
using Newtonsoft.Json.Serialization; 

namespace Repro.Cli 
{ 
    public class Program 
    { 
     static void Main(string[] args) 
     { 
      JsonConvert.DefaultSettings =() => 
      { 
       return new JsonSerializerSettings 
       { 
        ContractResolver = new PropertyNameMapContractResolver(new Dictionary<string, string>() 
        { 
         { "ID", "id" } 
        }) 
       }; 
      }; 

      //AJ: Init logging 
      Trace.AutoFlush = true; 
      Trace.Listeners.Add(new ConsoleTraceListener()); 
      Trace.Listeners.Add(new TextWriterTraceListener("trace.log")); 

      //AJ: Increase availible threads 
      //AJ: https://docs.microsoft.com/en-us/azure/storage/storage-performance-checklist#subheading10 
      //AJ: https://github.com/Azure/azure-documentdb-dotnet/blob/master/samples/documentdb-benchmark/Program.cs 
      var minThreadPoolSize = 100; 
      ThreadPool.SetMinThreads(minThreadPoolSize, minThreadPoolSize); 

      //AJ: https://docs.microsoft.com/en-us/azure/cosmos-db/performance-tips 
      //AJ: gcServer enabled in app.config 
      //AJ: Prefer 32-bit disabled in project properties 

      //AJ: DO IT 
      var program = new Program(); 

      Trace.TraceInformation($"Starting @ {DateTime.UtcNow}"); 
      program.RunAsync().Wait(); 
      Trace.TraceInformation($"Finished @ {DateTime.UtcNow}"); 

      //AJ: Wait for user to exit 
      Console.WriteLine(); 
      Console.WriteLine("Hit enter to exit..."); 
      Console.ReadLine(); 
     } 

     public async Task RunAsync() 
     { 
      using (new CodeTimer()) 
      { 
       var client = await this.GetDocumentClientAsync(); 
       var documentCollectionUri = UriFactory.CreateDocumentCollectionUri(ConfigurationManager.AppSettings["databaseID"], ConfigurationManager.AppSettings["collectionID"]); 

       //AJ: Prepare Test Documents 
       var documentCount = 10000; //AJ: 10,000 
       var documentsForUpsert = this.GetDocuments(documentCount); 
       await this.UpsertDocumentsAsync(client, documentCollectionUri, documentsForUpsert); 

       var allDocuments = this.GetAllDocuments(client, documentCollectionUri); 

       var area = this.GetArea(); 
       var documentsInArea = this.GetDocumentsInArea(client, documentCollectionUri, area); 
      } 
     } 

     private async Task<DocumentClient> GetDocumentClientAsync() 
     { 
      using (new CodeTimer()) 
      { 
       var serviceEndpointUri = new Uri(ConfigurationManager.AppSettings["serviceEndpoint"]); 
       var authKey = ConfigurationManager.AppSettings["authKey"]; 

       var connectionPolicy = new ConnectionPolicy 
       { 
        ConnectionMode = ConnectionMode.Direct, 
        ConnectionProtocol = Protocol.Tcp, 
        RequestTimeout = new TimeSpan(1, 0, 0), 
        RetryOptions = new RetryOptions 
        { 
         MaxRetryAttemptsOnThrottledRequests = 10, 
         MaxRetryWaitTimeInSeconds = 60 
        } 
       }; 

       var client = new DocumentClient(serviceEndpointUri, authKey, connectionPolicy); 

       await client.OpenAsync(); 

       return client; 
      } 
     } 

     private List<TestDocument> GetDocuments(int count) 
     { 
      using (new CodeTimer()) 
      { 
       return External.CreateDocuments(count); 
      } 
     } 

     private async Task UpsertDocumentsAsync(DocumentClient client, Uri documentCollectionUri, List<TestDocument> documents) 
     { 
      using (new CodeTimer()) 
      { 
       //TODO: AJ: Parallelise 
       foreach (var document in documents) 
       { 
        await client.UpsertDocumentAsync(documentCollectionUri, document); 
       } 
      } 
     } 

     private List<TestDocument> GetAllDocuments(DocumentClient client, Uri documentCollectionUri) 
     { 
      using (new CodeTimer()) 
      { 
       var query = client 
        .CreateDocumentQuery<TestDocument>(documentCollectionUri, new FeedOptions() 
        { 
         MaxItemCount = 1000 
        }); 

       var documents = query.ToList(); 

       return documents; 
      } 
     } 

     private Polygon GetArea() 
     { 
      //AJ: Longitude,Latitude i.e. X/Y 
      //AJ: Ring orientation matters 
      return new Polygon(new List<LinearRing>() 
      { 
       new LinearRing(new List<Position>() 
       { 
        new Position(1.8567 ,51.3814), 

        new Position(0.5329 ,51.4618), 
        new Position(0.2477 ,51.2588), 
        new Position(-0.5329 ,51.2579), 
        new Position(-1.17 ,51.2173), 
        new Position(-1.9062 ,51.1958), 
        new Position(-2.5434 ,51.1614), 
        new Position(-3.8672 ,51.139), 
        new Position(-4.1578 ,50.9137), 
        new Position(-4.5373 ,50.694), 
        new Position(-5.1496 ,50.3282), 
        new Position(-5.2212 ,49.9586), 
        new Position(-3.7049 ,50.142), 
        new Position(-2.1698 ,50.314), 
        new Position(0.4669 ,50.6976), 

        //AJ: Last point must be the same as first point 
        new Position(1.8567 ,51.3814) 
       }) 
      }); 
     } 

     private List<TestDocument> GetDocumentsInArea(DocumentClient client, Uri documentCollectionUri, Polygon area) 
     { 
      using (new CodeTimer()) 
      { 
       var query = client 
        .CreateDocumentQuery<TestDocument>(documentCollectionUri, new FeedOptions() 
        { 
         MaxItemCount = 1000 
        }) 
        .Where(document => document.Location.Intersects(area)); 

       var documents = query.ToList(); 

       return documents; 
      } 
     } 
    } 

    public class TestDocument : Resource 
    { 
     public string Name { get; set; } 
     public Point Location { get; set; } //AJ: Longitude,Latitude i.e. X/Y 

     public TestDocument() 
     { 
      this.Id = Guid.NewGuid().ToString("N"); 
     } 
    } 

    //AJ: This should be "good enough". The times being recorded are seconds or minutes. 
    public class CodeTimer : IDisposable 
    { 
     private Action<TimeSpan> reportFunction; 
     private Stopwatch stopwatch = new Stopwatch(); 

     public CodeTimer([CallerMemberName]string name = "") 
      : this((ellapsed) => 
      { 
       Trace.TraceInformation($"{name} took {ellapsed}, or {ellapsed.TotalMilliseconds} ms."); 
      }) 
     { } 

     public CodeTimer(Action<TimeSpan> report) 
     { 
      this.reportFunction = report; 
      this.stopwatch.Start(); 
     } 

     public void Dispose() 
     { 
      this.stopwatch.Stop(); 
      this.reportFunction(this.stopwatch.Elapsed); 
     } 
    } 

    public class PropertyNameMapContractResolver : DefaultContractResolver 
    { 
     private Dictionary<string, string> propertyNameMap; 

     public PropertyNameMapContractResolver(Dictionary<string, string> propertyNameMap) 
     { 
      this.propertyNameMap = propertyNameMap; 
     } 

     protected override string ResolvePropertyName(string propertyName) 
     { 
      if (this.propertyNameMap.TryGetValue(propertyName, out string resolvedName)) 
       return resolvedName; 

      return base.ResolvePropertyName(propertyName); 
     } 
    } 
} 
+0

क्या आप कृपया अपने बहुभुज को दिखाने के लिए अपना प्रश्न संपादित कर सकते हैं? –

+0

हाँ, मैंने इसे कोड फॉर्म में शामिल किया है, और अंगूठी अभिविन्यास के बारे में जानकारी जोड़ा है। – AndyJ

+0

क्या आप कृपया अपने संग्रह का नमूना दस्तावेज प्रदान कर सकते हैं? – Amor

उत्तर

1

मैं एक कस्टम ContractResolver उपयोग कर रहा था और उस जाहिर नेट एसडीके से DocumentDB वर्गों के प्रदर्शन पर एक बड़ा प्रभाव रहा था।

यह था कि मैं कितना ContractResolver की स्थापना की गई थी:

JsonConvert.DefaultSettings =() => 
{ 
    return new JsonSerializerSettings 
    { 
     ContractResolver = new PropertyNameMapContractResolver(new Dictionary<string, string>() 
     { 
      { "ID", "id" } 
     }) 
    }; 
}; 

और यह कि यह कैसे लागू किया गया था है:

public class PropertyNameMapContractResolver : DefaultContractResolver 
{ 
    private Dictionary<string, string> propertyNameMap; 

    public PropertyNameMapContractResolver(Dictionary<string, string> propertyNameMap) 
    { 
     this.propertyNameMap = propertyNameMap; 
    } 

    protected override string ResolvePropertyName(string propertyName) 
    { 
     if (this.propertyNameMap.TryGetValue(propertyName, out string resolvedName)) 
      return resolvedName; 

     return base.ResolvePropertyName(propertyName); 
    } 
} 

समाधान आसान था, JsonConvert.DefaultSettings तो ContractResolver 'isn निर्धारित नहीं करते टी इस्तेमाल किया।

परिणाम:

मैं 21799.0221 एमएस, जो 22 सेकंड है में अपने स्थानिक क्वेरी प्रदर्शन करने में सक्षम था।

पहले इसे 170717.151 एमएस लिया गया, जो 2 मिनट 50 सेकंड है।

यह लगभग 8x तेज है!

संबंधित मुद्दे