我有一个很大的对象列表,我需要在以后存储和检索。该列表将始终用作单位,并且不会单独检索列表项。该列表包含约7000个项目,总计约1GB,但可以轻松升级到该数量的十倍。
我们一直在使用BinaryFormatter.Serialize()
进行序列化(System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
)。然后,此字符串作为blob上载到Azure blob存储。我们发现它通常是快速有效的,但是当我们用更大的文件大小测试它时抛出OutOfMemoryException
它变得不充分。根据我的理解,虽然我正在使用流,但我的问题是BinaryFormatter.Serialize()
方法必须首先将所有内容序列化为内存,然后才能上传blob,从而导致异常。
二进制序列化程序如下所示:
public void Upload(object value, string blobName, bool replaceExisting)
{
CloudBlockBlob blockBlob = BlobContainer.GetBlockBlobReference(blobName);
var formatter = new BinaryFormatter()
{
AssemblyFormat = FormatterAssemblyStyle.Simple,
FilterLevel = TypeFilterLevel.Low,
TypeFormat = FormatterTypeStyle.TypesAlways
};
using (var stream = blockBlob.OpenWrite())
{
formatter.Serialize(stream, value);
}
}
OutOfMemoryException发生在formatter.Serialize(stream, value)
行。
如何以非常快的方式将大量项目序列化为blob存储,而不会遇到内存问题?
答案 0 :(得分:1)
也许您应该切换到JSON?
使用JSON Serializer,您可以流式传输文件并从文件串行化/反序列化(随着文件的读取)。
你的对象会映射到JSON吗?
这是我用来获取NetworkStream并放入Json对象的。
private static async Task<JObject> ProcessJsonResponse(HttpResponseMessage response)
{
// Open the stream the stream from the network
using (var s = await ProcessResponseStream(response).ConfigureAwait(false))
{
using (var sr = new StreamReader(s))
{
using (var reader = new JsonTextReader(sr))
{
var serializer = new JsonSerializer {DateParseHandling = DateParseHandling.None};
return serializer.Deserialize<JObject>(reader);
}
}
}
}
此外,您可以GZip流以减少文件传输时间。我们直接流式传输到GZipped JSON并再次返回。
编辑,虽然这是反序列化,但同样的方法应该适用于序列化
答案 1 :(得分:0)
JSON序列化可以正常工作,正如之前的海报所提到的那样,虽然列表足够大,但这也导致OutOfMemoryException
异常被抛出,因为字符串太大而不适合内存。如果你的对象是一个列表,你可以通过序列化来解决这个问题,但如果你对二进制序列化没有问题,那么使用Protobuf序列化会更快/更低的内存方式。
Protobuf有faster serialization than JSON并且需要更小的内存占用,但代价是它不是人类可读的。 Protobuf-net是一个很棒的C#实现。 Here is a way to set it up with annotations和here is a way to set it up at runtime。在某些情况下,您甚至可以GZip Protobuf序列化字节并节省更多空间。