问题:在反序列化从Redis接收的字节时面临性能下降。
我正在使用REDIS在我的ASP.NET Web应用程序中分发缓存。
为了与我的应用程序中的Redis对话,我使用StackExchange.Redis。
为了序列化/反序列化从/到DTO到/从服务器接收的字节,我使用protobuf-net
我的目标是将100,000个用户的词典(字典(int,用户))存储到Redis中,并在一个请求上多次将其取回。
该词典将位于MyContext.Current.Users属性下。该词典的关键字是用户标识,值是完整的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.
}
}
用户是我的上下文包装器类中公开的属性。
这是我为用户准备的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中:
命令:
private byte[] ObjectToByteArrayFromProtoBuff(Object obj)
{
if (obj == null)
{
return null;
}
using (MemoryStream ms = new MemoryStream())
{
Serializer.Serialize(ms, obj);
return ms.ToArray();
}
}
提取时-将字节转换为DTO,从
命令:
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个用户所花费的时间。
如您所见,将字节反序列化为用户词典(字典用户)所需的平均时间约为1.5至2秒,这太长了,因为我在很多地方都使用该属性从该词典中获取用户信息。
您能告诉我我在做什么错吗?
每次将Redis的100,000个用户列表反序列化到应用程序中然后使用它是否很好? (每个请求还必须在使用Users属性处理请求的地方反序列化)。
将用户的字典/集合/列表或任何其他大型集合以字节为单位存储在Redis中,然后在每次使用时通过反序列化将其取回是否正确?
根据以下帖子Does Stack Exchange use caching and if so, how?
我知道StackExchange大量使用Redis。我相信我的100,000个用户要少得多,它的大小(大约60-80 MB)远远小于StackExchange和其他站点(FB等)的大小。 StackOverflow为何如此快速地反序列化这么大的用户/头等问题列表以及许多其他项(在缓存中)?
我不能在DTO(该列表中的每个项目都具有100多个属性)下使用100,000个用户的字典,并在单个请求或每个请求中多次对它进行反序列化吗?
当我使用HttpRuntime.Cache作为缓存提供程序时,该列表/字典没有问题,但是当我切换到Redis时,反序列化部分会造成障碍,因为它仍然很慢。
我想在这篇文章中添加更多细节。以前,我使用BinaryFormatter反序列化该列表,它的速度比我现在使用的protobufnet慢近10倍。但是,即使使用protobufnet,从字节反序列化这些用户平均也要花费1.5到2秒,这仍然很慢,因为该属性必须在代码中多次使用。
最佳答案
是的,如果您尝试传输大量的许多对象,则将始终需要为整个图形支付带宽+反序列化成本。这里的关键是:不要这样做。每个请求多次获取100,000个用户的列表似乎是完全不必要的,并且非常成为性能瓶颈。
有两种常见方法:
Dictionary<,>
),但仅偶尔获取-如:在后台,每隔5分钟,或者如果您知道它已通过pub / sub 两种方法都可以,并且您希望使用哪种方法取决于您的请求率与数据更改率以及所需数据的最新程度。例如,对于第二种方法,您可以考虑使用redis hash,其中的键与您现在所使用的非常相似,哈希槽键是
int
(或其中的某些字符串/二进制表示形式),而hash- slot值是单个DyveUser
实例的序列化形式。在这里使用散列(相对于每个用户字符串)的优点是,您仍可以通过redis散列命令(例如hgetall
)一次获取/清除/等所有用户。 SE.Redis中带有Hash*
前缀的所有必需的哈希操作均可用。关于c# - 通过protobufnet从Redis反序列化大量用户定义的对象时出现性能问题,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/40722860/