Skip to content
EuroVPSEuroVPS

Slow website, idle server: finding where the time goes

The database is blamed first because it is where the wait is felt; measuring each tier of one slow request shows where the time is actually spent.

By EuroVPS EngineeringThe engine room

6 min read
In this article 4 sections
TL;DR
  • A slow page and a busy database look the same from the connection count, because the database is the last synchronous stop in most requests and inherits every stall above it.
  • Trace one slow URL tier by tier: the edge, the box, the application, then the database. The tier that owns the time is rarely the one named in the ticket.
  • A bigger machine shortens the part of the request that was already short. Measure before anything is bought.

Why the database is the usual suspect

When a page slows down, the database is where the symptom becomes visible first. Connections pile up, the process list fills with queries in the sending data state, and the slow query log starts growing. All of that is real, and none of it says where the time went. The database sits at the end of most request paths. A page that calls it two hundred times instead of twice, a worker that holds a connection open while it waits on a payment gateway, a cache that is being bypassed on every hit: each of these shows up as database load, because the database is doing exactly what it is asked, more often than it should be asked.

The same applies to the server. A box at high load is a box being given work. Whether the work is necessary is a question the load graph cannot answer.

Trace one request, not the whole site

The method is to pick one slow URL and follow it through the tiers, measuring at each boundary. Start from outside, from a machine that is not on the same network, and let curl report where the time goes before the first byte arrives.

TERMINAL
$ curl -o /dev/null -s -w 'dns %{time_namelookup}  connect %{time_connect}  tls %{time_appconnect}  ttfb %{time_starttransfer}  total %{time_total}\n' https://www.example.com/slow-page

If the time to first byte is most of the total, the wait is on the server side. If the total is far larger than the time to first byte, the page is heavy or the path between the visitor and the edge is the story, and the server was not part of the wait.

On the box, sample the CPU while the slow request is in flight, not an idle hour later.

TERMINAL
$ mpstat -P ALL 1 5
$ vmstat 1 5

A server that is mostly idle during a slow request, with no iowait and no steal, has cleared itself. The time is being spent waiting on something, not computing.

Then the application. Most frameworks can log, per request, the number of queries issued, the total time spent in them, and the time spent in outbound HTTP calls. Turn that on for the slow URL. Two numbers settle the argument: how many queries one page issues, and how much of the wall-clock time was spent outside the application entirely.

Finally the database, with the slow query log on and the threshold low enough to catch what matters.

TERMINAL
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 0.2;
SET GLOBAL log_queries_not_using_indexes = ON;

Read the log for the window of the slow request. A handful of queries that each take seconds is a database problem. Thousands of queries that each take a millisecond is an application problem the database is being blamed for.

The patterns that measure out

With the tiers separated, the cause tends to take one of a few shapes.

An endpoint called in a loop. A front end that re-fetches the same resource on every state change, or a page that issues one query per row instead of one query per page. The database answers each call quickly; the count is the problem.

A synchronous call to someone else. A payment provider, an ERP, a shipping API, a tag manager, sitting in the request path behind a long timeout. The server is idle for the whole wait, and the page is as slow as the slowest third party it depends on.

A cache that is being defeated. Headers that forbid caching on pages that do not change, or no edge cache in front of anonymous traffic that was never dynamic.

Pools sized by guesswork. Worker, connection and buffer pools set too large as often as too small. A pool far larger than the machine can serve concurrently turns a queue into a thrash.

Real exhaustion is the last shape, and when it appears it is usually downstream of one of the first three. The machine running out of something is the end of the story, not the beginning.

What a bigger server changes

Adding cores or memory shortens the part of the request that was computing. If that part was already small, the page does not get faster; the wait moves with it. The loop still loops, the third party still takes its time, the cache is still bypassed. The upgrade also hides the measurement that would have found the cause, because the graph that was red is now amber, and the ticket closes.

This is why we trace before we quote. A healthy platform stays boring, and the work is to find the tier that owns the time, even when that tier sits above anything we operate, and to hand back the measurement that points at the line.

Straight answers

Why is my website slow if the server has plenty of CPU and RAM?+

Because the time is being spent waiting, not computing: on a loop of calls, on a third-party API in the request path, on a cache that is bypassed, on a query issued thousands of times. An idle server during a slow request is a strong hint that the wait lives above the hardware.

Is a slow website usually the database?+

The database is usually where the wait is seen, because it is the last synchronous stop in the request and inherits every stall above it. Whether it is the cause is decided by the slow query log: a few slow queries point at the database, a flood of fast ones points at the application.

Should I buy more RAM or a bigger server to fix a slow site?+

Not before the trace. Capacity shortens the computing part of a request. If the request was waiting rather than computing, the upgrade moves the wait rather than removing it, and it hides the graph that would have found the cause.

How do you tell an application problem from a database problem?+

Count and time. Per request: how many queries, how long in total, how long in outbound calls. Per query: the slow log. If the per-query time is low and the count is high, the application is asking too often. If the per-query time is high, the query, the index or the schema is the problem.

Bring us your slowest URL and we will trace it tier by tier to the line that owns the time.

Talk to an engineer.

Talk to an engineer

You’ve reached the end.

Put the reading to work

What are
you running?

Tell us about the workload, traffic and recovery requirements. Start with the server builder, or talk through the details with an engineer.

Written by the engineers who build and run the infrastructure it describes.
Talk to an engineer
XIn

6 min left