介绍MySQL的架构,每一个关键组件的作用及工作方式简单介绍
MySQL Server
Client:应用程序通常使用JDBC驱动,与MYSQL-Server交互;向MySQL服务器发送要执行的命令及SQL;
连接器:通过TCP连接池管理客户端链接、并处理认证、鉴权等操作;
查询缓存:8.0之后被移除;由于表更新则缓存失效,Server层的缓存就很鸡肋;
分析器:对SQL字符串进行词法分析、语法分析、生成AST抽象语法树;
- 词法分析:标记SQL关键词、非关键词;
- 语法分析:对SQL进行正确性验证,解析为语法树;
预处理器:缓存SQL模板,后续相同的SQL,使用相同的模板,只需提交参数即可;
优化器:对将执行的SQL进行分析、优化,最终生成执行计划(EXPLAIN),决定了最终的执行策略(索引选择、表的JOIN顺序等); MySQL中主要有两种优化器:
- RBO:基于规则的优化器;
- CBO:基于代价的优化器(主流),根据IO成本、CPU成本,优化SQL;
优化器有时会判定全表扫描比索引更快(
结果集/总数据>30
): 索引执行需要按页读取数据,全表扫描则是以块读取,并可以顺序读; 当通过索引查询的数据量比较大,可能IO的代价比全表扫描更大,优化器可能选择全表扫描;
因此创建索引,一定要有一定的区分度(选择性)才能一定程度上防止索引失效;
执行器:操作存储引擎,调用存储引擎的API,执行SQL,获取执行结果,响应客户端等;
存储引擎:真正执行SQL、存储数据的组件;不同的存储引擎使用不同的存储、读写策略;存储引擎对外暴漏Handler API,进行交互;
- MyISAM:使用B树组织数据,只有哈希索引,不可范围查询、不支持事务;
- InnoDB:使用B+树组织数据,支持多种索引,支持事务;
Binary Log(bin_log):记录数据的变更,有2种记录模式:
- binlog_format = statement:保留完整SQL语句;
- binlog_format = row:记录数据修改前后的状态;
MyISAM
使用B+树作为索引数据结构,只不过叶子节点保存数据记录的地址,而非数据记录;
- 支持哈希索引、全文索引;
- 不支持事务和行级锁定,只有表锁
- 读操作性能非常高效;适合读多场景;索引支持范围查询,但可以扫表来范围查询;
InnoDB存储引擎
Buffer Pool:
- 大小由:innodb_buffer_pool_size决定;
- 个数由:可以有多个,提升并发性能;由innodb_buffer_pool_instances决定,各实例间隔离;
- 内存操作单元为:对应InnoDB页(16KB + 页描述信息)
- LRU缓存:双向链表缓存热点数据;
- 使用LRU算法(近期最少使用)进行内存管理;当内存不足时,淘汰不常使用的页,将其刷入磁盘;
- 缓存命中率:由于LRU机制,尽量不要查询不需要的列,挤占内存,导致LRU缓存命中率降低;
- Free链:即空闲缓存页,InnoDB需要从磁盘加载新的页时,会从Free链中取空白页,装载数据;
- Flush链:脏页链,存放被修改的脏页,放入链表尾部,由后台线程顺序刷入磁盘中;刷入完成后,将空页加入Free链;
Redo Log:InnoDB使用redo_log的顺序磁盘IO来加速持久化;
- InnoDB的数据最终存储在磁盘,如果直接写入磁盘,就会以随机IO的方式写入;
- 因此Innodb的数据写入BufferPool中时,就算是写入成功了;
- 但是宕机会丢失,因此使用redolog来以顺序IO的方式最快的进行持久化;保证宕机仍能够从redolog中恢复数据; Undo Log:用于事务回滚和MVCC数据版本;会定期清理不再需要的版本数据;(永远不需要读到的版本)
MySQL实例和库
一个MySQL实例可以包含多个逻辑库;
- 库与库之间的表、试图、存储过程、事务都相互隔离;
- 多个库共享MySQL实例的配置、CPU、内存资源、连接池、InnoDB缓存池、磁盘IO、物理存储;
- 多个库之间的锁资源是隔离的(如表锁),不会存在表锁竞争;
单节点多库的限制:
- 单节点多库:并不会提高并发度,多个库共享连接池、内存资源并不会提高并发度;
- 多库共享InnoDB缓存池;如果某个库有热点查询,其他库的缓存可能被清除;
什么场景需要单节点多库:
- 租户的数据隔离;
- 单表数据量过大的分库分表,提升SQL执行效率;
- 环境的隔离:开发环境、测试环境;