公司數個網站託管於美商虛擬主機,MySQL 服務幾乎每天莫名掛掉又自動恢復。雖然流量極低,但懷疑觸發 CPU 使用限制。服務商客服排查後保證非主機問題,只能自行深入調查。
在 my.ini 加入:
[mysqld]
log="d:/temp/mysql.log"
log_slow_queries="d:/temp/mysql_slow.log"
long_query_time=1
重啟 MySQL 後,發現隨意一頁就產生幾十至上千條查詢!正常論壇頁面約 10 條查詢,負擔放大數十倍,怪不得會掛。
以「顯示所有地區分類及其文章數」為例,原始碼:
$sql1 = "SELECT aid, count(*) as cc FROM pz_content WHERE uid=$uid GROUP BY aid";
$rs1 = $db->query($sql1);
if (is_array($rs1)) {
foreach ($rs1 as $r1) {
echo id2name($r1->aid); // 每迴圈一次查詢
}
}
function id2name($aid) {
$sql = "SELECT ename FROM pz_area_a WHERE aid_a=".$aid;
$result = mysql_query($sql);
$row = mysql_fetch_object($result);
return $row->ename;
}
若有 1 萬個地區,就會執行 1 萬次查詢。實測:記憶體 6MB、耗時 16 秒、查詢 1001 次。
$sql1 = "SELECT pz_area.aid, pz_area.ename, tb1.cc
FROM pz_area
RIGHT JOIN (
SELECT aid, count(*) as cc FROM pz_content WHERE uid=$uid GROUP BY aid
) AS tb1 ON pz_area.aid = tb1.aid";
$rs1 = $db->query($sql1);
if (is_array($rs1)) {
foreach ($rs1 as $r1) {
echo $r1->ename; // 直接取得名稱,無需額外查詢
}
}
| 指標 | 優化前 | 優化後 | 再加索引 |
|---|---|---|---|
| 查詢次數 | 1001 | 1 | 1 |
| CPU 時間 | 16 秒 | 647 ms | 432 ms |
| 記憶體 | 6 MB | ≈6 MB | ≈6 MB |
單純改寫 SQL 即達 26 倍加速;在 pz_content.aid 加索引後再提升 33%。
long_query_time=1 快速定位問題語句。JOIN、子查詢 合併的,堅決不寫迴圈。此頁面優化告一段落,後續將持續清理其他高負載頁面。