資料庫操作往往是 Web 應用的性能瓶頸。本文提煉 21 條經過生產驗證的優化準則,涵蓋查詢重寫、索引設計、架構調整、參數調優、工具鏈選型,助你構建高吞吐、低延遲的 MySQL 服務。
MySQL Query Cache 對確定性函數失效(NOW()、RAND()、CURDATE())。
-- ❌ 緩存失效
SELECT username FROM user WHERE signup_date >= CURDATE();
-- ✅ 使用變量替代
SET @today = CURDATE();
SELECT username FROM user WHERE signup_date >= @today;
關注 type(目標 ref 以上)、key(實際用到的索引)、rows(掃描行數)、Extra(Using filesort/Using temporary 需優化)。
-- ❌ 全表掃描
SELECT * FROM user WHERE country='China';
-- ✅ 找到即停
SELECT 1 FROM user WHERE country='China' LIMIT 1;
LIKE 'prefix%'` 可用索引,`LIKE '%suffix%'` 無效。大文本搜尋用全文索引或 Elasticsearch。
DECIMAL 不能與 INT JOIN 走索引;utf8 與 latin1 JOIN 會隱含轉換導致索引失效。
-- ❌ 全表排序,災難性效能
SELECT * FROM table ORDER BY RAND() LIMIT 1;
-- ✅ 方案一:主鍵範圍隨機
SELECT * FROM table
WHERE id >= (SELECT FLOOR(RAND() * (SELECT MAX(id) FROM table)))
LIMIT 1;
-- ✅ 方案二:應用層隨機 ID 查詢
只取需要的欄位:減少網絡傳輸、內存佔用、覆蓋索引命中率提升。
BIGINT UNSIGNED AUTO_INCREMENT 或 INT UNSIGNED。業務主鍵(如 email)建唯一索引,不做主鍵。例外:關聯表的組合主鍵(外鍵組合)。
內部存儲為 TINYINT,極致緊湊。適用於:性別、狀態、類型、部門等固定集合。
phpMyAdmin 「Propose table structure」基於此。數據量大時建議更準確。
NULL 需額外位圖標記,比較邏輯複雜(IS NULL/IS NOT NULL),索引統計不準。Oracle 中空字符串 = NULL,MySQL 不同。
PHP 使用 mysqli 或 PDO。
-- mysql_unbuffered_query() / mysqli_use_result()
-- 優點:記憶體佔用低,首行即可處理
-- 缺點:必須讀完或 free_result 才能執行下一查詢;無法用 num_rows/data_seek
-- 存儲
INSERT INTO t(ip) VALUES (INET_ATON('192.168.1.1')); -- 3232235777
-- 查詢
SELECT INET_NTOA(ip) FROM t WHERE ip BETWEEN INET_ATON('192.168.1.0') AND INET_ATON('192.168.1.255');
-- PHP
$ipLong = ip2long('192.168.1.1');
$ipStr = long2ip($ipLong);
無 VARCHAR/TEXT/BLOB 的表為 Static 格式,偏移量計算 O(1),掃描快、緩存友好。副作用:空間浪費。可用「垂直分割」分離變長欄位。
-- ❌ 鎖表 30 秒,網站掛掉
DELETE FROM logs WHERE created < '2023-01-01';
-- ✅ 分批刪除,每批 1000 行
WHILE (SELECT COUNT(*) FROM logs WHERE created < '2023-01-01') > 0 DO
DELETE FROM logs WHERE created < '2023-01-01' LIMIT 1000;
DO SLEEP(0.1); -- 讓出 CPU
END WHILE;
TINYINT < SMALLINT < MEDIUMINT < INT < BIGINT;DATE < DATETIME。預留擴展空間但不濫用。
| 場景 | 推薦引擎 | 理由 |
|---|---|---|
| 高並發讀、少寫、全文搜尋 | MyISAM | COUNT(*) 極快,全文索引成熟 |
| 高並發寫、事務、行鎖、崩潰恢復 | InnoDB | 行鎖、MVCC、ACID、雙寫緩衝 |
| 純內存、極速、數據可丟失 | MEMORY | Hash 索引精確查詢極快 |
| 時序、日誌、僅追加 | Archive / RocksDB (MyRocks) | 高壓縮、順序寫優化 |
JOIN FETCH/EntityGraph/resultMap 解決mysql_pconnect() / PDO::ATTR_PERSISTENT 理論減少連線開銷,實際易導致:連線泄漏、閒置連線佔用、Max connections 耗盡、Apache 多進程模型下連線池失控。建議:應用層連線池(HikariCP/Druid)替代。
-- 強制使用索引
SELECT * FROM t FORCE INDEX (idx_name) WHERE name='abc';
SELECT * FROM t FORCE INDEX (PRIMARY, idx_name) WHERE id>100;
-- 忽略索引
SELECT * FROM t IGNORE INDEX (idx_name) WHERE name='abc';
SELECT * FROM t IGNORE INDEX (PRIMARY) WHERE id>100;