-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 
Author Message
 Post subject: Pagination is 3 times slower than getting all rows!
PostPosted: Thu Dec 06, 2007 5:15 am 
Newbie

Joined: Thu Dec 06, 2007 4:52 am
Posts: 4
I'm using Liferay 4.2.1, and found some weird result with hibernate query.

There's a query with an outer join, and here is the portion of the hql:

Code:
StringBuffer hql = new StringBuffer();
hql.append("from com.liferay.portlet.journal.model.impl.JournalArticleImpl as article ");
hql.append("left join fetch article.categories cat ");
hql.append("where cat.id=? ");
hql.append("and article.primaryKey.groupId = ? ");
hql.append("and article.approved=true ");
hql.append("and article.expired=false ");
hql.append("and article.displayDate <= ? ");
hql.append("and (article.expirationDate is null or article.expirationDate > ?) ");
hql.append("and article.primaryKey.version =(select max(a.primaryKey.version) from com.liferay.portlet.journal.model.impl.JournalArticleImpl as a where a.primaryKey.articleId = article.primaryKey.articleId) ");
hql.append("order by article.displayDate desc");


This part of the code does not seem to be much of problem, I'm running it on my machine (slow machine), with 1GB of memory, there are 2000 rows of data, and getting all of them requires about 1 second.

Now, when I added the following for pagination:

Code:
query.setFirstResult(beginIndex);
query.setMaxResults(max);


and then

Code:
Date t1 = new Date();
query.list();
Date t2 = new Date();


And setting max to 10, it takes 3 to 5 times longer than if I just retrieve the whole list (without setFirstResult() and setMaxResults()). It takes 3 to 5 seconds to get 10 records. This does not make any sense.

As the amount of data grows, it is getting slower and slower. If I paginate over 100 records, the performance is ok, but still slower than getting the whole 100 records. But as data grow, it is getting slower.

Using setFirstReuslt() and setMaxResults() is the default method for paginating, but this is killing us.

It takes 3 to 5 time longer to get 10 records than retrieving the whole 2000 records!!! Now I can get the whole list, then spending a few more millisecs using sublist() to get the same 10 records, and I'm still better off, time-wise. But this is not the way to go, resources-wise.

I'm using:

OS: Linux (Ubuntu 07.04)
Liferay 4.2.1
PostgreSQL 8.1.10
Java 1.5.0_08

Could someone give a hint what is going on here?

thanks

weird coder


Top
 Profile  
 
 Post subject:
PostPosted: Fri Dec 07, 2007 2:10 pm 
Newbie

Joined: Fri May 25, 2007 2:09 pm
Posts: 6
I suspect the pagination through the sorted by desc displayDate. Is this column indexed?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Dec 10, 2007 12:11 am 
Newbie

Joined: Thu Dec 06, 2007 4:52 am
Posts: 4
displayDate is indexed, and as I described above, the whole query's performance is ok if I retrieve everything. It's really slow only when I do pagination.

The thing that does not make sense is that pagination is killing the system, performance-wise. It is supposed to do the opposite. The worst part is that this is the standard recommended pagination method for Hibernate. How can that be a standard and recommended method, and yet, with a such lousy performance?

For the same amount of data, SQL would return the desired data (same as what we do with HQL) in a fraction of second. Give that hibernate needs to create the objects etc, I can accept that the whole deal takes about 1 sec (although this is still slow), but when it takes 3 to 5 times slower, this is not going to fly.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Dec 10, 2007 2:46 am 
Beginner
Beginner

Joined: Fri Jun 25, 2004 11:47 am
Posts: 34
Could you post the generated sql and check/try it on the database?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Dec 10, 2007 9:24 pm 
Newbie

Joined: Thu Dec 06, 2007 4:52 am
Posts: 4
I finally figured that out. Hibernate generates a nasty n+1 sql query, looping on every row of the table.

The tables journalarticle and journalcategory have a many-to-many relationship, and hibernate creates an intermediate table articles_categories between them. The sql statement generated looks basically like the hql. and if I don't do pagination, that's just fine. If I want pagination, Hibernate should have just added
Code:
... limit ? offset ?

at the end of the sql statement, and would have a nice, clean solution. Instead, Hibernate will fetch every single row, loop every row found on the intermediate table articles_categories. Therefore, if I have 2000 records, Hibernate would query the database 2001 times!!!

I just get rid off the hql, and write my own native sql (which defeats the purpose of using Hibernate, really!), and now paginating time is less than 0.2 second, instead of 3 to 5 seconds. And for data less than 200 rows, the time is about 30 millisecs.

Now, I turn on every place where we have many-to-many relationship, and found that Hibernate is doing this kind of stupid things all over the place, even though we are following the best practices recommended.

I'm less and less impressed with Hibernate now.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 11, 2007 1:37 am 
Newbie

Joined: Thu Dec 06, 2007 4:52 am
Posts: 4
The trouble is, in a lot of many-to-one relationship, that also results in a huge number of queries. For example:

Code:
<class name="com.test.example.MyCoolThing" table="cool_thing">
   <cache usage="read-write" />
   <composite-id name="primaryKey" class="com.test.example.MyCoolThingPK">
      <key-property name="id1" />
      <key-property name="id2" />
   </composite-id>
   <property name="name" type="string" column="name_" length="64"/>
   <set name="jewels"
       lazy="false"
       inverse="true"   
       cascade="all">
       <key>
         <column name="jewelid1"/>         
         <column name="jewelid2"/>         
       </key>
       <one-to-many class="com.test.example.Jewel"/>
   </set>
</class>
   
<class name="com.test.example.Jewel" table="jewel">
   <cache usage="read-write" />
   <id name="id" type="java.lang.String" column="id">
      <generator class="assigned" />
   </id>
   <property name="jewelname" type="string" column="name_" length="64"/>
   <many-to-one name="coolThing" class="com.test.example.MyCoolThing"
      not-null="false"
      lazy="false">
      <column name="jewelid1"/>
      <column name="jewelid2"/>
   </many-to-one>
</class>

And now, with the following query:
Code:
query.append("FROM com.test.example.MyCoolThing WHERE ");
query.append("id1 = ? ");
query.append("ORDER BY ");
query.append("name ASC");

This result into huge number of queries, regardless of whether lazy is set to false or true, this has no effect. It will query the table cool_thing first, then for every row found, it will query the table jewel. For large number of data, the number of queries just kill the database (and yes, I have the columns indexed).

Now, I have really huge doubt with Hibernate. It's fine when you have simple single-table thing, but as soon as you have a bit complicated stuff, it just doesn't fly. In this case, OR-mapping just sucks big time, and the structure above is a pretty simple example, compared to what we have.

Could someone tell what's wrong with the structure and query above? How would Hibernate gurus recommend in this case?


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.