Jozsef Hocza

Why Laravel Is Slow - Eager Loading, Caching

Slow Code series

I am back with the second part of “Why Laravel Is Slow”. I really could make a series of it. :)

I was told that the previous post’s name was a bit clickbait-ish. Guess what, if you think that laravel is slow, how would you search it on google? “Why My Laravel App Is Slow”? No-no. You are going to google: “Why Laravel Is Slow” :)

But let’s dive in.

You are messing with Eloquent the wrong way

I guess you are familiar with the following code:

<table>
<thead>
<tr>
  <th>#id</th>
  <th>username</th>
  <th>Posts</th>
  <th>Comments</th>
</tr>
</thead>
<tbody>
@foreach(\App\User::all() as $user) <!-- users table contains 25 users -->
<tr>
  <td>{{ $user->id }}</td>
  <td>{{ $user->username }}</td>
  <td>{{ $user->posts->count() }}</td>
  <td>{{ $user->comments->count() }}</td>
</tr>
@endforeach
</tbody>
</table>

So what happens here?

You are doing 51 queries to the database!

Now imagine you have 200 concurrent page loads. That is 10 200 queries against the database. That is insane.

master vader says the truth

Solution? Eager loading!

A few more keystrokes and you made your app MUCH faster.

\App\User::with('posts','comments')->get()

Notice that you cannot use all() since we are chaining here

By doing so, you are now making a SQL query with posts and comments joined, leaving you with only 1 query to execute instead of 1 + 25.

Now if the same concurrent 200 page loads hits your server, it will be now 200 queries.

We made it 51 times more effective and probably a lot faster, cool huh? :)

What if I tell you, that you can make it even faster?

Catch some speed with Cache

I am using Redis as cache driver.

<?php

// UsersController

public function index() {
    $users = Cache::remember('users', 120, function() {
        return User::with(['posts', 'comments'])->get();
    });
    return view('users.index',compact('users'));
}

Now that single combined SQL query runs if the cache expired which will happen after 120 minutes in the example.

More thoughts on Cache

You can also use rememberForever('users', function(){ /* stuff here */ })
If you cache something for 120 minutes or forever, what happens if a user writes a post? Will the count() update?

Nope. But. You can force the cache to expire with Cache::forget('users')

It would sound crazy to copy paste Cache::forget('users') into your PostsController and CommentsController, but well, it depends on your project size and how big it will get.
Probably you could utilize Events, something like: UserCreatedContent and put the “forget” there.

Hey, this is really up to you.

But one thing to keep in mind: When you execute Cache::remember('users') you will use the “users” key. So please try not to overwrite it. If you have more things to cache, always use proper Cache key.

If you would like to cache single posts, you need to make the key unique:

<?php

// PostsController

public function show($id) {
    // cache key will be posts.$id
    $post = Cache::remember('posts.'.$id, 120, function() use($id) {
        return Post::find($id);
    });

    return view('posts.show',compact('post'));
}

Conclusion

Eager loading and Caching are nice ways to boost our application’s speed.
However use eager loading only when it is required.

How do you use these tools in your Applications?


Share this:

SUBSCRIBE
Subscribe to my e-mail list.
You can unsubscribe anytime.