使用hibernate最快的findByName查询是什么?

时间:2010-06-15 19:29:44

标签: java database performance hibernate

我确信我可以提高hibernate的以下findByName查询的性能:

public List<User> findByName(String name) {
  session.createCriteria(User.class).add(Restrictions.eq("name", name)).list();
}

瓶颈是findByName方法而我不能使用id。

在我的情况下,我知道名称是唯一的,但在name属性中添加索引注释并没有提高性能。我做了以下事情:

class User {
  @Index(name = "nameIdx")
  private String name;      
}

我应该以哪种方式改进它,或者更重要的是:我应该首先以哪种方式改进它?我将需要包含所有集合(layz与否)的完整对象以及此类的deps。

或者我可以改进它,如果我想要几个User对象(并且知道几个名字)?

UPDATE1:

@Index注释没有提高性能,因为数据库已经有了索引,因为我的唯一约束注释:

@UniqueConstraint(columnNames = {"name"})

UPDATE2:

  1. 仔细阅读答案!

  2. 在SQL日志记录的帮助下,我发现真正的问题是虽然我没有提交或刷新事务,但是引发了很多更新和插入语句。 这背后的原因是我做了(循环):

    User u = findByName(name);
    if(u == null) 
       attach(u = new User(name));
    

    所以hibernate需要在每次findByName查询之前将新创建的用户刷新到db。我用自己的缓存解决方法(LinkedHashMap)解决了这个问题。

  3. 我通过Jens Schauder的提示做了另一项改进:

    public Collection<User> findByNames(Collection<String> names) {
       return session.createCriteria(User.class).
              add(Restrictions.in("name", names)).list();
    }
    
  4. 将某些用户集合指定为非懒惰时,可以进一步改进:

    @LazyCollection(LazyCollectionOption.FALSE)
    

    Read this answer以获得更好的选择。

  5. 对我来说,最后也是最重要的一个是:用list替换我的SortedSet项,并在getItems方法中执行以下操作:

    Set set = new LinkedHashSet(items);
    items.clear();
    items.addAll(set);
    Collections.sort(items, itemComparator);
    return Collections.unmodifiableCollection(items);
    

    因此,hibernate可以处理项目集合(即添加),而无需从数据库加载整个集合。

  6. @Pascal Thivent和@Jens Schauder :一堆谢谢!对不起,我只能接受一个答案: - /

    有用的记录设置:

    log4j.logger.org.hibernate.tool.hbm2ddl=INFO, StdoutApp
    log4j.logger.org.hibernate.SQL=INFO, StdoutApp
    # additionally provide the information which parameters will be bound:
    log4j.logger.org.hibernate.type=TRACE
    

    Another helpful link

2 个答案:

答案 0 :(得分:3)

您没有提供足够的信息来获得完整的答案,但这里有一些想法:

  • 你可以使用id吗? Hibernate将为id选择准备查询,因此这些查询将比其他查询(一点点)更快
  • 是正确编入索引的名称吗?出于此查询的目的,它应该具有唯一键(您暗示,您期望单个结果)。当然,这样的索引在插入,更新和删除方面会产生性价比。
  • 当我们来参考时,它取决于你的表现你的意思:声明返回的时间?然后你应该使用延迟加载。它使第一个语句更快,因此可能更快。当然,一旦你的参考文献脱水,你就会有更多的陈述。否则(某些)急切加载可能会更快,尽管这在很大程度上取决于细节。
  • 使用缓存,如果可以从缓存中检索,这可能对引用尤其有帮助。
  • 调整您的数据库。给它足够的内存以便始终将所有内容保存在内存中。
  • 调整您的网络。对于显示的小查询,延迟可能是一个问题
  • 通过将db放在与代码相同的计算机上来删除网络。假设它足够大。

正如您所看到的,您有很多调整选项。唯一一个我期望对这项努力产生良好影响的是考虑一个指数。当然,当我们有关于问题的更多信息时,这可能会改变(例如完整的表结构,索引,hibernate映射,表的大小......)


根据评论更新:

调整时,第一个问题是:我们需要调整什么? 它是将Criteria转换为SQL语句吗?如果是这样,直接提供sql语句就可以完成这项工作。

是sql语句的实际执行吗?如果是这样,确定发布代码产生的sql语句将是第一件事。

我从未见过存储过程使事情变得更快的真实案例。当然,这并不意味着这种情况不存在。但现代rdbms的优化器非常聪明。

因此,为了正确启动:设置日志记录,以便您看到每个带有精确时间戳的sql语句。以及您正在调整的完整过程的开始和结束时间。如果这是大约数百次执行,你将不得不重新设置。

这将告诉您是否执行了sql语句,并且占用了大量时间,如果它是一个导致问题的sql语句。

大多数时候sql语句都表现不好,但不应该妄下结论。


更新许多名称部分:

您可以使用InExpression:http://docs.jboss.org/hibernate/core/3.3/api/org/hibernate/criterion/InExpression.html一次查找多个对象。这比单个查询更快。

答案 1 :(得分:2)

  

在我的情况下,我知道名称是唯一的,但在name属性中添加索引注释并没有提高性能。瓶颈是findByName方法。

我不会相信这......直到你展示一些证明我错了的数字:)所以:

  • 仔细检查索引是否生成(检查DDL语句和数据库)。此查询需要此列的索引。
  • 检查生成的查询的查询计划(应该类似于SELECT * FROM USER u WHERE u.NAME = 'foo')和执行时间。

稍后,您可以考虑激活二级缓存并缓存查询。但数据库是开始的地方(过早缓存事物只会隐藏真正的问题)。

衡量一切! 如果你无法衡量它,你无法改善它。 - Lord Kelvin