Learn how to identify inefficient SQL queries and use MySQL’s mysqlslap to measure their impact under concurrent access.
Have you ever blamed PHP or Laravel for a slow application?
Before blaming your framework, take a look at your SQL queries.
A query can execute in milliseconds when you are the only user, but become a serious performance problem when dozens or hundreds of users execute it simultaneously.
One common cause is applying functions directly to indexed columns, preventing the database from efficiently using conventional indexes.
In this article, we’ll explore this problem and, more importantly, learn how to reproduce it using MySQL’s mysqlslap, a tool that simulates concurrent database clients.
1. The SQL Mistake
Imagine you have an orders table with more than 100,000 records and an index on created_at.
You need to retrieve all orders created on a specific date.
A common approach is:
Non-optimized query:
SELECT id
FROM orders
WHERE DATE(created_at) = '2026-10-30';The problem is that DATE() is applied directly to the indexed column.
This can prevent MySQL from efficiently using a conventional index for a range lookup.
A better approach is to compare the column directly against a date range.
Optimized query:
SELECT id
FROM orders
WHERE created_at >= '2026-10-30 00:00:00'
AND created_at < '2026-10-31 00:00:00';With an index on created_at, MySQL can potentially use an index range scan.
Both queries retrieve the same date range, but their execution costs can be very different.
2. Why Test With Concurrent Users?
Testing a query with a single user doesn’t tell you the whole story.
Consider what happens when 40 users execute the same query simultaneously.
Each query consumes database resources. When inefficient queries are repeated, they can increase CPU usage, I/O operations, and database execution time.
This can eventually affect the entire Laravel application.
That’s why we need to test more than just whether a query works.
We need to understand how it behaves under load.
3. Simulate Database Load With mysqlslap
mysqlslap is a command-line utility included with MySQL.
It simulates multiple database clients executing SQL statements simultaneously and reports execution timing.
Let’s use it to compare our two queries.
Step 1: Check mysqlslap
Run:
mysqlslap --versionIf the command is not available, install the MySQL client utilities for your environment.
Step 2: Prepare your database
For this example, let’s assume you have:
- A database named
performance_lab. - An
orderstable with at least 100,000 representative records. - An index on
created_at.
Check the index:
SHOW INDEX FROM orders;If necessary, create it:
CREATE INDEX idx_orders_created_at
ON orders(created_at);Make sure the date used in the queries actually matches records in your dataset.
Important: Run benchmarks against a dedicated local or staging database, never against production.
Step 3: Create the SQL files
Create two files in the same directory.
slow-query.sql
SELECT id
FROM orders
WHERE DATE(created_at) = '2026-10-30';fast-query.sql
SELECT id
FROM orders
WHERE created_at >= '2026-10-30 00:00:00'
AND created_at < '2026-10-31 00:00:00';We use SELECT id in both queries to keep the result set comparable.
Step 4: Run the test with one client
First, test the non-optimized query:
mysqlslap \
--host=127.0.0.1 \
--user=benchmark_user \
--password \
--create-schema=performance_lab \
--concurrency=1 \
--iterations=3 \
--number-of-queries=100 \
--delimiter=";" \
--query=slow-query.sqlNow test the optimized query:
mysqlslap \
--host=127.0.0.1 \
--user=benchmark_user \
--password \
--create-schema=performance_lab \
--concurrency=1 \
--iterations=3 \
--number-of-queries=100 \
--delimiter=";" \
--query=fast-query.sqlThe password option prompts you for your MySQL password.
At this point, both queries are being tested under the same conditions.
Step 5: Increase concurrency
Now let’s simulate 40 simultaneous database clients.
Run the same commands, changing:
--concurrency=1to:
--concurrency=40You can experiment with different concurrency levels:
| Test | Concurrent clients |
|---|---|
| 1 | 1 |
| 2 | 10 |
| 3 | 20 |
| 4 | 40 |
| 5 | 100 |
Keep the number of queries, dataset, database configuration, and other parameters consistent.
Each client executes approximately 100 queries per iteration in this example. With 40 clients, that’s approximately 4,000 queries per iteration.
Step 6: Understand the results
After each execution, mysqlslap reports information such as:
- Average time to run the workload.
- Minimum and maximum execution times.
- Number of concurrent clients.
- Approximate number of queries per client.
Compare the results of both queries at each concurrency level.
Your goal is to discover whether the non-optimized query becomes significantly more expensive as the number of concurrent clients increases.
Don’t expect universal benchmark numbers. Results depend on hardware, indexes, dataset size, database configuration, and caching.
Also, remember that mysqlslap measures database performance directly. It does not measure the complete Laravel HTTP request, PHP execution, middleware, or network overhead.
For that, you need an additional application-level load test.
4. Apply the Same Optimization in Laravel
Now let’s translate the SQL improvement into Eloquent.
Avoid:
Order::whereDate('created_at', '2026-10-30')
->get();Depending on the database grammar, this can generate a condition that applies a date function to the column.
Prefer:
use Carbon\CarbonImmutable;
$date = CarbonImmutable::parse('2026-10-30');
$start = $date->startOfDay();
$end = $start->addDay();
$orders = Order::query()
->where('created_at', '>=', $start)
->where('created_at', '<', $end)
->get();This uses a date range and allows MySQL to potentially take advantage of the conventional index.
If your application stores timestamps in UTC, calculate the boundaries in the appropriate business timezone and convert them before querying.
5. What This Means for Building Resilient Systems
An inefficient query doesn’t just make one request slower.
Under high concurrency, it can consume database resources, keep PHP workers occupied, increase response times, and affect other operations.
This can become a cascading performance problem.
Building resilient applications means understanding how your software behaves when demand increases.
Remember these practical principles:
- Write queries that can efficiently use available indexes.
- Inspect execution plans with
EXPLAIN. - Test database queries under concurrent workloads.
- Monitor slow queries in production.
- Measure application endpoints separately from database queries.
Performance and resilience are closely related.
An application that handles failures but cannot handle increasing workloads is not truly prepared for production.
Conclusion
Before blaming PHP or Laravel for a slow application, investigate your SQL queries.
A small change in how you filter an indexed column can make a significant difference under concurrent workloads.
Use mysqlslap to reproduce database load, compare query implementations, and understand how your database behaves when multiple clients access it simultaneously.
Don’t just test whether your query works. Test how it performs when your application is under pressure.
Build software that keeps working.
The SQL Mistake That Can Make Your Laravel Application 100x Slower
Learn how to identify inefficient SQL queries and use MySQL’s mysqlslap to measure their impact under concurrent access.
Introduction
Have you ever blamed PHP or Laravel for a slow application?
Before blaming your framework, take a look at your SQL queries.
A query can execute in milliseconds when you are the only user, but become a serious performance problem when dozens or hundreds of users execute it simultaneously.
One common cause is applying functions directly to indexed columns, preventing the database from efficiently using conventional indexes.
In this article, we’ll explore this problem and, more importantly, learn how to reproduce it using MySQL’s mysqlslap, a tool that simulates concurrent database clients.
1. The SQL Mistake
Imagine you have an orders table with more than 100,000 records and an index on created_at.
You need to retrieve all orders created on a specific date.
A common approach is:
Non-optimized query:
SELECT id
FROM orders
WHERE DATE(created_at) = '2026-10-30';The problem is that DATE() is applied directly to the indexed column.
This can prevent MySQL from efficiently using a conventional index for a range lookup.
A better approach is to compare the column directly against a date range.
Optimized query:
SELECT id
FROM orders
WHERE created_at >= '2026-10-30 00:00:00'
AND created_at < '2026-10-31 00:00:00';With an index on created_at, MySQL can potentially use an index range scan.
Both queries retrieve the same date range, but their execution costs can be very different.
2. Why Test With Concurrent Users?
Testing a query with a single user doesn’t tell you the whole story.
Consider what happens when 40 users execute the same query simultaneously.
Each query consumes database resources. When inefficient queries are repeated, they can increase CPU usage, I/O operations, and database execution time.
This can eventually affect the entire Laravel application.
That’s why we need to test more than just whether a query works.
We need to understand how it behaves under load.
3. Simulate Database Load With mysqlslap
mysqlslap is a command-line utility included with MySQL.
It simulates multiple database clients executing SQL statements simultaneously and reports execution timing.
Let’s use it to compare our two queries.
Step 1: Check mysqlslap
Run:
mysqlslap --versionIf the command is not available, install the MySQL client utilities for your environment.
Step 2: Prepare your database
For this example, let’s assume you have:
- A database named
performance_lab. - An
orderstable with at least 100,000 representative records. - An index on
created_at.
Check the index:
SHOW INDEX FROM orders;If necessary, create it:
CREATE INDEX idx_orders_created_at
ON orders(created_at);Make sure the date used in the queries actually matches records in your dataset.
Important: Run benchmarks against a dedicated local or staging database, never against production.
Step 3: Create the SQL files
Create two files in the same directory.
slow-query.sql
SELECT id
FROM orders
WHERE DATE(created_at) = '2026-10-30';fast-query.sql
SELECT id
FROM orders
WHERE created_at >= '2026-10-30 00:00:00'
AND created_at < '2026-10-31 00:00:00';We use SELECT id in both queries to keep the result set comparable.
Step 4: Run the test with one client
First, test the non-optimized query:
mysqlslap \
--host=127.0.0.1 \
--user=benchmark_user \
--password \
--create-schema=performance_lab \
--concurrency=1 \
--iterations=3 \
--number-of-queries=100 \
--delimiter=";" \
--query=slow-query.sqlNow test the optimized query:
mysqlslap \
--host=127.0.0.1 \
--user=benchmark_user \
--password \
--create-schema=performance_lab \
--concurrency=1 \
--iterations=3 \
--number-of-queries=100 \
--delimiter=";" \
--query=fast-query.sqlThe password option prompts you for your MySQL password.
At this point, both queries are being tested under the same conditions.
Step 5: Increase concurrency
Now let’s simulate 40 simultaneous database clients.
Run the same commands, changing:
--concurrency=1to:
--concurrency=40You can experiment with different concurrency levels:
| Test | Concurrent clients |
|---|---|
| 1 | 1 |
| 2 | 10 |
| 3 | 20 |
| 4 | 40 |
| 5 | 100 |
Keep the number of queries, dataset, database configuration, and other parameters consistent.
Each client executes approximately 100 queries per iteration in this example. With 40 clients, that’s approximately 4,000 queries per iteration.
Step 6: Understand the results
After each execution, mysqlslap reports information such as:
- Average time to run the workload.
- Minimum and maximum execution times.
- Number of concurrent clients.
- Approximate number of queries per client.
Compare the results of both queries at each concurrency level.
Your goal is to discover whether the non-optimized query becomes significantly more expensive as the number of concurrent clients increases.
Don’t expect universal benchmark numbers. Results depend on hardware, indexes, dataset size, database configuration, and caching.
Also, remember that mysqlslap measures database performance directly. It does not measure the complete Laravel HTTP request, PHP execution, middleware, or network overhead.
For that, you need an additional application-level load test.
4. Apply the Same Optimization in Laravel
Now let’s translate the SQL improvement into Eloquent.
Avoid:
Order::whereDate('created_at', '2026-10-30')
->get();Depending on the database grammar, this can generate a condition that applies a date function to the column.
Prefer:
use Carbon\CarbonImmutable;
$date = CarbonImmutable::parse('2026-10-30');
$start = $date->startOfDay();
$end = $start->addDay();
$orders = Order::query()
->where('created_at', '>=', $start)
->where('created_at', '<', $end)
->get();This uses a date range and allows MySQL to potentially take advantage of the conventional index.
If your application stores timestamps in UTC, calculate the boundaries in the appropriate business timezone and convert them before querying.
5. What This Means for Building Resilient Systems
An inefficient query doesn’t just make one request slower.
Under high concurrency, it can consume database resources, keep PHP workers occupied, increase response times, and affect other operations.
This can become a cascading performance problem.
Building resilient applications means understanding how your software behaves when demand increases.
Remember these practical principles:
- Write queries that can efficiently use available indexes.
- Inspect execution plans with
EXPLAIN. - Test database queries under concurrent workloads.
- Monitor slow queries in production.
- Measure application endpoints separately from database queries.
Performance and resilience are closely related.
An application that handles failures but cannot handle increasing workloads is not truly prepared for production.
Conclusion
Before blaming PHP or Laravel for a slow application, investigate your SQL queries.
A small change in how you filter an indexed column can make a significant difference under concurrent workloads.
Use mysqlslap to reproduce database load, compare query implementations, and understand how your database behaves when multiple clients access it simultaneously.
Don’t just test whether your query works. Test how it performs when your application is under pressure.
Build software that keeps working.