#IIS Hacks

Server & System Administration Archive · 2007–2013

An independent, non-commercial archive. Not affiliated with, endorsed by or connected to any vendor named in these notes.

Steeply angled row of network ports on a graphite switch faceplate lit by a diffuse cyan glow, with open space on the right

MySQL Connection Response Slow on Windows 2008 / 2012

Archived note. Originally published 18 February 2013 on iishacks.com. It describes PHP applications connecting to MySQL on Windows Server 2008 and 2012 running IIS 7 and IIS 8, as those systems behaved at that time, and is preserved here as a record. The server, PHP and MySQL versions of the period are no longer current, and modern client libraries handle a failed IPv6 connection attempt far better than those described here did. Verify against current vendor documentation before applying any of it to a live system.

The recovered note

One of the reasons a PHP and MySQL combination seems so slow with regard to response times when moving from Windows 2003 with IIS 6 to Windows 7 or 2008 with IIS 7, or 2012 with IIS 8, is the way many scripts connect to MySQL combined with IPv6 being enabled by default.

Many server installations leave WordPress or other PHP script installations defaulting to localhost when connecting to MySQL. Windows 7, 2008 and 2012 have an issue resolving localhost when IPv6 is enabled. On the site the note was written about, the initial response time from MySQL went from around 600 ms using localhost down to around 70 ms using 127.0.0.1. Those figures are what that one system measured and are recorded here as such, not as a general benchmark.

Where the problem is initial response time but MySQL otherwise functions well, the connection string may be at fault. Rather than disabling IPv6, change localhost to 127.0.0.1 in the scripts and see whether response times improve.

Why localhost stopped meaning 127.0.0.1

The name localhost is resolved, not assumed. On Windows Server 2003 it resolved through the hosts file, whose shipped entry mapped it to 127.0.0.1, and that was the end of it.

From Windows Vista and Windows Server 2008 onward, IPv6 was installed and enabled by default and preferred over IPv4 by the default address-selection policy. localhost now had two answers — ::1 and 127.0.0.1 — and the preference rules returned the IPv6 loopback first. A client that connected to the first address it was given was therefore attempting an IPv6 connection to a service that, in most installations of the period, was not listening on one.

Neither piece of software was misconfigured. Resolution was correct, the address preference was correct, and the database was listening exactly where it had been told to. What changed was that a name with one meaning for a decade now had two.

Why the failed attempt was expensive

A rejected connection is cheap; one that goes unanswered is not, and that difference produced the cost.

If nothing is bound to ::1 on the port, the loopback stack can refuse immediately and the client moves on. But where a firewall rule or a socket bound to a different scope caused the attempt to be silently dropped instead, the client had nothing to react to. It waited out the connection timeout and only then tried the next address in the list — the IPv4 loopback, where the database answered at once. The delay was therefore not slow database work at all: it was a fixed timeout paid before the real connection was ever attempted.

Two properties make it hard to recognise. It is constant rather than proportional, so it responds to neither load nor tuning, and it is paid per connection — on a PHP application of the period every page request opened a new connection and paid it again. Query timing inside the application looks healthy, because by the time a query runs the connection already exists. Only the total is wrong.

Why the fix is the connection string

The obvious response — turn off IPv6 — is wrong, and was already wrong in 2013.

IPv6 is not a bolt-on component of a modern Windows Server. Parts of the operating system depend on it being present, and disabling the protocol stack is not a supported configuration; the supported adjustments are to address preference rather than to the protocol's existence. The change is also disproportionate: a system-wide protocol removed to correct one application's connection string.

Writing 127.0.0.1 instead of localhost removes name resolution from the path entirely — no lookup, no address list, no preference order, no failed first attempt. It is scoped to the one application that has the problem, visible in that application's own configuration, and it survives every operating system update that follows. The cost is a hard-coded loopback address, which matters only if the database later moves to another host.

The same shape in other stacks

This is not a MySQL problem, and recognising the pattern is worth more than the fix. The ingredients are a hostname with more than one address, a preference order that puts an unserved address first, and a client that waits for a timeout rather than failing fast. The same delay appears wherever those three meet: an application server reaching a message broker or a cache by name, a monitoring agent reaching a collector, an HTTP client calling an internal API.

The signature is identical every time — a constant penalty, the same under light and heavy load, unaffected by tuning the service at the far end, and absent when the address is used directly. The diagnosis follows from it: read the order of the addresses the name resolves to, confirm what the service is actually bound to, then try the address directly and compare. If the difference is a clean step rather than an improvement in the average, the time was being spent before the connection existed.

Related material sits under Internet Information Server and Windows and Server, the note on installing PHP 5.3 FastCGI on Windows 2008 and IIS 7 covers the other half of this stack, and every original post is indexed at post archives. Current references worth reading instead are the MySQL documentation on IPv6 support, the MySQL reference manual and the PHP manual's Windows installation chapter.