ServiceStack 基准测试继续:为什么将简单(复杂)持久化为 JSON 会减慢 SELECT 的速度? [英] ServiceStack benchmark continued: why does persisting a simple (complex) to JSON slow down SELECTs?

查看:22
本文介绍了ServiceStack 基准测试继续:为什么将简单(复杂)持久化为 JSON 会减慢 SELECT 的速度?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

在我的研究中,我得出的结论是,OrmLite 中的复杂对象被打包为 JSON,是导致 SELECT 非常慢的罪魁祸首.

因此,我创建了一个仅关注 OrmLite 的新项目,不与除自身以外的任何其他项目进行比较,这里的目的是了解具有 blob JSON 对象和没有它们之间的区别.

它可以在 GitHub 上找到:

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

我已启用 ThrowOnError:

OrmLiteConfig.ThrowOnError = JsConfig.ThrowOnError = true;

应用程序如下所示:

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

Primitives:只填充了一些简单的原始属性:

Prim + 一个复合体:上述所有基元 + 一个斑点复合体:

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

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

public void AddRow(T coreObject) where T : CoreObject{长 ID = 0;使用 (var _db = _dbFactory.Open()){id = _db.Insert(coreObject, selectIdentity: true);}}

读取按钮读取表中的所有行,并重新创建客户对象:

public ListFetchAll(){使用 (var _db = _dbFactory.Open()){列表<T>list = _db.Select();退货清单;}}

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

  • 选择模式,然后按创建,会显示花费的时间
  • 读取可回读当前表中的所有行

要测试另一种模式,请清空 db 表 (customer) 以获得干净的表.

<小时>

基准

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

  • 无数据:~26-27 秒
  • 原语:~27.1-27.4 秒
  • Prim + 一个复合体:~27.5-29 秒
  • 完整数据:~28 秒

所以,总而言之,大约相同,26-29 秒.

选择
从数据库中读取 10 000 个对象,如上所示.

  • 无数据:~460 毫秒
  • 原语:~700-720 毫秒
  • Prim + 一个复合体:~970-1030 毫秒
  • 完整数据:30000-32000 毫秒(30-32 秒)

结论

完整数据"显然是出现大问题的地方.添加的复杂斑点对象 (ContactDetails) 似乎把它搞砸了.我在之前的测试中注意到了这一点,但对象本身并不是很复杂,见下文.所以,我不确定它为什么会这样跳跃,或者这些数字是否合理.

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

问题是:为什么以这种方式持久化对象(根据 OrmLite 到 JSON)会减慢 SELECT 的速度?

[可序列化]公开课联系方式{公共列表联系人项目列表{得到;放;}public ContactItem CurrentContactItem{得到;放;}public ContactItem DefaultContactItem{得到;放;}public bool IgnorePrimaryWaitBuffer{得到;放;}public ContactDetails(List contactItemList, ContactItem currentContactItem, ContactItem defaultContactItem){ContactItemList = contactItemList;CurrentContactItem = currentContactItem;DefaultContactItem = defaultContactItem;}公共联系方式(){}}

解决方案

我已经设法下载并分析了这个解决方案,它突出了不缓存后期绑定类型的类型访问器的问题的原因,该问题通过

此更改从 v5.1.1 开始可用,现在 MyGet 上提供.

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

JsConfig.IncludeTypeInfo = true;

这会强制序列化程序为每个增加负载大小并降低性能的对象发出类型信息.ServiceStack.Text 已经在需要时发出类型信息,即对于 objectinterfacesabstract 类,所以你应该很少自己强制它除非绝对需要,否则它会对性能产生重大不利影响.

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

I would like to switch over to OrmLite, and I need to figure out if it is slow, and if so, why.

In my research, I come to the conclusion that complex objects, that in OrmLite are blobbed to JSON, is the culprit of very slow SELECTs.

Therefore, I created a new project that focuses solely on OrmLite, and does not compare with anything else other than itself, and the aim here is to see the differences between having blobbed JSON objects, and not having them.

It can be found on GitHub: https://github.com/tedekeroth/ormlitebenchmarking

Solution looks like this:

I am running OrmLite 5.1.1 on a Windows 7, 2.6Ghz, 24 GB RAM and no CPU load currently, using MySql 5.6. Application connects to 127.0.0.1 (root/root) and needs database "ormlite".

I have enabled ThrowOnError:

OrmLiteConfig.ThrowOnError = JsConfig.ThrowOnError = true;

The application looks like this:

No data: just the object created, no properties has data:

Primitives: Just some of the simple primitive properties are populated:

Prim + one complex: all primitives as above + one blobbed complex object:

Full data: all of the above + another 2 complex blobbed objects:

The Create button first creates 10 000 objects in a list, and then they are persisted using OrmLite Insert method. Time measurement is done only for the INSERTs, not creating the objects.

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

The Read button reads all rows in the table, and recreates the Customer objects:

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

So, testing should be done like:

  • Select mode, and press Create, time took will be displayed
  • Press Read to read back all the rows currently in the table

To test another mode, empty the db table (customer) to have a clean one.


BENCHMARKING

INSERT
Creating 10 000 objects (not measured) and inserting them into database.

  • No data: ~26-27 seconds
  • Primitives: ~27.1-27.4 seconds
  • Prim + one complex: ~27.5-29 seconds
  • Full data: ~28 seconds

So, all in all, around the same, 26-29 seconds.

SELECT
Reading 10 000 objects from db, as inserted above.

  • No data: ~460 ms
  • Primitives: ~700-720 ms
  • Prim + one complex: ~970-1030 ms
  • Full data: 30000-32000 ms (30-32 seconds)

CONCLUSIONS

"Full data" is obviously where the big smack-in-the-face appears. The complex blobbed objects that is added (ContactDetails), seems to mess it up. I noticed this in a previous test, but the object itself isn't very complex, see below. So, I am not sure why it jumps like this, or if these numbers are reasonable.

I had asked a previous question about this, but this benchmarking should be more accurate.

The question is: why does persisting an object (to JSON as per OrmLite) slow down SELECTs in this way?

[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()
    {
    }
}

解决方案

I've managed to download and profile this solution which highlighted the cause of the issue of not caching the type accessors of late-bound types which is resolved with this commit.

With this change performance for loading 10000 rows with complex types reduced from 11,765ms to 669ms (on my iMac 5k) as seen below:

This change is available from v5.1.1 that's now available on MyGet.

Note: I've removed the JsConfig.IncludeTypeInfo line below:

JsConfig.IncludeTypeInfo = true;

Which forces the Serializers to emit type info for every object which increases the payload size and decreases performance. ServiceStack.Text will already emit the type info when it's needed, i.e. for object, interfaces and abstract classes, so you should rarely force it yourself unless it's absolutely needed, as it can have a major detrimental impact on performance.

Ideally your DTO's should not use interfaces, late-bound objects or inheritance but if you are consider making the base types abstract to force the type info to only where it's needed, instead of always emitting them.

这篇关于ServiceStack 基准测试继续:为什么将简单(复杂)持久化为 JSON 会减慢 SELECT 的速度?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆