從 PHP 連接到 MySQL 非常慢
我剛剛重新安裝了 XAMPP。第一次打開 PHPMyAdmin 時,我注意到它非常慢。在 localhost 上打開每個頁面需要將近 5 秒的時間是沒有意義的。我做了一個小測試案例來轉移 PHPMyAdmin 的責任:
$con = new PDO("mysql:host=localhost;dbname=mysql", "root", ""); $statement = $con->query('SELECT host,user,password FROM user;'); $users = $statement->fetchAll(PDO::FETCH_ASSOC);
上面的腳本只需要大約 3 秒的時間來執行(儘管我第一次執行它時載入了接近 8 秒。)
然後檢查是否是 PDO 的錯,我嘗試使用
mysql_connect
:$con = mysql_connect("localhost", "root", ""); mysql_select_db("mysql", $con); $result = mysql_query('SELECT host,user,password FROM user;');
完成所需的時間完全相同。
一開始我以為是 PHP 的錯,但是 PHP 程式碼和靜態文件的提供比我點擊刷新要快。我通過執行這個小腳本來測試 PHP:
header("Content-Type: text/plain"); for($i = 0; $i < 5000; $i++) { echo sha1(rand()) . "\n"; }
5000 次
sha1
計算,頁面仍然顯示得比我刷新視窗要快。然後我認為這是 MySQL 的錯。但是同樣,沒有進行太多測試就可以確定 MySQL 的執行速度比我需要的要快。使用 MySQL CLI 客戶端,使用者選擇查詢甚至不需要可測量的時間——它在我什至讓返回鍵之前就完成了。
問題一定是 PHP 與 MySQL 的連接——據我所知。我可以找到大量關於 PHP 速度慢或 MySQL 速度慢的資訊,但沒有關於 PHP+MySQL 非常慢的資訊。
感謝任何可以幫助我解決這個問題的人!
我正在為 win32 使用 XAMPP 1.8.0(下載連結)
PHP 版本:5.4.4
MySQL 版本:14.14
編輯:計時後,原來是連接功能需要這麼長時間:
$time = microtime(true); $con = mysql_connect("localhost", "root", ""); mysql_select_db("mysql", $con); $con_time = microtime(true); $result = mysql_query('SELECT host,user,password FROM user;'); $sel_time = microtime(true); printf("Connect time: %f\nQuery time: %f\n", $con_time-$time, $sel_time-$con_time);
輸出:
連接時間:1.006148 查詢時間:0.000247
什麼會導致 PHP 花費大量時間連接到數據庫?CLI 客戶端、HeidiSQL 和 MySQL 工作台即時連接
是不是您的 mysql 在您連接時嘗試執行 rev-dns 查詢?嘗試添加到 my.cnf 部分 mysqld: skip-name-resolve。
這幾乎是從我在這裡的回答中逐字記錄的,但我知道我們對 SO 上的僅連結答案不滿意,所以我想你們也會這樣做:-)
如果您遇到此問題並使用 Windows 7 之前的 Windows 版本,這可能不是您問題的答案。
為什麼會這樣?
此問題的原因是 IPv4 與 IPv6。
當您使用主機名而不是 IP 地址時,MySQL 客戶端首先執行
AAAA
(IPv6) 主機查找名稱,如果成功將名稱解析為 IPv6 地址,則首先嘗試此地址。如果任一步驟失敗(名稱解析或連接),它將回退到 IPv4,執行A
查找並嘗試此主機。這實際上意味著,如果 IPv6
localhost
查找成功但 MySQL 未綁定到 IPv6 環回,則您需要等待一個連接超時周期,然後才會發生 IPv4 回退並且連接成功。這在 Windows 7 之前不是問題,因為
localhost
解決方案是通過 hosts 文件完成的,並且它僅預配置了127.0.0.1
- 它沒有與它的 IPv6 對應項一起提供::1
。然而,由於 Windows 7,解析內置於 DNS 解析器中
localhost
,原因如下 所述。這意味著 IPv6 查找現在將成功 - 但 MySQL 未綁定到該 IPv6 地址,因此連接將失敗,您將看到此問題中概述的延遲。那很好。告訴我如何解決它!
你有幾個選擇。環顧網際網路,一般的“解決方案”似乎是明確使用 IP 地址而不是名稱,但有幾個理由不這樣做,兩者都與可移植性相關,都可以說都不重要:
- 如果您將腳本移動到另一台僅支持 IPv6 的機器上,您的腳本將不再工作。
- 如果您將腳本移動到基於 *nix 的託管環境,則魔術字元串
localhost
將意味著 MySQL 客戶端如果配置了一個 Unix 套接字,則更願意使用它,這比基於 IP 環回的連接更有效它們聽起來很重要嗎?
他們不是。您應該設計您的應用程序,以便在配置文件中定義此類事情。如果您將腳本移動到另一個環境,則可能還需要配置其他內容。
總之,使用 IP 地址不是最好的解決方案,但它很可能是可以接受的。
那麼最好的解決方案是什麼?
最好的方法是更改 MySQL 伺服器使用的綁定地址。然而,這並不像人們想像的那麼簡單。與 Apache、Nginx 和幾乎所有其他健全的網路服務應用程序不同,MySQL 僅支持單個綁定地址,因此不僅僅是添加另一個綁定地址的情況。不過幸運的是,作業系統在這裡確實支持了一些魔法,所以我們可以讓 MySQL 同時使用 IPv4 和 IPv6。
您需要執行 MySQL 5.5.3 或更高版本,並且需要使用
--bind-address=
命令行參數啟動 MySQL。您有 4 個選項docs,具體取決於您要執行的操作:
- 您可能熟悉的,並且您最有可能(有效)使用的,
0.0.0.0
. 這將綁定到機器上所有可用的 IPv4 地址。即使您不關心 IPv6,這實際上也可能不是最好的做法,因為它與::
.- 顯式 IPv4 或 IPv6 地址(例如
127.0.0.1
或::1
用於環回)。這會將伺服器綁定到該地址並且僅綁定到該地址。- 魔線
::
。這將在 IPv4 和 IPv6 模式下將 MySQL 綁定到機器上的每個地址,包括環回和物理介面地址。這存在潛在的安全風險,僅當您需要 MySQL 接受來自遠端主機的連接時才這樣做。- 使用IPv4 映射的 IPv6 地址。這是 IPv6 中內置的一種特殊機制,用於在 4 -> 6 轉換期間向後兼容,它允許您綁定到特定的 IPv4 地址,它是 IPv6 的等效地址。除了“雙環回”地址之外,這不太可能對您有用
::ffff:127.0.0.1
。對於大多數人來說,這很可能是最好的解決方案,只綁定到環回但允許 IPv4 和 IPv6 連接。我需要修改hosts文件嗎?
沒有。不要修改主機文件。DNS 解析器知道如何處理
localhost
,重新定義它充其量是沒有效果的,最壞的情況是把解析器弄得一團糟。由於相關但略有不同的原因,這也可以解決問題。
如果沒有此配置選項,MySQL 將嘗試通過
PTR
DNS 查詢將所有客戶端連接 IP 地址解析為主機名。如果您的 MySQL 伺服器已經啟用了使用 IPv6,但連接仍然需要很長時間,可能是因為反向 DNS (PTR
) 記錄配置不正確。禁用名稱解析將解決此問題,但它確實有其他後果,特別是配置為在條件下使用 DNS 名稱的任何訪問權限
Host
現在都將失敗。如果您要這樣做,您需要將所有授權配置為使用 IP 地址而不是名稱。