跳到主要内容

Benchmark报告

Jimmer不仅可以为你带来强大的功能,还可以为你带来极致的性能。

基准测试的源代码在此,使用H2的内存数据库,无需任何环境准备即可直接运行。

报告

每秒操作次数

  • 横坐标表示每次从数据库中查询到的数据对象的数量。
  • 纵坐标表示每秒操作次数。

每次操作耗时

  • 横坐标表示每次从数据库中查询到的数据对象的数量。
  • 纵坐标表示每次操作耗时(微秒)。
备注

由于Spring移除了对OpenJPA的支持,本基准测试不包含JPA(OpenJPA)

实现原则

  1. 所有框架禁用缓存

  2. 所有框架关闭日志

  3. 所有框架每次都打开和关闭连接/会话,不做共享;靠连接池保证性能。

  4. 接入Spring的连接管理机制。因不同框架API不同,实现方法略有不同。

    但最终效果都一样

  5. 不使用事务

    Exposed比较特殊,其API强制要求事务,给予假的实现。

  6. 使用嵌入的H2内数据库,尽可能压缩数据库的消耗,凸显ORM本身的性能,即映射的性能。

价值

一种常见的观点:ORM本身的性能不重要,实际项目中,数据库并非有内嵌内存数据库,所以ORM本身耗时相对于数据库耗时可忽略不计。

反驳:Java19发布后,支持虚拟线程。ORM能尽快完成映射,可以让JVM去调度更多的虚拟线程,可以提高系统的吞吐量。

为什么如此快?

JDBC有两种编程风格用于从java.sql.ResultSet中读取值

  • JDBC(ColIndex),即按照列索引读取,rs.getString(1)

  • JDBC(ColName),按照列名称读取:rs.getString("VALUE_1")

JDBC(ColIndex)JDBC(ColName)性能高,因此,Jimmer本身也将JDBC(ColIndex)作为底层的访问ResultSet的手段,为性能指标超越JDBC(ColName)提供了可能性。

现在要我们重点讨论图表 (选中JDBC原生JDBC指标) 中的两个案例

  • 每次查询10条数据,JDBC(ColName)Jimmer(Java)的OPS接近。

    这种场景下,Jimmer和JDBC(ColName)性能相似。JDBC(ColName)的测试代码直接硬编码SQL,而Jimmer采用DSL机制需要动态构建SQL,从这个角度讲Jimmer应该更慢。然而Jimmer把ResultSet转化为对象的的过程比JDBC(ColName)快,二者抵消。所以性能相近。

    当然Benchmark中采用的SQL语句不复杂,如果换用更复杂的SQL,直接硬编码SQL的JDBC(ColName)会更快,这也是Jimmer后续版本可以优化的空间。

  • 每次查询1000条数据,Jimmer的ops明显高于JDBC(ColName)的OPS。

    在这种情况下,Jimmer把ResultSet转化为对象的性能优势得到了充分体现,即便Jimmer使用DSL临时生产SQL拖慢了速度,但整体结果仍然比JDBC(ColName)快。

    Jimmer把ResultSet转化为对象的性能非高,主要是因为如下两个原因

    • 底层使用JDBC(ColIndex)

    • 不使用Java的反射机制为对象动态设置各属性。

      在编译时,Jimmer为每一个不可变的实体类型生称一个可修改的DraftImpl类,提供了一个通用的__set(PropId propId, Object value)方法完成和Java反射类似的动态设置对象属性的功能。

      同时,与编译器每个属性分配一个整数作为id,DraftImpl类的的__set方法内部使用switch语句实现了对传入的属性id进行鉴别。以生成的Java代码为例

      @Override
      public void __set(PropId prop, Object value) {
      int __propIndex = prop.asIndex();
      switch (__propIndex) {
      case -1:
      __set(prop.asName(), value);
      return;
      case 0:
      setId((Long)value);
      break;
      case 1:
      setName((String)value);
      break;
      case 2:
      setEdition((Integer)value);
      break;
      ......
      }

      现代编译器对这种基于整数的switch语句有充分优化,可以认为Jimmer对实体对象的动态赋值不会比JDBC测试代码中的硬编码慢多少。

    以上两个原因,是Jimmer的对象映射性能如此高的最重要的原因。