Reputation: 169
I am using Laravel 7.0 and MySQL 5.7.
I am using Eloquent to fetch records from the database and when the database contains a large amount of data the response time becomes extremely slow.
A few examples are shown below:
Movie::where('title', 'LIKE', '%'.$search.'%')->take(10)->get();
to get the first 10 results that match a search string.
Movie::orderBy('rating', 'desc')->take(10)->get()
to get the 10 top rated movies.
Person::has('acted')->orderBy('popularity', 'desc')->take(10)->get()
to get the 10 most popular actors.
When I use a test database of ~1000 records the response time is acceptable, but when I switch to the live database of ~500,000 records it's horrible.
Any suggestions on how to improve the performance are appreciated!
Upvotes: 3
Views: 7572
Reputation: 142208
LIKE '%...'
is very inefficient. Each row must be checked by scanning the text.
Much better is the use of
FULLTEXT INDEX(title)
and
WHERE MATCH(title) AGAINST('+$search')
(Sorry, I don't know how to turn that into Laravel.)
Movie::orderBy('rating', 'desc')->take(10)->get()
That is probably helped by
INDEX(rating)
Upvotes: 2
Reputation: 13035
I don't think anyone will need to see 500,000 results in a single page, you would need to break it into parts and paginate the results. You can also use chunk, or use the cursor method. Choose one that would fit your needs best.
If this is to be viewed by the user, then I would suggest to paginate the results. This way the user can go to the next page and the next page etc. Or click on "Load More" button to make a request to your API to load page 2 then page 3 etc.
If this is just to process data for manipulation or update values, I would suggest using chunk or cursor.
To paginate:
Movie::where('title', 'LIKE', '%'.$search.'%')->paginate(20);
This will fetch 20 items per page.
Source: https://laravel.com/docs/pagination#introduction
To chunk:
Movie::where('title', 'LIKE', '%'.$search.'%')->chunk(200, function ($movies) {
foreach ($movies as $movie) {
//
}
});
This will fetch 200 at a time then loop through them.
Source: https://laravel.com/docs/eloquent#chunking-results
To use cursor method:
foreach (Movie::where('title', 'LIKE', '%'.$search.'%')->cursor() as $movie) {
//
}
This will iterate through your database records using a cursor, which will only execute a single query.
Source: https://laravel.com/docs/eloquent#chunking-results
Laracasts has a 10min video of it explained in detail.
Upvotes: 2