MySQL 會緩存完全相同的 SELECT 結果。**含非確定性函數(NOW()、RAND()、CURDATE())的查詢不會被緩存**。
-- 不走緩存
SELECT * FROM user WHERE signup_date >= CURDATE();
-- 走緩存:應用層計算日期字串
$today = date('Y-m-d');
$stmt = $pdo->prepare("SELECT * FROM user WHERE signup_date >= ?");
$stmt->execute([$today]);
EXPLAIN SELECT ... 顯示 type(連接類型)、key(實際索引)、rows(預估掃描行)、Extra(Using filesort/Using temporary = 慢)。
案例:忘記在 group_id 建索引導致 type=ALL,加上索引後變 ref,rows 從幾萬降至個位數。
LIMIT 1-- 慢:掃描全表/索引
SELECT * FROM user WHERE state = 'Alabama';
-- 快:找到第一筆即停止
SELECT 1 FROM user WHERE state = 'Alabama' LIMIT 1;
不僅主鍵/唯一鍵,WHERE、JOIN、ORDER BY、GROUP BY 涉及的欄位都應評估建索引。
JOIN ON a.col = b.col 時,a.col、b.col 必須同型別(INT vs INT)、同字元集(utf8 vs utf8)、同長度,否則隱性轉換導致索引失效、全表掃描。
ORDER BY RAND()每行計算隨機數、排序、再取一行,複雜度 O(N log N)。
替代方案:
$count = $pdo->query("SELECT COUNT(*) FROM user")->fetchColumn();
$offset = mt_rand(0, $count - 1);
$pdo->query("SELECT username FROM user LIMIT $offset, 1");
SELECT *Using index)避免回表。SELECT * FROM table_name PROCEDURE ANALYSE();
依現有資料分布建議最優型別(如 ENUM 替代 VARCHAR、INT UNSIGNED 等)。
$stmt = $mysqli->prepare("SELECT username FROM user WHERE state = ?");
$stmt->bind_param("s", $state);
$stmt->execute();
$stmt->bind_result($username);
$stmt->fetch();
UNSIGNED INTVARCHAR(15) 佔 15+ 位元組、字串比較慢。
INT UNSIGNED 固定 4 位元組、數值比較極快。
-- 寫入
UPDATE users SET ip = INET_ATON('192.168.1.1') WHERE id = 1;
-- 讀取
SELECT INET_NTOA(ip) FROM users WHERE id = 1;