`
myprincejava
  • 浏览: 29630 次
  • 性别: Icon_minigender_1
  • 来自: 来自太平洋
社区版块
存档分类
最新评论

发疯Hibernate分页问题,性能优化!

阅读更多

最近写了一个Hibernate分页,之前用游标来实现得到count 总行数..用HQL语句查询!可是现在问题出现了,但数据达到海量的时候,出现比较慢,现在要进行优化:

1.不能使用游标得到count总行数!

ScrollableResults rs = query.scroll(ScrollMode.SCROLL_INSENSITIVE);
  rs.last();
  return (rs.getRowNumber() + 1);

2.不能使用select count(*) from .....这样要得到count 和list 分布操作很麻烦!查询和count 都要同步!不想两次操作...

假如更改字段都要更改...不想这么做!要公用....

3.不能使用query.list().size()

 int count=query.list().size();

除了这三种方法实现得到count,不知道还能用什么方法去实现!相信javaeye上面大师们应该会出现这样情况吧!遇到过的提提建议...

分享到:
评论
76 楼 iamzealotwang 2008-08-23  
引用

同意caohj的说法,我的想法是用spring的AOP拦截Dao的所有方法,一旦方法以remove开头则肯定是删除记录,一旦方法以add开头则是删除记录,hibernate在执行方法的时候会返回一个int值,意义是影响的行数,在Context环境中维护一个static的总数值,一旦AOP拦截器起作用则此static总数值跟着发生变化!说的可能不清楚,我演示一下


1·remove方法一定是删除,add方法一定是添加。不过Dao的remove和add一定就只删除更改1条记录么?
   要是级联咋办?要是saveUpDate呢?我怎么知道他是save了,还是仅仅update了?

这个我觉得应该在数据库触发器里面做文章,那样保险。

2·记录共享 static ,数据量小的时候没有意义,对不?大的时候又需要读取文件。这里否存在进程之间的并发现象?就是操作系统里面读写者问题?

3·对于一般性查询语句,我认为记录总数是没有意义的 是吧?
75 楼 usomething 2008-08-22  
同意caohj的说法,我的想法是用spring的AOP拦截Dao的所有方法,一旦方法以remove开头则肯定是删除记录,一旦方法以add开头则是删除记录,hibernate在执行方法的时候会返回一个int值,意义是影响的行数,在Context环境中维护一个static的总数值,一旦AOP拦截器起作用则此static总数值跟着发生变化!说的可能不清楚,我演示一下

Dao某方法:
public int addProduct(List<Product> products){
    //具体实现方法,建议使用hibernate的批量插入
}

此方法被Spring拦截到在其拦截器中有一下代码
private static Long total=0L;

public void modifyTotal(int i){
  total+=(long)i;
}

如果你记录数实在多得惊人,建议一改变total后立即存入一个文件中或一个字段中!
74 楼 nowonder 2008-08-21  
mysql的表分区貌似一直都很烂
73 楼 icewubin 2008-08-21  
murainwood 写道
4300万条?
一张表么?还不拆?


银行项目还有百亿级的呢。

表只是个抽象概念,底层可以水平分区或者垂直分区的,上层未必要拆的。
72 楼 murainwood 2008-08-21  
4300万条?
一张表么?还不拆?
71 楼 xudong82113 2008-08-20  
burt 写道
ecsun 写道
count(*)一般来说会损失一些性能,加上first和max以后,基本可以不考虑损失的性能,如果实在能count(*)性能不满意
或以改为count(id),这种方式比count(*)性能要好一些
可以拿你的海量数据测一下,就明白了.

同时,如果真的数据量太海量了,count也就意义不大了.这时候可以这么做,比如每一次取出1000条记录,放前台去分页,当然,是不是一次1000条,要看你的访问压力了.

因为频繁访问海量数据,本身对数据库已经是一个很大的压力了,为了每次取得10条一页的数据,去访问一次数据库,划不来,一次多取一点,展现完了再去后台取.



用mssql,4300万的数据,相比select count(*),select count(id)并没有多大提升。
还是4分钟36秒左右。




4300万太过了, 我查看过资料, mysql在单表情况下, 1500万还能保持一个比较高的读写速度, 超过这个数量级, 性能就开始拐点下降了。
70 楼 soci 2008-08-20  
总数保存可以的,很多人这么用。
69 楼 iamzealotwang 2008-08-20  
引用

removeOrders 和 removeSelect 方法注释很明白了,前一个去掉ODER BY 后一个去掉 SELECT XXXX
只取中间的from xxx where xx 作为计算COUNT的条件


明白了 谢谢了。


引用

关于这种海量数据分页的问题,如果不是很在意总数的实事性,是否可以只在第一次查询的时候就算出count,后面的查询只查询内容而不用再次计算count呢?

即第一次查询:

1·查处满足sql条件的所有记录数目 ( count(*) )
2·返回前100条数据


第二次查询(即表示层调用了翻页)
1·查询满足sql条件的第101~200条记录


.....

第n次查询(查找到最后一页)
1·查询满足sql条件的第1000~1200条记录(假设count*的结果是1200)


那关于这点,我想的对么? 麻烦您了。
68 楼 icewubin 2008-08-20  
soci 写道
这个方法不知道多个JOIN TABLE会不会有问题
我的感觉,分页这东西很鸡肋的,还不如像BLOG那样 搞个日历,通过日期访问记录更有意义。


join table不会有问题,这个方法我已经在两个项目中用了半年多了。
67 楼 soci 2008-08-20  
iamzealotwang 写道
能看一下我那个问题么? 谢谢了


removeOrders 和 removeSelect 方法注释很明白了,前一个去掉ODER BY 后一个去掉 SELECT XXXX
只取中间的from xxx where xx 作为计算COUNT的条件

回到主贴 我还是认为如果海量数据要分页这个需求要灵活些
比如你是新闻网站,最好按日期列出新闻
如果是小说网站,比如给红楼梦分页,那就上一页下一页就可以或者按书的章节分。
如果是论文检索网站,你可以先按年度分页。
总之有很多办法把取总数这个问题绕过去。
66 楼 iamzealotwang 2008-08-20  
能看一下我那个问题么? 谢谢了

65 楼 soci 2008-08-20  
这个方法不知道多个JOIN TABLE会不会有问题
我的感觉,分页这东西很鸡肋的,还不如像BLOG那样 搞个日历,通过日期访问记录更有意义。
64 楼 icewubin 2008-08-20  
soci 写道
这样行不:
hql作为分页类的参数,方法签名:
listByPage(String hql,Page page)

int position = hql.toLowerCase().indexOf("from");
String totalHql = "select count(*) as totals " + hql.substring(position);
这个就可以算总数了,ORDER BY 其实不去掉也没事吧。


SpringSide就是这么做的,order by不去掉,可能会浪费无谓的排序计算吧。
63 楼 soci 2008-08-19  
这样行不:
hql作为分页类的参数,方法签名:
listByPage(String hql,Page page)

int position = hql.toLowerCase().indexOf("from");
String totalHql = "select count(*) as totals " + hql.substring(position);
这个就可以算总数了,ORDER BY 其实不去掉也没事吧。
62 楼 iamzealotwang 2008-08-19  
    /**
     * 去除hql的select 子句
     * @param hql
     * @return hql
     */
    public static String removeSelect(String hql) {
        Assert.hasText(hql);
        int beginPos = hql.toLowerCase().indexOf("from");
        Assert.isTrue(beginPos != -1, " hql : " + hql + " must has a keyword 'from'");
        return hql.substring(beginPos);
    }
    /**
     * 去除hql的orderby 子句
      * @param hql
     * @return hql
     */
    public static String removeOrders(String hql) {
        Assert.hasText(hql);
        Pattern pattern = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*"
            , Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(hql);
        StringBuffer buf = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(buf, "");
        }
        matcher.appendTail(buf);
        return buf.toString();
    }


其中这个removeOrders 和 removeSelect 谁能告诉我一下有什么用处么? 谢谢

还有关于这种海量数据分页的问题,如果不是很在意总数的实事性,是否可以只在第一次查询的时候就算出count,后面的查询只查询内容而不用再次计算count呢?

即第一次查询:

1·查处满足sql条件的所有记录数目 ( count(*) )
2·返回前100条数据


第二次查询(即表示层调用了翻页)
1·查询满足sql条件的第101~200条记录


.....

第n次查询(查找到最后一页)
1·查询满足sql条件的第1000~1200条记录(假设count*的结果是1200)

请问是这样一个过程么 ? 谢谢
61 楼 litianyi520 2008-08-19  
不行就用lucence 吧~ 索引文件搞~ 查库啥时候都慢~
60 楼 lu_pp 2008-08-19  
caohj 写道
建议用一个字段来存储count数,当有增加一条记录时count数+1,删除对应的一条记录时,count -1,查询分页的取count数时候直接去取一个字段记录就可以了.

按不同条件进行查询的话,好象就不好用了
我是使用SELECT COUNT 来返回总数
59 楼 。。。 2008-08-19  


放一个List getPage(int page)的方法出来,如果返回的list是empty的, 给用户弹个对话框就说找不着了。至于跳转....提供首尾页和上下翻的按钮, 再提供一个页码输入框......大不了我不告诉你到底有多少记录.....
一定要计算Count的话,尽量避免count(*)的出现。


不过我实际环境中还没用过这种方法. 只是一个想法。

计算Count数据库可能有点小负担,而且还是count(*)。
58 楼 coolenhai 2008-08-19  
[align=left][/align][size=x-small][/size][b][/b]
引用
引用
57 楼 我不知道 2008-08-07  
4300万的数据 用户不可能查出这么多的数据,1页1页的翻着看吧。
我要是用户我准是疯了。

可能给用户分页100页,每页100条(那就是100×100=10000条),就已经疯了。当然,有时候他想知道总数,那就给他提供一个可查看总数的地方。不过我认为,分页这里没有几个用户分了100页,1页1页看的。

所以可以考虑在查询条件方面限制用户查出那么多的数据,比如用户必须选择某个查询条件等。
而且,只在第1次访问的时候计算总数,其他翻页的时候把总数和分页数等传过去好了。

相关推荐

Global site tag (gtag.js) - Google Analytics