【问题标题】:Serializing a very large List of items into Azure blob storage using C#使用 C# 将非常大的项目列表序列化到 Azure blob 存储中
【发布时间】:2016-04-07 13:17:52
【问题描述】:

我有大量对象需要稍后存储和检索。该列表将始终用作一个单元,并且不会单独检索列表项。该列表包含大约 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) 行。

因此我尝试使用不同的协议,Protocol Buffers。我尝试使用 Nuget 包 protobuf-net 和 Google.Protobuf 中的两种实现,但序列化速度非常慢(大约 30 分钟),而且根据我的阅读,Protobuf 没有针对序列化大于 1MB 的数据进行优化。所以,我回到绘图板上,遇到了 Cap'n Proto,它承诺通过使用内存映射来解决我的速度问题。我正在尝试使用 @marc-gravell 的 C# 绑定,但我在实现序列化程序时遇到了一些困难,因为该项目还没有完整的文档。此外,我不能 100% 确定 Cap'n Proto 是协议的正确选择 - 但我正在努力在网上找到任何替代建议。

如何以相当快的方式将非常大的项目集合序列化到 blob 存储,而不会遇到内存问题?

【问题讨论】:

  • 将批次上传到多个 blob,而不是一次序列化所有内容?
  • 谢谢,这是我正在考虑的一个选项。虽然每个列表已经是我们领域中的一个块,但是这些 blob 会在某种程度上失去上下文,这会使事情变得有点复杂。不过建议不错,如果没有其他协议或方法建议,我会试一试。

标签: c# azure serialization protocol-buffers capnproto


【解决方案1】:

也许您应该切换到 JSON?

使用 JSON 序列化器,您可以流式传输文件和从文件中流式传输和序列化/反序列化零碎(在读取文件时)。

你的对象能很好地映射到 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 并再次返回。

编辑,虽然这是反序列化,但同样的方法应该适用于序列化

【讨论】:

    【解决方案2】:

    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。在某些情况下,您甚至可以对 Protobuf 序列化字节进行 GZip 压缩并节省更多空间。

    【讨论】:

      猜你喜欢
      • 2014-08-05
      • 2017-10-14
      • 2021-12-21
      • 2012-03-02
      • 2019-12-19
      • 2019-09-26
      • 2017-02-01
      • 2019-06-28
      • 2019-04-03
      相关资源
      最近更新 更多