MySQL架构
MySQL采用客户端/服务器架构,客户端与MySQL服务器建立TCP连接并通过SQL语句与MySQL服务器交互。MySQL服务器可分为三层架构:
- 连接层:负责建立TCP连接,验证用户名、密码、权限,从线程池分配线程给客户端
- 服务层:对SQL语句做预处理
- 缓存:对SQL语句的结果进行缓存,当SQL语句相同时直接返回结果,但是命中率较低,且数据更新后相关缓存都会失效,故在8.0后已废弃
- 解析器:对SQL语句进行语法语义分析,确保SQL语法正确
- 优化器:确定SQL语句的执行计划,包括选择索引、表连接顺序、子查询优化、加锁策略等,这些也是SQL语句优化的主要方向
- 引擎层:负责MySQL中数据的存储和读取,主要存储引擎包括InnoDB,MyISAM,Memory等
MySQL存储引擎
- InnoDB:MySQL默认存储引擎。支持事务、外键、表级锁和行级锁、主键索引使用的是聚簇索引。InnoDB提供高并发并保证数据完整性,适合高并发场景或复杂事务场景。
- MyISAM:非事务处理存储引擎。不支持事务,行级锁,外键,索引使用的是非聚簇索引,保存表的具体行数。MyISAM是早期使用的存储引擎,适合只读或低并发写入的应用场景。
- Memory:将所有数据存储在内存中,访问速度快,但重启会丢失数据,适合做缓存。
SQL语句执行顺序/流程
以查询语句为例,SQL语句执行顺序为:
- from选择表
- join on连接表
- where条件筛选
- group by分组聚合
- having分组后的条件过滤
- select选择查询的列
- order by对查询结果进行排序
- limit限制返回多少条查询结果
执行顺序决定了where无法使用聚合函数,无法使用select定义的别名。
SQL语句在底层的执行流程为:
- 客户端通过TCP连接发送请求到MySQL服务器,服务器进行权限验证,分配线程
- 先查看缓存,当两条SQL语句完全一样时返回缓存结果
- 再由解析器对SQL语句进行语义分析,若SQL语句正确则生成语法树
- 再由优化器确定SQL语句的执行计划,决定是否使用索引,表之间如何连接等
- 最后由执行器调用存储引擎的API完成具体操作,将结果集返回给客户端
如果是更新语句的话,还要检查是否有排他锁,写undolog,redolog,binlog,刷盘,commit事务
MySQL数据存储形式
MySQL的底层数据结构为B+树(后续文章中介绍),而B+树存储的数据是整个表,表空间由段、区、页、行组成,如下图所示
- 段:表空间由多个段组成,数据段用来存储B+树叶节点的区,索引段用来存储B+树非叶节点的区,回滚段存储回滚数据的区
- 区:区处于段和页中间,段由多个区组成,区是一组连续的页(64页,1M数据)。B+树中的相邻节点都位于一个区,在磁盘中连续,可以使用顺序IO而不是随机IO,加快了范围查询的速度
- 页:页是InnoDB读写数据的基本单位(16KB),页的空间是连续的,B+树的一个节点就是一页,数据库每次读写最少16KB数据(提高读写效率)
- 行:页中即为一行一行的数据,默认行格式为Dynamic。行格式具体分为:额外信息,隐藏字段,真实数据,如下图所示。
- 记录的额外信息
- 变长字段长度列表:存储如varchar类型的数据长度。(非必须)
- NULL值列表:真实数据中不会存NULL值,NULL值统一存在NULL值列表中,每个列对应一个二进制位,对应位为1时表示对应列的值为NULL。(非必须)
- 记录头信息:包括删除字段(标记该条数据是否被删除),下一条记录的位置等
- 隐藏字段
- row_id:若建表时指定了主键或唯一约束,则没有该隐藏字段,否则添加row_id,因为当没有指定主键和唯一约束时InnoDB会使用row_id作为聚簇索引
- trx_id:表示该数据是由哪个事务生成的,用于实现MVCC
- roll_pointer:记录该数据上一个版本(undolog中)的指针,用于实现MVCC
其他问题:
- varchar(n)中的n最大取值为多少
- 首先一行占用的字节数不能超过65535,存储实际的字符数要看使用的字符集,还要受行格式中的额外信息的影响
- 行溢出后MySQL怎么处理
- MySQL中磁盘与内存交互的单位是页,当一条记录大到一页存不下时就会发生行溢出,多出的数据会存到溢出页中(当前页保存溢出页地址)
- IP地址如何存储
- 可以使用varchar(15)存储,因为IP地址最少7个字符(0.0.0.1),最多15个字符(225.225.225.225),但是更高效的方式是使用无符号整数存储,因为IP地址是32位无符号整数,直接使用unsigned int存储只需要4字节,并且MySQL提供了转义函数INET_ATON,这样可以节省存储空间,便于使用范围查询。
- 图片如何存储
- 一般不直接存储图片文件,而是使用varchar存储图片文件的路径
- emoji表情如何存储
- 每个表情对应一个unicode编码,存储编码即可,但是要使用uft8-mb4编码
- 长文章如何存储
- 使用LongTEXT类型存储,最多可以存储4GB数据,文章太大时也可以将其存储到外部文件系统中,MySQL保存文件路径
- 金额如何存储
- 使用decimal类型,避免浮点数的精度损失问题(float底层使用二进制表示)