Add article about jpa
This commit is contained in:
62
jpa-hibernate-stupid-performance.html
Normal file
62
jpa-hibernate-stupid-performance.html
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Achieving Stupid Performance with JPA and Hibernate</title>
|
||||||
|
<meta name="description" value="Making JPA and Hibernate break the sound barrier on a ridiculous data set.">
|
||||||
|
</head>
|
||||||
|
<body style="margin: 0; padding: 0; background-color: rgb(254, 249, 232)">
|
||||||
|
<div style="margin: 48px; width: 580px;">
|
||||||
|
<a href="/"><< back</a>
|
||||||
|
<h1 id="jpa-is-slow">JPA is slow, lets make it stupid fast</h1>
|
||||||
|
<p>
|
||||||
|
JPA is <b>not</b> 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 <pre><code>logging.level.org.hibernate.SQL=DEBUG</code></pre> and <pre><code>logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE</code></pre>,
|
||||||
|
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 <pre><code>@Batch(100)</code></pre>, it could work, only issue is JPA will batch each relationship, so each of our tables will still require
|
||||||
|
<pre><code>n/100</code></pre> calls where <pre><code>n</code></pre> 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 <pre><code>OUTER JOIN</code></pre>? 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"><pre><code>@EntityGraph</code></pre></a>
|
||||||
|
on your JPA repository, what this does is forces JPA to use a <pre><code>OUTER JOIN</code></pre>
|
||||||
|
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 <pre><code>@NamedEntityGraph</code></pre> 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>
|
||||||
|
<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>
|
||||||
|
<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 <pre><code>OUTER JOIN</code></pre>, 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 <pre><code>DBI</code></pre> universe when I write Perl. It's funny shocking Ruby on Rails and Java developers with my ~15ms queries :D
|
||||||
|
</p>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
17
trying-lite-xl.html
Normal file
17
trying-lite-xl.html
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Running mod_perl with Mojolicious in Docker</title>
|
||||||
|
<meta name="description" content="Trying Lite XL, my thoughts on the Lite XL text editor">
|
||||||
|
<meta name="keywords" content="Lite-XL text-editor text editor perl emacs">
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
</head>
|
||||||
|
<body style="margin: 0; padding: 0; background-color: rgb(254, 249, 232)">
|
||||||
|
<div style="margin: 48px; width: 530px;">
|
||||||
|
<a href="/"><< back</a>
|
||||||
|
<h1 id="trying-lite-xl">Trying Lite-XL</h1>
|
||||||
|
<p>
|
||||||
|
<!-- TODO: Write about why I like and dislike Lite-XL -->
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user