2016-07-04 49 views
0

我需要通過我的系統中的所有用戶創建Web API搜索功能。 客戶端(使用電話)給我發請求使用端點:通過可靠的字典搜索

HTTP 1.1 GET http://sf.cluster:80/ 
Path /search/users?q=Aa&take=10 

q是在搜索字段中輸入一個字符串用戶。 - 手機要顯示多少條目。

我上傳到Azure存儲表的可靠字典89000項。它具有結構:

IReliableDictionary<Guid, string> 

我的搜索方法是這樣的:

public async Task<IEnumerable<UserInfo>> Search(string q, int take) 
    { 
     var usersDictionary = await GetUsersDictionary(); 

     IEnumerable<UserInfo> results; 
     using (var tx = StateManager.CreateTransaction()) 
     { 
      var searchResults = (from r in (await usersDictionary.CreateEnumerableAsync(tx)).ToEnumerable() 
          where r.Value.StartsWith(q, StringComparison.InvariantCultureIgnoreCase) 
          select new UserInfo() 
          { 
           Id = r.Key, 
           Name = r.Value 
          }).Take(take); 

      results = new List<UserInfo>(searchResults); 

      await tx.CommitAsync(); 
     } 

     return results; 
    } 

問題:它可以在手機上很好,我得到了我的預期。但是當我開始用一堆請求開始推送我的端點時(使用Soap UI工具同時約使用大約60個線程),超時開始時間從1秒增加到35秒!看起來我在某個地方犯了一個錯誤,或者選擇了一種錯誤的搜索實現方式。

有人實現了這樣的功能嗎?任何人都可以使用正確的搜索方法嗎?

UPD:實現無狀態服務,其中存儲名稱爲List<string>,並執行相同的操作(通過列表搜索)。結果:150-300ms。它看起來像我應該存儲列表狀態(在有狀態服務),並得到它的要求..

回答

3

我不知道你的ToEnumerable方法的實施是什麼,但我見過的大部分是相當只是採取異步枚舉並將其複製到列表的懶惰實現。現在,有一個可靠的890,000個元素的字典,這是非常低效的。另外,事務就像一個互斥體,所以當你複製這個巨大的列表時,你將鎖定下面的集合。我建議檢查this library中的AsyncEnumerable linq實現,因爲它實現了將linq與服務結構AsyncEnumerable結合使用的有效方法。利用這一點,你的搜索將是這個樣子:

using (var tx = StateManager.CreateTransaction()) 
    { 
     var enumerable = await usersDictionary.CreateEnumerableAsync(tx); 
     results = await enumerable.Where(kvp=>kvp.Value.StartsWith(q, StringComparison.InvariantCultureIgnoreCase)) 
      .Select(kvp=> new UserInfo() 
        { 
         Id = r.Key, 
         Name = r.Value 
        }) 
      .Take(take) 
      .ToListAsync(tx); 
    } 

此外,作爲一個方面說明,既然你不以任何方式則不需要提交事務修改底層集合。提交事務只是一種告訴狀態管理員你已經修改了狀態並完成了更改的方法,然後它將更改後的值傳播給輔助部分。如果這是一個重讀狀態,你甚至可以在次級調用這個方法,但是請注意,寫操作可能還沒有被傳播。

1

ReliableDictinonary返回IAsyncEnumerable,因爲ReliableDictionary會列出某些值。這意味着磁盤IO可能需要讀取一些值。 IAsyncEnumerable允許我們儘可能地阻塞很少的線程。

如果讀取延遲是一個問題,您可以使用通知來構建完全內存中的二級索引。您還可以按值排序次級索引,以提高前綴匹配搜索的效率。以下是相關文檔:https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-services-notifications

對pdylanross的回答進行了一些小修正:CreateEnumerableAsync使用不鎖定集合的mvcc模型提供快照隔離。因此,其他事務可以在快照讀取事務處於運行中的同時繼續進行讀取和寫入操作。欲瞭解更多信息隔離級別:https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-services-reliable-collections

希望這會有所幫助,