I ran into an interesting phenomenon with PHP and MySQL this morning while working on a web application I’ve been developing at work. Late last week, I noted that page loads in this application had gotten noticeably slower. With the help of Firebug, I was able to determine that a 1-second delay was consistently showing up on each PHP page load. Digging a little deeper, it became clear that the delay was a result of a change I recently made to the application’s MySQL connection logic.
Previously, I was using the IP address
127.0.0.1 as the connection host for the MySQL server:
$db = new mysqli("127.0.0.1", "myUserName", "myPassword", "myDatabase");
I recently changed the string to
localhost (for reasons I don’t recall):
$db = new mysqli("localhost", "myUserName", "myPassword", "myDatabase");
This change yielded the aforementioned 1-second delay. But why? The hostname
localhost simply resolves to
127.0.0.1, so where is the delay coming from? The answer, as it turns out, is that IPv6 handling is getting in the way and slowing us down.
I should mention that I’m running this application on a Windows Server 2008 system, which uses IIS 7 as the web server. By default, in the Windows Server 2008 hosts file, you’re given two hostname entries:
127.0.0.1 localhost ::1 localhost
I found that if I commented out the IPV6 hostname (the second line), things sped up dramatically. PHP bug #45150, which has since been marked “bogus,” helped point me in the right direction to understanding the root cause. A comment in that bug pointed me to an article describing MySQL connection problems with PHP 5.3. The article dealt with the failure to connect, which happily wasn’t my problem, but it provided one useful nugget: namely that the MySQL driver is partially responsible for determining which protocol to use. Using this information in my search, I found a helpful comment in MySQL bug #6348:
The driver will now loop through all possible IP addresses for a given host, accepting the first one that works.
So, long story short, it seems as though the PHP MySQL driver searches for the appropriate protocol to use every time (it’s amazing that this doesn’t get cached). Apparently, Windows Server 2008 uses IPV6 routing by default, even though the IPV4 entry appears first in the hosts file. So, either the initial IPV6 lookup fails and it then tries the IPV4 entry, or the IPV6 route invokes additional overhead; in either case, we get an additional delay.
The easiest solution, therefore, is to continue using
127.0.0.1 as the connection address for the database server. Disabling IPV6, while a potential solution, isn’t very elegant and it doesn’t embrace our IPV6 future. Perhaps future MySQL drivers will correct this delay, and it might go away entirely once the world switches to IPV6 for good.
As an additional interesting note, the PHP documentation indicates that a local socket gets used when the MySQL server name is
localhost, while the TCP/IP protocol gets used in all other cases. But this is only true in *NIX environments. In Windows, TCP/IP gets used regardless of your connection method (unless you have previously enabled named pipes, in which case it will use that instead).