在使用分布式缓存的时候,都不可避免的要做这样一步操作,将数据序列化后再存储到缓存中去。
序列化这一操作,或许是显式的,或许是隐式的,这个取决于使用的package是否有帮我们做这样一件事。
本文会拿在.NET Core环境下使用Redis和Memcached来当例子说明,其中,Redis主要是用StackExchange.Redis,Memcached主要是用EnyimMemcachedCore。
先来看看一些我们常用的序列化方法。
常见的序列化方法
或许,比较常见的做法就是将一个对象序列化成byte数组,然后用这个数组和缓存服务器进行交互。
关于序列化,业界有不少算法,这些算法在某种意义上表现的结果就是速度和体积这两个问题。
其实当操作分布式缓存的时候,我们对这两个问题其实也是比较看重的!
在同等条件下,序列化和反序列化的速度,可以决定执行的速度是否能快一点。
序列化的结果,也就是我们要往内存里面塞的东西,如果能让其小一点,也是能节省不少宝贵的内存空间。
当然,本文的重点不是去比较那种序列化方法比较牛逼,而是介绍怎么结合缓存去使用,也顺带提一下在使用缓存时,序列化可以考虑的一些点。
下面来看看一些常用的序列化的库:
在这些库中
System.Runtime.Serialization.Formatters.Binary是.NET类库中本身就有的,所以想在不依赖第三方的packages时,这是个不错的选择。
Newtonsoft.Json应该不用多说了。
protobuf-net是.NET实现的Protocol Buffers。
MessagePack-CSharp是极快的MessagePack序列化工具。
这几种序列化的库也是笔者平时有所涉及的,还有一些不熟悉的就没列出来了!
在开始之前,我们先定义一个产品类,后面相关的操作都是基于这个类来说明。
public class Product { public int Id { get; set; } public string Name { get; set; } }
下面先来看看Redis的使用。
Redis
在介绍序列化之前,我们需要知道在StackExchange.Redis中,我们要存储的数据都是以RedisValue的形式存在的。并且RedisValue是支持string,byte[]等多种数据类型的。
换句话说就是,在我们使用StackExchange.Redis时,存进Redis的数据需要序列化成RedisValue所支持的类型。
这就是前面说的需要显式的进行序列化的操作。
先来看看.NET类库提供的BinaryFormatter。
序列化的操作
using (var ms = new MemoryStream()) { formatter.Serialize(ms, product); db.StringSet("binaryformatter", ms.ToArray(), TimeSpan.FromMinutes(1)); }
反序列化的操作
var value = db.StringGet("binaryformatter"); using (var ms = new MemoryStream(value)) { var desValue = (Product)(new BinaryFormatter().Deserialize(ms)); Console.WriteLine($"{desValue.Id}-{desValue.Name}"); }
写起来还是挺简单的,但是这个时候运行代码会提示下面的错误!
说是我们的Product类没有标记Serializable。下面就是在Product类加上[Serializable]。
再次运行,已经能成功了。
再来看看Newtonsoft.Json
序列化的操作
using (var ms = new MemoryStream()) { using (var sr = new StreamWriter(ms, Encoding.UTF8)) using (var jtr = new JsonTextWriter(sr)) { jsonSerializer.Serialize(jtr, product); } db.StringSet("json", ms.ToArray(), TimeSpan.FromMinutes(1)); }
反序列化的操作
var bytes = db.StringGet("json"); using (var ms = new MemoryStream(bytes)) using (var sr = new StreamReader(ms, Encoding.UTF8)) using (var jtr = new JsonTextReader(sr)) { var desValue = jsonSerializer.Deserialize<Product>(jtr); Console.WriteLine($"{desValue.Id}-{desValue.Name}"); }
由于Newtonsoft.Json对我们要进行序列化的类有没有加上Serializable并没有什么强制性的要求,所以去掉或保留都可以。
运行起来是比较顺利的。
当然,也可以用下面的方式来处理的:
var objStr = JsonConvert.SerializeObject(product); db.StringSet("json", Encoding.UTF8.GetBytes(objStr), TimeSpan.FromMinutes(1)); var resStr = Encoding.UTF8.GetString(db.StringGet("json")); var res = JsonConvert.DeserializeObject<Product>(resStr);
再来看看ProtoBuf
序列化的操作
using (var ms = new MemoryStream()) { Serializer.Serialize(ms, product); db.StringSet("protobuf", ms.ToArray(), TimeSpan.FromMinutes(1)); }
反序列化的操作