MySQL MyISAM引擎是什么?有什么致命缺陷?為何現在都不使用了?
上一篇《MySQL:MyISAM 鎖表致千萬損失!穿越工程師如何逆天改命》,我發現自己穿越到了 過去,這個年代的 MySQL 居然還在用 MyISAM……次日上午,技術部緊急會議
"林工,你說要換引擎就換?"
首席 DBA 老張拍案而起,"這系統跑了三年都沒事,你才來三天就搞事情?"
林淵默然調出昨晚的監控數據:
# 昨夜事故報告
Lock_time_avg: 12.7s # 表鎖平均等待時間
Table_locks_immediate=2345
Table_locks_waited=8765 # 鎖等待率高達78%
"各位請看,"林淵點擊投影,"這不是故障,而是架構級癌癥。"
連接池危機
詭異現象:
SHOW PROCESSLIST;
+-----+------+-----------+------+---------+------+-------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+-----+------+-----------+------+---------+------+-------+------------------+
| 101 | app | 10.0.0.5 | prod | Sleep | 632 | | NULL |
| 102 | app | 10.0.0.5 | prod | Sleep | 587 | | NULL |
| 103 | app | 10.0.0.5 | prod | Sleep | 524 | | NULL |
(超過 300 個僵尸連接,消耗 1GB 內存)
技術解析:
圖片
- 線程泄漏原理: MySQL 4.0 采用"每連接每線程"模型,線程執行完不會銷毀而是進入
thread_cache
。 - 但當
wait_timeout
設置過大時(默認 8 小時),大量空閑線程堆積。
林淵的解法:
// 修改mysqld.cc的線程管理邏輯
void handle_one_connection(THD *thd) {
while (!abort_loop) {
if (thd->net.vio->read_packet() == 0) { // 無數據時主動釋放
thread_scheduler.end_thread(thd, true);
break;
}
do_command(thd);
}
}
操作結果: 內存占用從 3.2GB 降至 1.8GB,QPS 提升 40%。
SQL 執行過程
驚魂時刻:當林淵試圖優化慢查詢時,系統突然報錯:
ERROR 1064 (42000): You have an error in your SQL syntax...
——用戶輸入的SELECT * FORM orders
竟然未被攔截!
解剖流程:
圖片
關鍵發現:
- 查詢緩存陷阱:
query_cache_type=ON
導致頻繁緩存失效(命中率僅 12%) - 解析器漏洞:未啟用嚴格模式(
sql_mode
未設置)允許錯誤語法通過 - 優化器缺陷:缺乏直方圖統計,錯誤選擇全表掃描
林淵的急救包:
SET GLOBAL query_cache_size=0; -- 關閉毒藥級查詢緩存
SET GLOBAL sql_mode='STRICT_TRANS_TABLES'; -- 啟用嚴格模式
ANALYZE TABLE orders; -- 手動更新統計信息
變更存儲引擎
驚險時刻:當林淵嘗試在線更換存儲引擎時
ALTER TABLE orders ENGINE=InnoDB;
系統突然僵死!SHOW PROCESSLIST
顯示:
| 145 | system user | NULL | NULL | alter table | 89 | copy to tmp table |
引擎切換原理:
圖片
林淵的破局操作:
- 使用
pt-online-schema-change
工具在線變更(提前 20 年發明) - 分階段遷移數據:
# 步驟1:創建影子表
CREATE TABLE _orders_new LIKE orders ENGINE=InnoDB;
# 步驟2:分批拷貝(每次10萬條)
INSERT INTO _orders_new SELECT * FROM orders WHERE id BETWEEN ? AND ?;
# 步驟3:原子切換(0.01秒鎖定)
RENAME TABLE orders TO _orders_old, _orders_new TO orders;
引擎插件的秘密
林淵在ha_myisam.cc
中發現關鍵結構:
struct st_mysql_storage_engine myisam_storage_engine = {
"MyISAM",
"MySQL AB",
"Default engine with fast read speed",
{ /* 函數指針表 */
myisam_create_handler,
myisam_hton_commit,
NULL // 事務相關為空
}
};
"原來 MyISAM 的事務支持是先天殘疾..."他若有所思。
下節預告:
"當我啟動 InnoDB 引擎時,服務器內存突然耗盡..." —— 林淵如何用 Buffer Pool 優化化解內存危機?
且看下一章節《InnoDB 架構設計:行級鎖原理、預寫日志(WAL)、Change Buffer》!