ARTICLE · 1046352
从源码看MySQL [4]|Handler接口:SQL层和InnoDB之间那道"翻译层"
从源码看MySQL [4]|Handler接口:SQL层和InnoDB之间那道"翻译层"
C++ C++
SQL SQL
SQL
SQL
从源码看MySQL [4]|Handler接口:SQL层和InnoDB之间那道"翻译层"点击下方按钮复制正文,然后粘贴到微信公众号编辑器
复制正文
从源码看MySQL [4]|Handler接口:SQL层和InnoDB之间那道"翻译层"
本系列:《高性能MySQL第三版》源码级伴读
实验环境:MySQL 8.0.46-debug
本文是 Phase 1 第 4 篇:存储引擎抽象层
一、场景:为什么 MySQL 能有那么多存储引擎?
❓ 场景:同事问"我们要不要用 MyISAM?听说查询快一点。"
回答这个问题前,我们得先理解:MySQL 怎么让不同的存储引擎"插拔"?
二、书上怎么说?——《高性能MySQL》关于存储引擎架构
《高性能MySQL》第1章明确指出:
「MySQL 采用插件式存储引擎架构,最上层是 SQL 层,下层是各种存储引擎。」
书里也指出:
- InnoDB 是 5.5 之后的默认引擎
- MyISAM 不支持事务和外键
- Memory 引擎数据存在内存中
但书没讲:
❌ Handler 接口是怎么设计的 ❌ 怎么"看"到引擎调用次数
三、源码:Handler 接口的真相
MySQL 用 C++ 虚函数实现"接口抽象":
"color: #6a737d; font-style: italic;">// sql/handler.h (简化) class handler { public: "color: #6a737d; font-style: italic;">// 核心虚函数 virtualint open(constchar *name, int mode, uint test_if_locked) = 0; virtualint close(void) = 0; virtualint write_row(uchar *buf) = 0; "color: #6a737d; font-style: italic;">// INSERT virtualint update_row(const uchar *old, uchar *new) = 0; "color: #6a737d; font-style: italic;">// UPDATE virtualint delete_row(const uchar *buf) = 0; "color: #6a737d; font-style: italic;">// DELETE virtualint index_read(uchar *buf, const uchar *key, uint key_len, enum ha_rkey_function find_flag) = 0; "color: #6a737d; font-style: italic;">// SELECT virtualint index_next(uchar *buf) = 0; "color: #6a737d; font-style: italic;">// ... 还有几十个 };
InnoDB 的实现(storage/innobase/handler/ha_innodb.cc):
class ha_innobase : public handler { public: int index_read(uchar *buf, ...) override { "color: #6a737d; font-style: italic;">// 调用 InnoDB 自己的 row_search_mvcc() return row_search_mvcc(...); } };
设计哲学:
SQL 层只跟 handler虚函数打交道不同引擎实现 handler子类就行- 完全符合"开闭原则"
——加新引擎不动 SQL 层
四、实测:看你的实例支持哪些引擎
SHOW ENGINES\G
输出(节选):
Engine: InnoDB Support: DEFAULT Comment: Supports transactions, row-level locking, and foreign keys Transactions: YES XA: YES Savepoints: YES Engine: MyISAM Support: YES Transactions: NO ← 不支持事务 XA: NO Engine: MEMORY Support: YES Transactions: NO ← 不支持事务 Comment: Hash based, stored in memory, useful for temporary tables Engine: PERFORMANCE_SCHEMA Support: YES Transactions: NO ← 不是真"数据库"Handler 调用计数:
SHOWSTATUSLIKE'Handler_%';
Handler_read_key | WHERE id = ? | |
Handler_read_next | ||
Handler_read_rnd_next | SELECT * FROM t | |
Handler_update | ||
Handler_write |
运维用法:
-- 看 SQL 走没走索引SELECT * FROM orders WHERE id = 1; -- Handler_read_key +1SELECT * FROM orders; -- Handler_read_rnd_next +N
五、各引擎能力对比(基于代码)
| 通用/默认 |
结论:绝大多数情况用 InnoDB。MyISAM 只用于"只读 + 不在乎崩溃" 的极小场景。
六、Handler 计数实战:识别慢查询
-- 重置计数器(仅当前session) FLUSH STATUS; -- 跑你的 SQLSELECT * FROM orders WHERE user_id = 1001; SELECT * FROM orders WHERE id IN (1,2,3); -- 看计数器(MySQL 8.0支持WHERE子句)SHOWSESSIONSTATUSWHERE Variable_name LIKE'Handler_%'AND Value > 0;
输出解读:
Handler_read_key > 0:走了索引(好事) Handler_read_rnd_next = N:扫描了 N 行(小心,可能是全表扫描) 比例 Handler_read_rnd_next / Handler_read_key > 100:基本确定是慢查询
七、本篇对应解决的运维问题
| 用 InnoDB | |
Handler_read_* 计数 + EXPLAIN | |
SHOW TABLE STATUS LIKE 'table_name' |
八、关键参数
default_storage_engine | ||
internal_tmp_disk_storage_engine |
九、本篇作业 & 下期预告
作业
执行 SHOW ENGINES\G看你的实例支持哪些跑一条 SELECT * FROM orders,看Handler_read_rnd_next增加了多少试试建一个 MyISAM 表: CREATE TABLE test_myisam (id INT) ENGINE=MyISAM;(如果支持)
下期预告
Phase 1 第 5 篇|InnoDB 页结构:数据在磁盘上长什么样——Handler 说"读一行",InnoDB 要去一个 16KB 的页里找这行。页是 InnoDB 数据的最小存储单位,结构到底是什么样的?
如果觉得有帮助,请点赞、在看、转发三连。Handler 接口是 SQL 层和存储引擎的"桥梁"——下一站,我们看看这桥通向什么样的数据。
⚖️ 版权声明:
本文为原创技术分析文章,非官方授权翻译或转载 对《高性能MySQL第三版》的引用仅限简短概括,用于评论和教学,符合《著作权法》第24条合理使用 MySQL 源码引用遵循 GPL v2 开源协议,用于教育目的的代码片段分析不构成"分发" "MySQL"为 Oracle Corporation 注册商标,本文使用仅为技术描述 建议购买原书深入学习:《高性能MySQL》第三版,电子工业出版社 未经作者许可,禁止转载或用于商业用途
已复制到剪贴板!去微信后台粘贴吧