ServiceStack 基准测试继续:为什么坚持简单(复杂)到 JSON 会减慢 SELECT 的速度?

ServiceStack benchmark continued: why does persisting a simple (complex) to JSON slow down SELECTs?

我想切换到 OrmLite,我需要弄清楚它是否很慢,如果是,为什么。

在我的研究中,我得出的结论是,在 OrmLite 中被斑点化为 JSON 的复杂对象是 SELECTs 非常慢的罪魁祸首。

因此,我创建了一个只关注 OrmLite 的新项目,不与除自身以外的任何其他项目进行比较,这里的目的是查看有斑点 JSON 对象和没有拥有它们。

可以在GitHub上找到: https://github.com/tedekeroth/ormlitebenchmarking

解决方案如下所示:

我是 运行 OrmLite 5.1.1 Windows 7、2.6Ghz、24 GB RAM,目前没有 CPU 负载,使用 MySql 5.6。应用程序连接到 127.0.0.1 (root/root) 并需要数据库 "ormlite".

我启用了 ThrowOnError:

OrmLiteConfig.ThrowOnError = JsConfig.ThrowOnError = true;

应用程序如下所示:

无数据:只有创建的对象,没有属性有数据:

基元:只填充了一些简单的基元属性:

Prim + 一个复数:所有原语如上+一个斑点复数对象:

完整数据:以上所有 + 另外 2 个复杂的斑点对象:

Create 按钮首先在列表中创建 10000 个对象,然后使用 OrmLite Insert 方法将它们持久化。时间测量仅针对 INSERT,而不是创建对象。

public void AddRow<T>(T coreObject) where T : CoreObject
{
    long id = 0;
    using (var _db = _dbFactory.Open())
    {
        id = _db.Insert<T>(coreObject, selectIdentity: true);
    }           
}

Read 按钮读取 table 中的所有行,并重新创建 Customer 对象:

public List<T> FetchAll<T>()
{
    using (var _db = _dbFactory.Open())
    {
        List<T> list = _db.Select<T>();
        return list;
    }
}

因此,测试应该像这样进行:

要测试另一种模式,请清空数据库 table (customer) 以获得干净的模式。


基准测试

插入
创建 10 000 个对象(未测量)并将它们插入数据库。

所以,总而言之,大致相同,26-29 秒。

SELECT
从数据库中读取 10 000 个对象,如上所示。

结论

"Full data" 显然是大打脸的地方。 添加的复杂斑点对象 (ContactDetails) 似乎把事情搞砸了。我在之前的测试中注意到了这一点,但对象本身并不是很复杂,请参见下文。所以,我不确定为什么它会这样跳,或者这些数字是否合理。

我之前问过一个关于这个的问题,但这个基准测试应该更准确。

问题是:为什么持久化对象(按照 OrmLite 到 JSON)会以这种方式减慢 SELECTs?

[Serializable]
public class ContactDetails 
{
    public List<ContactItem> ContactItemList
    {
        get; set;
    }
    public ContactItem CurrentContactItem
    {
        get; set; 
    }
    public ContactItem DefaultContactItem
    {
        get; set;
    }
    public bool IgnorePrimaryWaitBuffer
    {
        get; set;
    }

    public ContactDetails(List<ContactItem> contactItemList, ContactItem currentContactItem, ContactItem defaultContactItem)
    {
        ContactItemList = contactItemList;
        CurrentContactItem = currentContactItem;
        DefaultContactItem = defaultContactItem;
    }

    public ContactDetails()
    {
    }
}

我已经成功下载并分析了此解决方案,它突出显示了未缓存后期绑定类型的类型访问器问题的原因,该问题已通过 this commit 解决。

通过此更改,加载 10000 行复杂类型的性能从 11,765 毫秒减少到 669 毫秒(在我的 iMac 5k 上),如下所示:

此更改适用于现在 available on MyGet 的 v5.1.1。

注意:我删除了下面的 JsConfig.IncludeTypeInfo 行:

JsConfig.IncludeTypeInfo = true;

这会强制序列化程序为 increases the payload size and decreases performance 的每个对象发出类型信息。 ServiceStack.Text 会在需要时发出类型信息,即 objectinterfacesabstract 类,所以你应该很少自己强制它,除非它是绝对的需要,因为它会对性能产生重大的不利影响。

理想情况下,您的 DTO 不应使用接口、后期绑定对象或继承,但如果您考虑使基本类型 abstract 强制类型信息仅在需要的地方使用,而不是总是发出它们。