網上常見說法:「SQL 前後有空格會導致 Query Cache 未命中,視為不同查詢」。**此說法不嚴謹。**
SET GLOBAL query_cache_type=ON; SET GLOBAL query_cache_size=67108864;SELECT * FROM t WHERE id=1; → Qcache_inserts=1 SELECT * FROM t WHERE id=1; (前導空格) → Qcache_hits=1, inserts 不變結論:前導空格**不影響**命中。
dispatch_command()bool alloc_query(THD *thd, const char *packet, uint packet_length) {
// 1. 移除開頭空白
while (packet_length > 0 && my_isspace(thd->charset(), packet[0])) {
packet++; packet_length--;
}
// 2. 移除結尾 ; 和空白
const char *pos = packet + packet_length;
while (packet_length > 0 && (pos[-1] == ';' || my_isspace(thd->charset(), pos[-1]))) {
pos--; packet_length--;
}
// 3. 複製到查詢緩衝區 (含 db_name、flags)
...
}
關鍵點:alloc_query 在構建查詢緩衝區**前**已完成 TrimLeft/TrimRight,因此前導/尾隨空格、分號均被標準化。
Key = normalized_query_string + current_database + client_flags
normalized_query_string:經 alloc_query 處理後的標準化 SQL。current_database:當前默認庫 (影響同 SQL 不同庫語義)。client_flags:包含 SQL_MODE、字符集等影響執行結果的標誌位。**SQL 中間** 的多餘空格:SELECT * FROM t vs SELECT * FROM t → 視為不同 Key (中間空格未被標準化)。
Qcache_hits / (Qcache_hits + Qcache_inserts) 命中率,低於 20% 考慮關閉。Query Cache 鍵值機制經過標準化處理,**前導/尾隨空格無影響,中間空格有影響**,且 Key 包含庫名與 Flags。理解底層機制,避免盲目優化,並在 8.0 時代平滑遷移至應用層緩存架構。