当前位置: 首页 > 知识库问答 >
问题:

优化。NET POCOs的JSON序列化性能

王轶
2023-03-14

我一直试图优化要导入到MongoDB中的超过500k个POCO的JSON序列化,但除了头痛之外什么也没有遇到。我最初尝试了Newtonsoft json.convert()函数,但这花费了太长时间。然后,根据SO、NewtonSoft自己的站点和其他位置上的几篇文章的建议,我尝试手动序列化这些对象。但没有注意到太多,如果有任何业绩增益。

这是我用来启动序列化过程的代码...在每行上面的注释中,是给定1000个对象的数据集,每个单独操作完成所需的时间。

//
// Get reference to the MongoDB Collection
var collection = _database.GetCollection<BsonDocument>("sessions");
//
// 8ms - Get the number of records already in the MongoDB. We will skip this many when retrieving more records from the RDBMS
Int32 skipCount = collection.AsQueryable().Count();
//
// 74ms - Get the records as POCO's that will be imported into the MongoDB (using Telerik OpenAcces ORM)
List<Session> sessions = uow.DbContext.Sessions.Skip(skipCount).Take(1000).ToList();

//
// The duration times displayed in the foreach loop are the cumulation of the time spent on 
// ALL the items and not just a single one.
foreach (Session item in sessions)
{
    StringWriter sw       = new StringWriter();         
    JsonTextWriter writer = new JsonTextWriter(sw);     
    //
    // 585,934ms (yes - 9.75 MINUTES) - Serialization of 1000 POCOs into a JSON string. Total duration of ALL 1000 objects 
    item.ToJSON(ref writer);
    //
    // 16ms - Parse the StringWriter into a String. Total duration of ALL 1000 objects.
    String json = sw.ToString();
    //
    // 376ms - Deserialize the json into MongoDB BsonDocument instances. Total duration of ALL 1000 objects.
    BsonDocument doc = MongoDB.Bson.Serialization.BsonSerializer.Deserialize<BsonDocument>(json); // 376ms

    //
    // 8ms - Insert the BsonDocument into the MongoDB dataStore. Total duration of ALL 1000 objects.
    collection.InsertOne(doc);

}

目前,将每个对象序列化为JSON文档需要0.5到75秒,相当于1000个文档需要10分钟,10000个文档需要100分钟,等等。我发现这些持续时间相当一致,但最终这意味着,为了加载600K记录,执行DataLoad需要大约125个小时的处理时间。这是一个消息传递系统,它最终可能每天增加20K-100K的新文档,所以性能对我们来说是一个真正的问题。

我构造的序列化代码将在前面的代码示例中创建的JsonTextWriter实例传递到POCOs的ToJSON函数中,因此我们不会为每个模型创建新的编写器,以便在序列化自身时使用。

下面的代码是几个对象的截断示例,试图说明实现技术(编写器是如何传递的,以及JSON是如何手动构造的)。有更多的属性和更多的相关/嵌套对象,但这是我必须进行的“最深入”遍历的一个例子。

它从“session”对象开始,然后递归地调用它的依赖属性来序列化它们自己。

public class Session
{

    #region properties

    public Guid SessionUID { get; set; }

    public String AssetNumber { get; set; }

    public Int64? UTCOffset { get; set; }

    public DateTime? StartUTCTimestamp { get; set; }

    public DateTime? StartTimestamp { get; set; }

    public DateTime? EndTimestamp { get; set; }

    public String Language { get; set; }

    // ... many more properties 

    #endregion properties 

    #region navigation properties

    public virtual IList<SessionItem> Items { get; set; }

    #endregion navigation properties

    #region methods
    public void ToJSON(ref JsonTextWriter writer)
    {
        Session session = this;     
        // {
        writer.WriteStartObject();

        writer.WritePropertyName("SessionUID");
        writer.WriteValue(session.SessionUID);

        writer.WritePropertyName("AssetNumber");
        writer.WriteValue(session.AssetNumber);

        writer.WritePropertyName("UTCOffset");
        writer.WriteValue(session.UTCOffset);

        writer.WritePropertyName("StartUTCTimestamp");
        writer.WriteValue(session.StartUTCTimestamp);

        writer.WritePropertyName("StartTimestamp");
        writer.WriteValue(session.StartTimestamp);

        writer.WritePropertyName("EndTimestamp");
        writer.WriteValue(session.EndTimestamp);

        writer.WritePropertyName("Language");
        writer.WriteValue(session.Language);

        // continues adding remaining instance properties

        #endregion write out the properties

        #region include the navigation properties

        // "Items": [ {}, {}, {} ]
        writer.WritePropertyName("Items");
        writer.WriteStartArray();
        foreach (SessionItem item in this.Items)
        {
            item.ToJSON(ref writer);
        }
        writer.WriteEndArray();

        #endregion include the navigation properties

        // }
        writer.WriteEndObject();
        //return sw.ToString();
    }

    #endregion methods 
}

public class SessionItem
{
    #region properties

    public Int64 ID { get; set; }

    public Int64 SessionID { get; set; }

    public Int32 Quantity { get; set; }

    public Decimal UnitPrice { get; set; }

    #endregion properties

    #region navigation properties

    public virtual Session Session { get; set; }

    public virtual IList<SessionItemAttribute> Attributes { get; set; }

    #endregion navigation properties

    #region public methods
    public void ToJSON(ref JsonTextWriter writer)
    {
        // {
        writer.WriteStartObject();

        #region write out the properties

        writer.WritePropertyName("ID");
        writer.WriteValue(this.ID);

        writer.WritePropertyName("SessionID");
        writer.WriteValue(this.SessionID);

        writer.WritePropertyName("Quantity");
        writer.WriteValue(this.Quantity);

        writer.WritePropertyName("UnitPrice");
        writer.WriteValue(this.UnitPrice);

        #endregion write out the properties

        #region include the navigation properties
        //
        // "Attributes": [ {}, {}, {} ]
        writer.WritePropertyName("Attributes");
        writer.WriteStartArray();
        foreach (SessionItemAttribute item in this.Attributes)
        {
            item.ToJSON(ref writer);
        }
        writer.WriteEndArray();

        #endregion include the navigation properties

        // }
        writer.WriteEndObject();
        //return sw.ToString();
    }
    #endregion public methods
}

public class SessionItemAttribute : BModelBase, ISingleID
{
    public Int64 ID { get; set; }

    public String Name { get; set; }

    public String Datatype { get; set; }

    public String Value { get; set; }

    #region navigation properties

    public Int64 ItemID { get; set; }
    public virtual SessionItem Item { get; set; }

    public Int64 ItemAttributeID { get; set; }
    public virtual ItemAttribute ItemAttribute { get; set; }

    #endregion navigation properties

    #region public methods
    public void ToJSON(ref JsonTextWriter writer)
    {
        // {
        writer.WriteStartObject();

        #region write out the properties

        writer.WritePropertyName("ID");
        writer.WriteValue(this.ID);

        writer.WritePropertyName("Name");
        writer.WriteValue(this.Name);

        writer.WritePropertyName("Datatype");
        writer.WriteValue(this.Datatype);

        writer.WritePropertyName("StringValue");
        writer.WriteValue(this.StringValue);

        writer.WritePropertyName("NumberValue");
        writer.WriteValue(this.NumberValue);

        writer.WritePropertyName("DateValue");
        writer.WriteValue(this.DateValue);

        writer.WritePropertyName("BooleanValue");
        writer.WriteValue(this.BooleanValue);

        writer.WritePropertyName("ItemID");
        writer.WriteValue(this.ItemID);

        writer.WritePropertyName("ItemAttributeID");
        writer.WriteValue(this.ItemAttributeID);

        #endregion write out the properties

        // }
        writer.WriteEndObject();
        //return sw.ToString();
    }
    #endregion public methods
}

我怀疑我忽略了什么,或者问题在于我实现序列化的方式。一个SO poster声称,通过手动序列化数据,他的加载时间从28秒减少到31毫秒,所以我期待着更戏剧性的结果。实际上,这与我使用Newtonsoft json.convert()方法观察到的性能几乎完全相同。

如能帮助诊断序列化中的延迟来源,将不胜感激。谢谢!

更新

虽然我还没有从ORM中解脱数据访问,但我已经能够确认延迟实际上来自ORM(谢谢评论者)。当我按照建议添加FetchStrategy时,延迟仍然存在,但时间从用于序列化转移到用于查询(即导航属性的加载)。

因此,问题不在于序列化,而在于优化数据检索。

共有1个答案

逄俊力
2023-03-14

为了解决这个问题,我想发布我的解决方案。

经过进一步研究,原帖的评论者认为是正确的。这不是序列化问题,而是数据访问问题。当序列化过程中请求导航属性时,ORM“懒洋洋地加载”导航属性。当我实现FetchStrategy来“贪婪地”获取相关的对象时,延迟的来源从序列化过程周围的计数器转移到数据访问周围的计数器。

我可以通过在数据库中的外键字段上添加索引来解决这个问题。延迟下降了90%以上,原本需要100多分钟才能运行的事情现在只需10分钟就能完成。

所以感谢那些评论我的人,他们提醒我还发生了什么,帮助我消除了眼罩。

 类似资料:
  • 问题内容: 使用Jackson 2,我正在寻找一种 通用的 方式将对象序列化为单个值(然后序列化它们,然后再填充该单个字段),而不必重复创建JsonSerializer / JsonDeserializer来处理每种情况。@JsonIdentityInfo批注非常接近,但由于我知道,它将始终对完整的子对象进行序列化,因此略微遗漏了该标记。 这是我想做的一个例子。给定的类: 我希望Order可以序列

  • Android 应用性能优化系列 原文链接分别为 : https://www.youtube.com/playlist?list=PLWz5rJ2EKKc9CBxr3BVjPTPoDPLdPIFCE https://www.udacity.com/course/ud825 译者 : 胡凯 Android性能优化典范 Android性能优化之渲染篇 Android性能优化之运算篇 Android性能

  • A curated list of Web Performance Optimization. Everyone can contribute here! Categories :memo: Articles :newspaper: Blogs :books: Books :book: Docs :movie_camera: Talks Tools Analyzers Analyzers API

  • 问题内容: 既然没有,可以使用什么本机实现来处理呢? 我注意到了,并且可以使用此格式将数据格式化为JSON,但是如何反序列化? 或者,也许我缺少某些依赖项? 问题答案: 您可以使用,它的依赖项是的依赖项。因此,您无需在project.json中添加依赖项。 请注意,使用WebAPI控制器,您无需处理JSON。 更新ASP.Net Core 3.0 Json.NET已从ASP.NET Core 3.

  • 问题内容: 我在Web API项目中使用了Dictionary,该项目的序列化方式类似于JSON: 因为我有重复的键,所以我不能再使用Dictionary类型,而现在我正在使用 但这是以这种方式序列化的: 有没有办法像字典一样进行序列化? 谢谢。 问题答案: 如果您使用Newtonsoft Json.NET库,则可以执行以下操作。 定义一个转换器,以所需的方式写入键/值对的列表: 然后使用转换器:

  • 问题内容: 我想要一种尽可能自动地将对象序列化和反序列化为JSON的方法。 序列化: 对我来说,理想的方式是,如果我在实例JSONSerialize()中调用,它将返回带有JSON对象的字符串,该对象具有该对象的所有公共属性。对于那些原始值,这很简单,对于对象,它应该尝试调用每个JSONSerialize()或ToString()或类似的东西来递归序列化所有公共属性。对于集合,它也应该正确运行(只