whoops
This commit is contained in:
56
posts/jpa-hibernate-stupid-performance.html
Normal file
56
posts/jpa-hibernate-stupid-performance.html
Normal file
@@ -0,0 +1,56 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Achieving Stupid Performance with JPA and Hibernate</title>
|
||||
<meta name="description" content="Making JPA and Hibernate break the sound barrier on a ridiculous data set."/>
|
||||
<link rel="stylesheet" href="/index.css"/></head>
|
||||
<body>
|
||||
<a href="/"><< Back</a>
|
||||
<h1 id="jpa-is-slow">JPA is slow, lets make it stupid fast</h1>
|
||||
<p>
|
||||
JPA is <strong>not</strong> performant. Mainly because of things like batching, and this incessant idea that
|
||||
querying relationships lazily is somehow fast, its literally just deferring the work, which makes sense
|
||||
if you potentially don't want to use the data, but most people want that data anyways, or serialize it.
|
||||
</p>
|
||||
<p>
|
||||
I experienced this recently with a massive data set, this database was 1 core table of 4 million rows, followed by multiple child One-To-Many table
|
||||
of close to 10 million rows, and a single One-to-One with ~4 million rows as well. To use the service built on top of this massive data, you had to search
|
||||
with JPA search specifications, which turns out also to be insanely slow. The benchmark starts us at 15 or so minutes, which is obviously unacceptable.
|
||||
</p>
|
||||
<h2 id="go-fast">Time to go fast</h2>
|
||||
<p>
|
||||
By enabling <code>logging.level.org.hibernate.SQL=DEBUG</code> and <code>logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE</code>
|
||||
you're able to see the ridiculous number queries JPA is executing, they're un-countable, there's so many!! How can we fix this?
|
||||
</p>
|
||||
<p>
|
||||
If you're thinking of using <code>@Batch(100)</code>, it could work, only issue is JPA will batch each relationship, so each of our tables will still require
|
||||
<code>n/100</code> calls where <code>n</code> is the number of rows with relationships. So theoretically a 100x speed bump right? Well, it's actually more
|
||||
like a 33x performance bump, since you still have the overhead of making the database connections, and need to get a lock every time you select. Doing this we moved to around
|
||||
3-5 minutes depending on load per large call. This however, was still unacceptable.
|
||||
</p>
|
||||
<p>
|
||||
What if instead we just forced JPA to use <code>OUTER JOIN</code>? Isn't that exactly what the ANSI SQL wizards would tell you to do?
|
||||
Obviously, this is the solution, one single connection, 1 lock, and let the B-Tree optimizations inside of the database handle all of this ugliness for us.
|
||||
To do this you need to use something called <a href="https://www.baeldung.com/jpa-entity-graph"><code>@EntityGraph</code></a>
|
||||
on your JPA repository, what this does is forces JPA to use a <code>OUTER JOIN</code>
|
||||
to join relationships. This even works on nested entities, say I have Entity A with a child B which also has a One-to-Many on C, all you need to do
|
||||
is add the relationship to the entity graph with the <code>@NamedEntityGraph</code> annotation, then provide it with a few nice args like name,
|
||||
and attributeNodes which will point to your sub entity. Then finally on your JPA repository call you stitch the entity graph back together.
|
||||
</p>
|
||||
<pre>
|
||||
<code>
|
||||
@Repository
|
||||
public interface ARepository extends JpaRepository<A, UUID>, JpaSpecificationExecutor<A> {
|
||||
@Override
|
||||
@EntityGraph(attributePaths = { "bs", "B.cs" }) // Where B's are children of A's, and C's are children of B's.
|
||||
Page<A> findAll(@Nullable Specification<A> specification, Pageable pageable);
|
||||
}
|
||||
</code>
|
||||
</pre>
|
||||
<p>
|
||||
After applying these, the query time went down from 15 minutes to ... *drum-roll* ... 3 seconds. So please, for the love of all that is computationally efficient, force your JPA
|
||||
to use <code>OUTER JOIN</code>, or even roll your own SQL, ORM's really are one of the biggest trade-offs, and they really can bite you. This is why tend to
|
||||
stay in the <code>DBI</code> universe when I write Perl. It's funny shocking Ruby on Rails and Java developers with my ~15ms queries :D
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user