2016-11-24 58 views
0

我們是一個旅遊網站。 在特定的搜索條件(Location,CheckIn,CheckOut,Adults,Child)中,我在UI上顯示結果的同時將結果存儲在redis服務器中進行緩存,因爲值不會經常更改。處理併發性

ie鍵將是搜索條件,值將是List.Now當下一個請求出現時,它首先搜索redis,如果所請求的鍵的值在那裏,則結果將從Redis顯示,其他明智的重新搜索將被應用並且結果將被存儲在緩存中。

問題:如果在Redis中沒有結果並且百萬個用戶同時應用搜索相同的搜索條件,那麼這種情況將如何處理?因爲所有搜索都將同時發生並且當時緩存沒有結果。 我們必須牢記性能。

幫助將不勝感激。

回答

0

看來你想避免不必要地調用數據源。然後,如果許多線程同時請求相同的內容,則希望允許查詢數據源並緩存數據,並保留其他數據直到填充數據。

您可能希望有一個decorator,以確保只有一個查詢類型在同一時間執行。您可以使用處於線程安全集合中的對象來表示查詢,並在查詢執行期間對其進行鎖定。

因此,考慮表達您查詢您的數據源的方式的接口:

interface IQueryExecuter<TQuery, TResult> 
{ 
    TResult Execute(TQuery query); 
} 

你可以使用一個線程安全的裝飾對象緩存查詢結果,如果查詢結果不被緩存,只有一個線程執行查詢到數據源:

未經測試的代碼!

class QueryThrottler<TQuery, TResult> : IQueryExecuter<TQuery, TResult> 
{ 
    // do not lock on external objects 
    class QueryObject 
    { 
     public TQuery Query { get; set; } 
    } 

    readonly IQueryExecuter<TQuery, TResult> _inner; 
    readonly ConcurrentDictionary<TQuery, QueryObject> _queries; 

    public QueryThrottler(IQueryExecuter<TQuery, TResult> inner) 
    { 
     _queries = new ConcurrentDictionary<TQuery, QueryObject>(); 
     _inner = inner; 
    } 

    public TResult Execute(TQuery query) 
    { 
     // if it is on cache return the result 
     TResult result; 
     if (!IsCached(query, out result)) 
     { 
      // otherwise lock other threads 
      // on the same query 
      var queryObject = _queries.GetOrAdd(query, k => new QueryObject() { Query = k }); 
      lock (queryObject) 
      { 
       // double check it is not cached already 
       if (!IsCached(query, out result)) 
       { 
        result = _inner.Execute(queryObject.Query); 
        PopulateCache(query, result); 
       } 
      } 
     } 
     return result; 
    } 

    private void PopulateCache(TQuery query, TResult result) 
    { 
     // Save the result in Redis using TQuery as key 
    } 

    private bool IsCached(TQuery query, out TResult result) 
    { 
     // go to redis and check if the query is cached using TQuery as key 
     // if exists, set the result out parameter and return true 
     // otherwise, return false 
     result = default(TResult); 
     return false; 
    } 
} 

此代碼依賴於TQuery的具有GetHashCodeEquals適當的實施方式。

修飾對象(構造函數中的inner)是將對數據源執行實際查詢的對象。

如果你有許多服務器,並希望確保只有一個線程從所有服務器進行實際查詢到的數據源,而不是lock,你可以從StackExchange.Redis使用分佈式鎖像LockTake/LockRelease