現今資料庫操作已成為應用系統效能瓶頸,尤其在 Web 應用中更明顯。優化不只是 DBA 的事,開發人員更需關注。以下 21 條針對 MySQL 的實戰優化技巧,按重要性排序。
MySQL 內建查詢快取,相同查詢自動命中快取。但某些函數會導致快取失效:
-- 不走快取(函數結果不固定)
SELECT * FROM user WHERE signup_date >= CURDATE();
-- 走快取(用變數代替函數)
SET @today = DATE('2024-01-15');
SELECT * FROM user WHERE signup_date >= @today;
原則: 避免在 WHERE 條件中直接使用 NOW()、RAND()、CURDATE() 等非確定性函數。
EXPLAIN SELECT * FROM users JOIN orders ON users.id = orders.user_id WHERE users.status = 1;
重點關注 type、key、rows、Extra 欄位,找出全表掃描、未用索引、臨時表等問題。
-- 低效 SELECT * FROM user WHERE country = 'China'; -- 高效 SELECT 1 FROM user WHERE country = 'China' LIMIT 1;
找到第一筆即停止掃描,大幅降低 I/O。
LIKE 'keyword%'` 可用索引,`LIKE '%keyword%'` 無法使用一般索引,需全文索引或外部搜尋引擎。
-- 錯誤:DECIMAL JOIN INT,字符集不同 SELECT * FROM users JOIN companies ON users.state = companies.state;
雙方欄位型別、長度、字符集完全一致才能用上索引。
-- 極差效能 SELECT * FROM user ORDER BY RAND() LIMIT 1; -- 替代方案 SELECT COUNT(*) FROM user; -- 取得總數 N SET @offset = FLOOR(RAND() * N); SELECT * FROM user LIMIT @offset, 1;
只查需要的欄位,減少網路傳輸、記憶體佔用、磁碟 I/O。
推薦 INT UNSIGNED AUTO_INCREMENT。VARCHAR 主鍵效能差,除非是關聯表的複合主鍵(外鍵組合)。
性別、狀態、部門等有限值集合,ENUM 內部存 TINYINT,極度緊湊高效。
在 phpMyAdmin 點「Propose table structure」或執行:
SELECT * FROM table_name PROCEDURE ANALYSE();
NULL 需額外位元組標記,比較邏輯複雜,索引統計也不準。除非業務真正需要「未知」語意。
$stmt = $mysqli->prepare("SELECT username FROM user WHERE state=?");
$stmt->bind_param("s", $state);
$stmt->execute();
$result = mysqli_query($link, $sql, MYSQLI_USE_RESULT); // 不緩衝
while ($row = mysqli_fetch_assoc($result)) { /* 逐行處理 */ }
mysqli_free_result($result);
節省記憶體,首行即可開始處理,但需讀完或釋放才能執行下一查詢。
-- 寫入
UPDATE users SET ip = INET_ATON('192.168.1.1') WHERE id = 1;
-- 讀出
SELECT INET_NTOA(ip) FROM users WHERE id = 1;
PHP 對應 ip2long() / long2ip()。範圍查詢 BETWEEN 極速。
無 VARCHAR/TEXT/BLOB 的表為固定長度,偏移量計算快、易快取、易重建。可用「垂直分割」將變長欄位移出。
將低頻、大欄位(如 address、last_login)拆到另一表,核心表保持精簡,提升快取命中率。注意:分割後避免頻繁 JOIN,否則得不償失。
while (true) {
$affected = mysqli_query($link, "DELETE FROM logs WHERE log_date <= '2023-01-01' LIMIT 1000");
if (mysqli_affected_rows($link) == 0) break;
usleep(50000); // 讓出 CPU、減輕鎖壓力
}
參考 Storage Requirements 選最小足夠型別:TINYINT < SMALLINT < MEDIUMINT < INT < BIGINT。預留擴充空間但別過度。
| 引擎 | 適用場景 | 特點 |
|---|---|---|
| MyISAM | 大量讀、少寫、需 COUNT(*) 極快 | 表鎖、不支援事務 |
| InnoDB | 讀寫混合、高併發、需事務 | 列鎖、支援事務、外鍵、崩潰恢復 |
⚠️ 小心 N+1 查詢問題,適時用 Eager Loading。
mysql_pconnect() 理論上減少連線建立開銷,但 Apache 多進程模型下易導致連線耗盡、記憶體洩漏、檔案描述符耗盡。現代架構建議用連線池而非持久連線。