c# - 通过 protobufnet 从 Redis 反序列化大量用户定义对象时出现性能问题

标签 c# redis protobuf-net stackexchange.redis

问题:在反序列化从 Redis 接收到的字节时性能下降。

我正在使用 REDIS用于在我的 ASP.NET Web 应用程序中分发缓存。

为了从我的应用程序中与 Redis 对话,我使用了 StackExchange.Redis .

为了序列化/反序列化从服务器接收到/从服务器接收到的字节,我正在使用 protobuf-net

我的目标是将包含 100,000 个用户的字典 (Dictionary (int, User)) 存储到 Redis 中,并根据单个请求多次检索它

该字典将驻留在 MyContext.Current.Users 属性下。该字典的键是用户 ID,值是完整的 dto。我现在遇到的问题是,从字节中反序列化列表 100,000 个用户需要 1.5-2 秒(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.
    }
}

Users 是在我的上下文包装器类中公开的属性。

这是我为用户准备的 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; }

    .
    .
    .
    .
}

这是我在 StackExchange.Redis 的帮助下用来与 Redis 对话的代码片段:

在存储时——将我的DTO转换为字节,以便它可以存储到Redis中:

db.StringSet(cacheKey, bytes, 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 Performance Profiler 的屏幕截图,显示了 protobuf-net 从 Redis 提供的字节中反序列化 100,000 个用户所花费的时间。

enter image description here

如您所见,将字节反序列化为用户字典 (Dictionary Users) 所需的平均时间约为 1.5 到 2 秒,这太多了,因为我在很多地方使用该属性从该字典中获取用户信息.

你能告诉我我做错了什么吗?

每次从Redis反序列化100000个用户列表到应用程序中然后使用它好吗? (每个请求还必须在使用 Users 属性处理请求的地方反序列化)。

以字节为单位将字典/集合/用户列表或任何其他大型集合存储到 Redis 中,然后在每次我们必须使用它时通过反序列化取回它是否正确?

根据以下帖子Does Stack Exchange use caching and if so, how? 我了解到 StackExchange 大量使用 Redis。我相信我的 100,000 个用户要少得多,而且它的大小(大约 60-80 MB)也远小于 StackExchange 和其他网站(FB 等)所拥有的。为什么 StackOverflow 如此快速地反序列化如此大的用户/热门问题列表和许多其他项目(在缓存中)?

我不能在缓存下使用包含 100,000 个用户的字典和 DTO(该列表中的每个项目都有超过 100 个属性)并在单个请求或每个请求中多次反序列化它吗?

当我使用 HttpRuntime.Cache 作为缓存提供程序时,我对该列表/字典没有任何问题,但是当我切换到 Redis 时,反序列化部分造成了阻碍,因为它仍然很慢。

我想在这篇文章中再添加一个细节。以前我用的是BinaryFormatter反序列化该列表,它比我现在使用的 protobufnet 慢近 10 倍。但是,即使使用 protobufnet,平均需要 1.5 到 2 秒才能从字节中反序列化这些用户,这仍然很慢,因为该属性必须在代码中多次使用。

最佳答案

是的,如果您尝试传输包含许多对象的大型集合,您将始终需要为整个图支付带宽 + 反序列化成本。这里的关键是:不要那样做。每次请求多次获取 100,000 个用户的列表似乎完全没有必要,而且在很大程度上是性能瓶颈。

有两种常见的方法:

  • 使用大对象(Dictionary<,>),但只是偶尔获取它 - 例如:在后台,每 5 分钟一次,或者如果您知道它已通过 pub/sub 更改
  • 只处理每个请求所需的谨慎对象,将其余的留给 redis 服务器;每个请求最多只获取一次

这两种方法都可以,您更喜欢哪种方法可能取决于您的请求率与数据更改率以及您要求数据的最新程度等因素。例如,对于第二种方法,您可以考虑使用 redis hash , key 与您现在使用的非常相似,哈希槽 key 是 int (或一些字符串/二进制表示),哈希槽值是 单个 DyveUser 的序列化形式实例。在这里使用 hash 的优点(与每个用户的字符串相反)是您仍然可以通过 redis 哈希命令(例如 hgetall)一次获取/清除/等所有用户. SE.Redis 中所有必需的哈希操作都可用 Hash*前缀。

关于c# - 通过 protobufnet 从 Redis 反序列化大量用户定义对象时出现性能问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40722860/

相关文章:

c# - 如何使用 ProtoGen 从 proto 文件生成结构

c# - protobuf-net v2 r450继承

c# - linq 到实体 : linq query performance optimization

c# - 如何在不迭代的情况下获取、排序和计数舍入插值数据?

c# - 如何从值类型 T 的 List<List<T>> 中删除重复项?

python - celery 恢复从队列中消费

ruby - 如何将此 Ruby 代码转换为 Redis 命令行?

node.js - 将 Redis 用于 session 存储似乎不起作用

c# - ProtoBuf - 迁移到新版本

c# - 在方法范围之外定义 "var"类型