MySQL的内部组件结构
MySQL 可以分为 Server 层和存储引擎层两部分。
Server 层主要包括连接器、查询缓存、分析器、优化器、执行器
等,涵盖 MySQL 的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。
存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持 InnoDB
、MyISAM
、Memory
等多个存储引擎。现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5.5 版本开始成为了默认存储引擎。也就是说如果我们在create table时不指定表的存储引擎类型,默认会给你设置存储引擎为InnoDB。
连接器
我们知道由于MySQL是开源的,他有非常多种类的客户端:navicat、mysql front、jdbc、SQLyog
等非常丰富的客户端,包括各种编程语言实现的客户端连接程序,这些客户端要向mysql发起通信都必须先跟Server端建立通信连接,而建立连接的工作就是有连接器完成的。
连接器负责跟客户端建立连接、获取权限、维持和管理连接。
mysql -h host[数据库地址] -u root[用户] -p root[密码] -P 3306
查询缓存
MySQL 拿到一个查询请求后,会先到查询缓存看看,之前是不是执行过这条语句。之前执行过的语句及其结果可能会以 key-value 对的形式,被直接缓存在内存中。key 是查询的语句,value 是查询的结果。如果你的查询能够直接在这个缓存中找到 key,那么这个 value 就会被直接返回给客户端。
查询缓存默认是关闭的,mysql 8.0已经移除了查询缓存功能
#查看当前mysql实例是否开启缓存机制
show global variables like "%query_cache_type%";#配置my.cnf
#query_cache_type有3个值 0代表关闭查询缓存OFF,1代表开启ON,2(DEMAND)代表当sql语句中有SQL_CACHE关键词时才缓存
query_cache_type=2
分析器
分析器先会做“词法分析”。你输入的是由多个字符串和空格组成的一条 SQL 语句,MySQL 需要识别出里面的字符串分别是什么,代表什么。MySQL 从你输入的"select"这个关键字识别出来,这是一个查询语句。它也要把字符串“T”识别成“表名 T”,把字符串“ID”识别成“列 ID”。做完了这些识别以后,就要做“语法分析”。根据词法分析的结果,语法分析器会根据语法规则,判断你输入的这个 SQL 语句是否满足 MySQL 语法。
优化器
优化器是在表里面有多个索引的时候,决定使用哪个索引;或者在一个语句有多表关联(join)的时候,决定各个表的连接顺序;以及一些mysql自己内部的优化机制。
执行器
开始执行的时候,要先判断一下你对这个表 T 有没有执行查询的权限,如果没有,就会返回没有权限的错误,如下所示 (在工程实现上,如果命中查询缓存,会在查询缓存返回结果的时候,做权限验证)。如果有权限,就打开表继续执行。打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。
MySQL日志
MySQL主要包括重做日志(redo log)、回滚日志(undo log)、归档日志(binlog)、错误日志(errorlog)、慢查询日志(slow query log)、一般查询日志(general log)、中继日志(relay log)。重点讲述重做日志(redo log)、回滚日志(undo log)和归档日志(binlog)。
redo log重做日志
如果每次更新操作都需要写磁盘,然后磁盘也要找到对应的那条记录,然后再更新,整个过程 IO 成本、查找成本都很高。为了解决这个问题,MySQL 的设计者就用了WAL
技术来提升更新效率。
WAL
的全称是 Write-Ahead Logging
,它的关键点就是先写日志,再写磁盘
具体来说,当有一条update语句要执行的时候,InnoDB 引擎就会先把记录写到 redo log
里面,并更新内存,这个时候更新就算完成了。同时,InnoDB引擎会在适当的时候,将这个操作记录更新到磁盘里面,而这个更新往往是在系统比较空闲的时候做。InnoDB 的 redo log
是固定大小的。
redo log 是物理日志,记录的是在某个数据页上做了什么修改。
有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe
crash-safe
就是落盘处理,将数据存储到了磁盘上,断电重启也不会丢失。
redo log 写入磁盘过程分析
redo log 从头开始写,写完一个文件继续写另一个文件,写到最后一个文件末尾就又回到第一个文件开头循环写,如下面这个图所示。
write pos
是当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头。
checkpoint
是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件里。
write pos
和 checkpoint
之间的部分就是空着的可写部分,可以用来记录新的操作。如果 write pos
追上checkpoint
,表示redo log
写满了,这时候不能再执行新的更新,得停下来先擦掉一些记录,把 checkpoint
推进一下。
binlog二进制归档日志
binlog二进制日志记录保存了所有执行过的修改操作语句,不保存查询操作。如果 MySQL 服务意外停止,可通过二进制日志文件排查,用户操作或表结构操作,从而来恢复数据库数据。
启动binlog记录功能,会影响服务器性能,但如果需要恢复数据或主从复制功能,则好处则大于对服务器的影响。
binlog 是逻辑日志,记录的是这个语句的原始逻辑。
# 查看binlog相关参数
show variables like '%log_bin%';
MySQL5.7 版本中,binlog默认是关闭的,8.0版本默认是打开的。上图中log_bin的值是OFF就代表binlog是关闭状态,打开binlog功能,需要修改配置文件my.ini(windows)或my.cnf(linux),然后重启数据库。
在配置文件中的[mysqld]部分增加如下配置:
# log-bin设置binlog的存放位置,可以是绝对路径,也可以是相对路径,这里写的相对路径,则binlog文件默认会放在data数据目录下
log-bin=mysql-binlog
# Server Id是数据库服务器id,随便写一个数都可以,这个id用来在mysql集群环境中标记唯一mysql服务器,集群环境中每台mysql服务器的id不能一样,不加启动会报错
server-id=1
# 其他配置
binlog_format = row # 日志文件格式,下面会详细解释
expire_logs_days = 15 # 执行自动删除距离当前15天以前的binlog日志文件的天数, 默认为0, 表示不自动删除
max_binlog_size = 200M # 单个binlog日志文件的大小限制,默认为 1GB
binlog 的日志格式
用参数 binlog_format 可以设置binlog日志的记录格式,mysql支持三种格式类型:
- STATEMENT: 基于
SQL
语句的复制,每一条会修改数据的sql
都会记录到master
机器的bin-log
中,这种方式日志量小,节约IO开销,提高性能,但是对于一些执行过程中才能确定结果的函数,比如UUID()
、SYSDATE()
等函数如果随sql
同步到slave
机器去执行,则结果跟master
机器执行的不一样。 - ROW: 基于行的复制,日志中会记录成每一行数据被修改的形式,然后在
slave
端再对相同的数据进行修改记录下每一行数据修改的细节,可以解决函数、存储过程等在slave
机器的复制问题,但这种方式日志量较大,性能不如Statement
。举个例子,假设update
语句更新10行数据,Statement
方式就记录这条update
语句,Row方式会记录被修改的10行数据。 - MIXED: 混合模式复制,实际就是前两种模式的结合,在
Mixed
模式下,MySQL
会根据执行的每一条具体的sql
语句来区分对待记录的日志形式,也就是在Statement
和Row
之间选择一种,如果sql
里有函数或一些在执行时才知道结果的情况,会选择Row
,其它情况选择Statement
,推荐使用这一种。
binlog写入磁盘机制
binlog
写入磁盘机制主要通过 sync_binlog
参数控制,默认值是 0。
- 为0的时候,表示每次提交事务都只
write
到page cache
,由系统自行判断什么时候执行fsync
写入磁盘。虽然性能得到提升,但是机器宕机,page cache
里面的binlog
会丢失。 - 也可以设置为1,表示每次提交事务都会执行
fsync
写入磁盘,这种方式最安全。 - 还有一种折中方式,可以设置为
N(N>1)
,表示每次提交事务都write
到page cache
,但累积N个事务后才fsync
写入磁盘,这种如果机器宕机会丢失N个事务的binlog。
undo log回滚日志
InnoDB对undo log文件的管理采用段的方式,也就是回滚段(rollback segment) 。每个回滚段记录了 1024 个 undo log segment ,每个事务只会使用一个undo log segment。
在MySQL5.5的时候,只有一个回滚段,那么最大同时支持的事务数量为1024个。在MySQL 5.6开始,InnoDB支持最大128个回滚段,故其支持同时在线的事务限制提高到了 128*1024 。
innodb_undo_directory:设置undo log文件所在的路径。该参数的默认值为"./",即innodb数据文件存储位置,目录下ibdata1文件就是undo log存储的位置。
innodb_undo_logs: 设置undo log文件内部回滚段的个数,默认值为128。
innodb_undo_tablespaces: 设置undo log文件的数量,这样回滚段可以较为平均地分布在多个文件中。设置该参数后,会在路径innodb_undo_directory看到undo为前缀的文件。
undo log日志什么时候删除
新增类型的,在事务提交之后就可以清除掉了。
修改类型的,事务提交之后不能立即清除掉,这些日志会用于mvcc。只有当没有事务用到该版本信息时才可以清除。