2016-11-21 88 views
1

問題:在反序列化從Redis接收到的字節時,性能降低。通過protobufnet反序列化來自Redis的大量用戶定義對象集時的性能問題

我正在使用REDIS在我的ASP.NET Web應用程序中分發緩存。

爲了從我的應用程序中與Redis交談,我使用的是StackExchange.Redis

爲了序列化/反序列的字節接收到/從服務器從/到DTO我使用protobuf-net

我的目標是存儲100,000個用戶(詞典(INT,用戶))的字典成Redis的並通過單個請求多次檢索它。

該字典將駐留在MyContext.Current.Users屬性中。該字典的關鍵字是用戶標識,值是完整的dto。我現在的問題是,它需要1.5-2秒將列表中的100,000個用戶從字節反序列化(Redis給我的字節)。我必須多次在我的請求中使用該屬性。

public Dictionary<int, User> Users 
{ 
    get 
    { 
     // Get users from Redis cache. 
     // Save it in Redis cache if it is not there before and then get it. 
    } 
} 

用戶是暴露在我的上下文包裝類中的屬性。

這裏是DTO我有用戶(這DTO是有超過100個屬性):

[ProtoContract] 
public class User 
{ 
    [ProtoMember(1)] 
    public string UserName { get; set; } 

    [ProtoMember(2)] 
    public string UserID { get; set; } 

    [ProtoMember(3)] 
    public string FirstName { get; set; } 

    . 
    . 
    . 
    . 
} 

這裏是我使用的交談與Redis的與StackExchange的幫助的代碼段.Redis:

在存儲的時候 - 我的DTO轉換爲字節,因此,它可以存儲到Redis的:

db.StringSet(cacheKey,字節,slidingExpiration)

命令:

private byte[] ObjectToByteArrayFromProtoBuff(Object obj) 
{ 
    if (obj == null) 
    { 
     return null; 
    } 

    using (MemoryStream ms = new MemoryStream()) 
    { 
     Serializer.Serialize(ms, obj); 
     return ms.ToArray(); 
    } 
} 

在抓取的時間 - 轉換字節DTO,從

db.StringGet(cacheKey)接收到的字節;

命令:

private T ByteArrayToObjectFromProtoBuff<T>(byte[] arrBytes) 
{ 
    if (arrBytes != null) 
    { 
     using (MemoryStream ms = new MemoryStream(arrBytes)) 
     { 
      var obj = Serializer.Deserialize<T>(ms); 
      return obj; 
     } 
    } 
    return default(T); 
} 

這裏是ANTS性能分析器的表示由protobuf網反序列化從字節10萬個用戶其中Redis的是給所花費的時間的屏幕截圖。

enter image description here

正如你所看到的字節反序列化到用戶(詞典用戶)的詞典所用的平均時間約爲1.5〜2秒,這實在是太多了,因爲我使用的財產,在這麼多的地方從該字典中獲取用戶信息。

你能讓我知道我在做什麼錯嗎?

每次將Redis的100,000個用戶列表反序列化到應用程序中,然後使用它,這是否很好? (每個請求還必須反序列化用戶屬性用於處理請求的地方)。

將字典/集合/用戶列表或任何其他大型集合以字節爲單位存儲到Redis中,然後通過反序列化將其重新獲取,每次我們必須使用它時,它是正確的嗎?

根據下面的帖子Does Stack Exchange use caching and if so, how? 我知道StackExchange被大量使用Redis的。我相信我的10萬用戶遠遠少於它的規模(大約60-80 MB),遠遠低於StackExchange和其他站點(FB等)的規模。 StackOverflow如何快速反序列化如此龐大的用戶/熱門問題列表和許多其他項目(緩存中)?

我不能在單個請求或每個請求中使用帶有DTO的DTO(具有超過100個屬性的每個項目)100,000個用戶的字典並將其反序列化多次?

我對此列表/字典中沒有問題,當我使用HttpRuntime.Cache作爲緩存提供者,但是當我切換到Redis的反序列化部分導致了hinderence,因爲它仍然緩慢。

我想添加一個更多的細節到這篇文章。以前我使用BinaryFormatter來反序列化這個列表,它比我現在使用的protobufnet慢了近10倍。但是,即使使用protobufnet,平均需要1.5到2秒才能將這些用戶從字節反序列化,這仍然很慢,因爲該屬性必須在代碼中多次使用。

回答

4

是的,如果您嘗試傳輸多個對象的大量集合,則必須始終爲整個圖表支付帶寬+反序列化成本。關鍵在於:不要這樣做。每個請求獲取100,000個用戶的列表多次似乎完全沒有必要,並且非常成爲性能瓶頸。

有兩種常用的方法:

  • 與大對象(Dictionary<,>)工作,但只是很偶爾獲取它 - 作爲:在後臺,每隔5分鐘,或者如果你知道它有通過發佈/訂閱
  • 更改通過發佈/分
  • 與您需要每個請求只需謹慎的對象一起工作,並將剩下的其餘部分留在redis服務器上;每個請求最多隻能獲取一次

任何一種方法都可以,您喜歡的方式可能取決於您的請求速率與數據更改速率以及您需要的最新數據。例如,對於第二種方法,您可以考慮使用redis hash,其中密鑰與您現在使用的非常相似,散列槽密鑰是int(或其中的某個字符串/二進制表示),而散列 - 插槽值是單個DyveUser實例的序列化形式。在這裏使用散列的優點(與每個用戶字符串相反)是,您仍然可以通過redis散列命令(例如hgetall)一次獲取/清除/ etc所有用戶。所有需要的哈希運算都可以在SE.Redis中以Hash*前綴獲得。

+0

感謝您寶貴的回覆。 – Raghav

相關問題