【问题标题】:Hibernate (PostgreSQL) slow select query WHERE clause on foreign key compared to jdbc与 jdbc 相比,Hibernate(PostgreSQL)外键上的慢速选择查询 WHERE 子句
【发布时间】:2019-01-03 12:36:09
【问题描述】:

我编写了一个应用程序,它可以抓取互联网广播播放列表,然后将它们保存到数据库中。为了了解休眠,我将应用程序迁移到使用休眠,但与其他尝试相比,我在执行 SELECT ... WHERE 查找时发现性能大幅下降。在我的 python sqlite 原型中,相同的过程(获取大约 17,000 首曲目,按播放的程序和播放者分组)花费了 150 毫秒,而使用 apache db utils 的初始 Java 版本花费了大约 250 毫秒,而我的(可能是可怕的) 休眠版本,大约需要 1100 毫秒。

@Override
public DJAllProgrammes getAllProgrammesFromDJ(Collection<String> names) {
    DJAllProgrammes djAllProgrammes = new DJAllProgrammes();
    session.beginTransaction();
    List<Presenter> result = session.createQuery("from Presenter p WHERE p.presenter_name in :names", Presenter.class)
            .setParameterList("names", names)
            .getResultList();
    for (Presenter presenter : result) {
        int presenter_id = presenter.getPresenter_id();
        List<Programme> programmes = session
                .createQuery("from programme prog WHERE prog.presenter_origin_id = :pres_orig_id", Programme.class)
                .setParameter("pres_orig_id", presenter_id)
                .getResultList();
        for (Programme programme : programmes) {
            //this is the critical performance death zone 
            List<Track> tracksOnThisProgramme = session
                    .createQuery("FROM track t WHERE t.programme.programme_id in :progIds", Track.class)
                    .setParameter("progIds", programme.getProgramme_id())
                    .getResultList();
            djAllProgrammes.addProgramme(new ProgrammeData(presenter.getPresenter_name(), programme.getDate(), tracksOnThisProgramme));
        }
    }
    session.getTransaction().commit();
    return djAllProgrammes;
}

调试信息:

信息:会话指标

{
    33339 nanoseconds spent acquiring 1 JDBC connections;
    71991 nanoseconds spent releasing 1 JDBC connections;
    12938819 nanoseconds spent preparing 258 JDBC statements;
    88949720 nanoseconds spent executing 258 JDBC statements;
    0 nanoseconds spent executing 0 JDBC batches;
    0 nanoseconds spent performing 0 L2C puts;
    0 nanoseconds spent performing 0 L2C hits;
    0 nanoseconds spent performing 0 L2C misses;
    4671332 nanoseconds spent executing 1 flushes (flushing a total of 9130 entities and 0 collections);
    599862735 nanoseconds spent executing 258 partial-flushes (flushing a total of 1079473 entities and 1079473 collections)
}

环顾互联网,我看到了一个建议,即事务中有太多实体“使用分页和较小的批量增量”-我可以找到有关什么是分页的信息,但找不到“使用较小的批量增量”的信息意思是“

我有点陷入困境,这个应用程序在使用 Apache DB Utils(一个轻量级 jdbc 包装器)做基本相同的事情时具有良好的性能,我太无知了,我什至不知道要搜索什么加快速度。帮兄弟解惑?

还有这里使用的bean(持久性实体...?)https://pastebin.com/pSQ3iGK2

【问题讨论】:

    标签: java sql postgresql hibernate


    【解决方案1】:

    一般来说:OR-Mapper 允许您对与另一个实体相关的实体进行建模。 我在你的代码中看到了一些 1:N 关系,演示者有很多程序。

    类名“Programme”可能是第一个错误,因为它是复数形式。更好地使用“Programm”并在“Presenter”类中建模@OneToMany 关系。

    当你这样做时,你只需要触发一个休眠查询。找到的“Presenter”类型的实体将包含“Programm”列表/集。遍历实体并将它们转换为您的返回值“DJAllProgrammes”,它应该只包含普通值(dto)而不是对实体的引用。 IE。将实体映射到 dto。

    【讨论】:

      【解决方案2】:

      在这个任务中使用像 Hibernate 这样的 ORM 总是比原型版本中的 db utils 慢,后者直接使用 JDBC 层。考虑正在发生的事情:

      List<Presenter> result = session.createQuery("from Presenter p WHERE p.presenter_name in :names", Presenter.class)
              .setParameterList("names", names)
              .getResultList();
      

      解析查询后,解析对象,然后names的大小决定了它将扩展到(?,?,?...)的参数个数。
      然后发送查询,一旦结果进来,每个查询都会创建两个副本。一个在结果列表中给出,一个保留在内部以检查更改。

      for (Presenter presenter : result) {
          int presenter_id = presenter.getPresenter_id();
          List<Programme> programmes = session
                  .createQuery("from programme prog WHERE prog.presenter_origin_id = :pres_orig_id", Programme.class)
                  .setParameter("pres_orig_id", presenter_id)
                  .getResultList();
      

      这里我们再次发生了同样的事情,只是它实际上更糟了一点。您不是重复使用查询,而是在每个循环上创建一个新查询,然后将其丢弃。
      嵌套循环中也发生了同样的事情。
      此外,如果presenter.getPresenter_id() 返回Integer 对象,而不是原始int,则您将进行不必要的拆箱,然后在.setParameter("pres_orig_id", presenter_id) 调用上重新装箱。如果方法返回 Integer 对象,则将其更改为 Integer presenter_id。但是如果它是一个原始的int,那么这不是必需的,但它不会受到伤害,因为唯一的用法是作为对象传递。您甚至可以直接在setParameter 中使用它。

      因此,总的来说,当您将 createQuery 调用从循环中取出时,您会得到这个。

      @Override
      public DJAllProgrammes getAllProgrammesFromDJ(Collection<String> names) {
          DJAllProgrammes djAllProgrammes = new DJAllProgrammes();
          session.beginTransaction();
          List<Presenter> result = session.createQuery("from Presenter p WHERE p.presenter_name in :names", Presenter.class)
                  .setParameterList("names", names)
                  .getResultList();
          TypedQuery<Programme> progByPresenterOrigin = session
                      .createQuery("from programme prog WHERE prog.presenter_origin_id = :pres_orig_id", Programme.class);
          TypedQuery<Track> trackByProgrammeId = session
                          .createQuery("FROM track t WHERE t.programme.programme_id in :progIds", Track.class)
          for (Presenter presenter : result) {
              List<Programme> programmes = progByPresenterOrigin
                      .setParameter("pres_orig_id", presenter.getPresenter_id())
                      .getResultList();
              for (Programme programme : programmes) {
                  //this is the critical performance death zone 
                  List<Track> tracksOnThisProgramme = trackByProgrammeId
                          .setParameter("progIds", programme.getProgramme_id())
                          .getResultList();
                  djAllProgrammes.addProgramme(new ProgrammeData(presenter.getPresenter_name(), programme.getDate(), tracksOnThisProgramme));
              }
          }
          session.getTransaction().commit();
          return djAllProgrammes;
      }
      

      【讨论】:

        猜你喜欢
        • 2017-08-24
        • 2012-03-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多