Reputation: 4963
I have the following query for a queue system. The slowest query that troubles me is this one:
UPDATE workers SET work = $workid, last_used = NOW()
WHERE status = 1 AND work IS NULL ORDER BY last_used ASC LIMIT 1
When there's no load, the query executes within about 0.04
seconds but when many php scripts are executing this query the execution time goes higher and higher up to 40.0
seconds which is a big problem.
The table has around 40.000
entries and there is an index for status
and for liking_media
. The EXPLAIN for the query shows the parser is using an intersect with status
and liking_media
and gets about 3000
rows to process with ORDER_BY
. EXPLAIN
shows further using where; using filesort
.
The VPS behind it has 8 cores @ 2.5ghz, 12GB RAM. When the query runs very slow there's only low CPU usage. The CPU usage is much higher when the load begins to ramp up.
How can I greatly improve the performance of this query under load when many php scripts are running it? Can I tweak general mysql settings to fix it? Or is the table architecture bad or is an index missing? I'd like to be able to run about 300 of this queries per second without loosing performance.
Upvotes: 2
Views: 254
Reputation: 37045
I think this should avoid the ORDER BY, which is forcing the query to get all results instead of just the one with newest last_used
:
UPDATE workers
INNER JOIN (
SELECT MIN(last_used), worker_id
FROM workers
) AS newest_worker
ON newest_workder.worker_id = workers.worker_id
SET workers.work = $workid, workers.last_used = NOW()
WHERE workers.status = 1 AND workers.work IS NULL
Upvotes: 2
Reputation: 1269445
The problem with the query is finding the appropriate row(s) to update. For this query:
UPDATE workers
SET work = $workid, last_used = NOW()
WHERE status = 1 AND work IS NULL
ORDER BY last_used ASC
LIMIT 1;
You want the following composite index: workers(status, work, last_used)
.
This should speed the query and prevent multiple updates from locking each other out.
Upvotes: 2