避免使用 NOW()、RAND()、CURDATE() 等非確定性函數,改用應用層變數:
-- 不走緩存
SELECT * FROM user WHERE signup_date >= CURDATE();
-- 走緩存
$today = date('Y-m-d');
SELECT * FROM user WHERE signup_date >= '$today';
關注 rows (預估掃描行)、key (實際用索引)、Extra (Using index/where/temporary/filesort)。
-- 低效 SELECT * FROM user WHERE country='China'; -- 高效 SELECT 1 FROM user WHERE country='China' LIMIT 1;
LIKE 'prefix%'` 可用索引,`LIKE '%suffix'` 無法用索引 (需全文索引)。
-- 錯誤:DECIMAL JOIN INT、utf8 JOIN latin1 -- 正確:同類型、同字符集、雙方都有索引
-- 災難寫法 SELECT * FROM user ORDER BY RAND() LIMIT 1; -- 正確:應用層隨機 ID $count = SELECT COUNT(*) FROM user; $rand = mt_rand(0, $count-1); SELECT * FROM user LIMIT $rand, 1;
只取需要欄位:減少 I/O、記憶體、CPU、網傳,利用索引覆蓋。
業務主鍵 (如 email) 設唯一索引,不做主鍵。關聯表除外 (複合主鍵 = 外鍵組合)。
儲存為 TINYINT,效能接近數值型。PROCEDURE ANALYSE() 會建議轉換。
phpMyAdmin「Propose table structure」功能即基於此。
NULL 需額外位元組標記,MyISAM 每欄位 1 bit 對齊到 byte。比較邏輯更複雜。
mysql_unbuffered_query() -- PHP -- 記憶體省、首行即可處理、但需讀完或 free_result 才能下一查詢
INET_ATON('192.168.1.1') → 3232235777 (插入)
INET_NTOA(3232235777) → '192.168.1.1' (查詢)
PHP: ip2long() / long2ip() (注意有號無號轉換)
偏移量計算 O(1),快取重建快。垂直分割將變長欄位移出主表。
while (1) {
DELETE FROM logs WHERE log_date <= '2012-01-01' LIMIT 1000;
if (mysql_affected_rows() == 0) break;
usleep(50000); -- 50ms 讓出鎖
}
TINYINT < SMALLINT < MEDIUMINT < INT < BIGINT,DATE < DATETIME。預留擴展空間避免 ALTER TABLE 鎖表數小時。
| 引擎 | 優點 | 缺點 | 適用 |
|---|---|---|---|
| MyISAM | 讀快、COUNT(*) 極快、全文索引 | 表鎖、無事務、崩潰易損壞 | 讀多寫少、日誌/報表 |
| InnoDB | 行鎖、事務、外鍵、崩潰恢復 | 寫入較慢、空間較大 | 高併發寫入、金融/核心業務 |
Lazy Loading、批次 SQL 打包事務、Doctrine 推薦。警惕 N+1 查詢問題。
Apache 多進程模型下易耗盡連線數、記憶體、檔案控制代碼。現代架構建議短連線 + 連線池。