2010-06-15 60 views
2

我相信我可以提高休眠以下findByName查询的性能:什么是最快速的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;  
} 

在哪种方式,我应该改进它,甚至更重要的是:在这方面,我应该首先改善了吗?我将需要这个类的所有集合(无论是否是)和全部的全部对象。

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

UPDATE1:

的@index注释没有提高性能,因为数据库已经有了一个指标,因为我的唯一约束注释:

@UniqueConstraint(columnNames = {"name"}) 

UPDATE2:

  1. 请仔细阅读答案!

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

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

    等Hibernate需要每findByName查询之前刷新新创建的用户数据库。我用我自己的缓存解决方法(LinkedHashMap)解决了这个问题。

  3. 我通过延Schauder不尖提出的另一项改进:

    Read this answer得到一个:

    public Collection<User> findByNames(Collection<String> names) { 
        return session.createCriteria(User.class). 
          add(Restrictions.in("name", names)).list(); 
    } 
    
  4. 进一步的改进可以指定一些用户采集的时候不会偷懒进行更好的选择。

  5. 最后也是最重要的一个对我来说是:用一个列表代替我的SortedSet项目,做的getItems方法如下:

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

    与,Hibernate可以在项目收集工作(即添加)而不从数据库加载整个集合。

@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 

+0

*(...)因为数据库已经有一个索引,因为我唯一的约束注释*:这很可能。你真的需要检查查询计划。 – 2010-06-15 21:28:36

+0

好的。真的非常感谢你的评论!现在表现很合理。查看更新的问题。 – Karussell 2010-06-16 07:52:02

回答

2

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

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

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

之后,您可能会考虑激活二级缓存并缓存查询。但数据库是开始的地方(缓存太早会掩盖真正的问题)。

并测量的东西! 如果你不能测量它,你不能改善它。 - 凯尔文勋爵

+0

当表格很小时,索引可能不被使用,尽管它看起来像完美匹配。 当然,这是完全可能的,OP添加了注释,但没有改变架构:) – 2010-06-15 20:16:09

+0

感谢您的建议!我现在将检查索引是否已创建... – Karussell 2010-06-15 20:16:25

+0

@Jens Schauder:我应该如何更改模式? (我在慢速导入会话之前运行drop + create Schema) – Karussell 2010-06-15 20:17:36

3

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

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

正如你所看到的,你有很多调整选项。我认为唯一一个考虑索引的工作效果很好。

调整时:当然,当我们对这个问题的详细信息(如表的完整结构,索引,Hibernate映射,表格的大小...)的基础上的评论


UPDATE这可能会改变,第一个问题是:我们需要调整什么? 是否将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找到多个对象一气呵成。这将比单个查询更快。

+0

对不起,没有提供足够的信息这是一个商业项目,我会尽量给你最大的信息,而不会让我的老板生气。首先:我不能使用id。 我虽然我可以使用本机SQL查询或存储过程?这会提高性能吗? – Karussell 2010-06-15 19:53:34