一、SQL查询语句是如何执行的?
平时在工作中,都是用框架去执行一条SQL语句,那有没有想过一条SQL在MySQL内部到底经历了哪些流程呢?
首先我们来看一张MySQL的基本架构示意图:
从这张图中可以清楚地看到 SQL 语句在 MySQL的各个功能模块中的执行过程。
缓存模块在MySQL8.0废除
Server 层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖 MySQL 的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。
而存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持 InnoDB、MyISAM、Memory 等多个存储引擎。
现在最常用的存储引擎是 InnoDB,它从 MySQL5.5.5 版本开始成为了默认存储引擎。
其中的细节暂时不用深究,简单了解一下即可,因为面试官一般不会问这些知识点,鸟瞰其全貌,这样能够帮助你从高维度理解问题。
二、SQL更新语句是如何执行的?
先创建一张表:
mysql> create table T(ID int primary key, c int); //表的创建语句
接下来从一个表的一条更新语句看看整个流程是怎么样的:
mysql> update T set c=c+1 where ID=2; //执行一条update语句
(先更新数据还是先写日志?)
更新流程涉及两个重要的日志模块:redo log(重做日志)和 binlog(归档日志)
对数据文件的物理更改,保证总是先写日志,再更新数据,也就是所谓的WAL,即在持久化数据文件前,保证之前的redo log已经写到磁盘。
WAL即Write-Ahead Logging,在对数据页进行修改时, 通过将"修改了什么"这个操作记录在日志中, 而不必马上将更改内容刷新到磁盘上, 从而将随机写转换为顺序写,,提高了性能。【脏页的概念】扫描二维码关注公众号,回复: 11131539 查看本文章
redo log(重做日志)
my.cnf 配置文件中:
innodb_log_file_size = 256M
#InnoDB redo log文件大小,对应于磁盘上ib_logfile*文件。
去到MySQL的data目录下,我们可以看到ib_logfile文件。
Redo log 以顺序的方式写入文件文件,写满时则回溯到第一个文件,进行覆盖写。
redo log包括两部分:一是内存中的日志缓冲(redo log buffer),该部分日志是易失性的;二是磁盘上的重做日志文件(redo log file),该部分日志是持久的。
Redo 写盘操作
参数innodb_flush_log_at_trx_commit 作用于事务提交时,写入磁盘策略:
- 当设置该值为1时,每次事务提交都要做一次fsync,这是最安全的配置,即使宕机也不会丢失事务;
- 当设置为2时,则在事务提交时只做write操作,只保证写到系统的page cache,因此实例crash不会丢失事务,但宕机则可能丢失事务;
- 当设置为0时,事务提交不会触发redo写操作,而是留给后台线程每秒一次的刷盘操作,因此实例crash将最多丢失1秒钟内的事务。
还有其他场景也可能会触发redo log写文件(简单了解,这里又能引出很多知识点):
- Redo log buffer空间不足时
- 后台线程
- 做checkpoint
- 实例shutdown时
- binlog切换时
有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为crash-safe。
binlog(归档日志)
redo log是 InnoDB 引擎特有的日志,而 binlog是Server 层的日志,所有引擎都可以使用。
my.cnf 配置文件中:
log_bin =/usr/local/mysql/data #二进制日志存放路径
binlog_format = row
#binlog格式,复制有3种模式STATEMENT,ROW,MIXED
sync_binlog = 1
#sync_binlog=0(默认),事务提交后MySQL不刷新binlog_cache到磁盘,而让Filesystem自行决定,或者cache满了才同步。
#sync_binlog=n,每进行n次事务提交之后,MySQL将binlog_cache中的数据强制写入磁盘。
磁盘上存储的binlog日志文件:
innodb_flush_log_at_trx_commit =1 和 sync_binlog = 1 双11配置,保证数据库的安全性。
MySQL为什么会有两份日志呢?
因为最开始 MySQL 里并没有 InnoDB 引擎。MySQL 自带的引擎是 MyISAM,但是MyISAM 没有 crash-safe 的能力,binlog 日志只能用于归档。而 InnoDB 是另一个公司以插件形式引入 MySQL 的,既然只依靠 binlog 是没有 crash-safe 能力的,所以InnoDB 使用另外一套日志系统——也就是 redo log 来实现 crash-safe 能力。
因为要写两份日志文件,那必然会存在redo log与binlog的一致性问题;MySQL通过内部XA机制解决这种一致性的问题。将 redo log 的写入拆成了两个步骤:prepare 和 commit,这就是"两阶段提交"。【XA分布式事务】
这里就简单的看看XA的概念:
XA(分布式事务)规范主要定义了(全局)事务管理器(TM: Transaction Manager)和(局部)资源管理器(RM: Resource Manager)之间的接口。XA为了实现分布式事务,将事务的提交分成了两个阶段:也就是2PC (tow phase commit),XA协议就是通过将事务的提交分为两个阶段来实现分布式事务。
看完概念,在通过上面的流程图,应该有个大概的了解2PC的是什么了。
两阶段提交
第一阶段:InnoDB prepare, write/sync redo log;binlog不作任何操作;
第二阶段:包含两步,write/sync Binlog; InnoDB commit (commit in memory)。
当第二阶段的第1步执行完成之后,binlog已经写入,MySQL会认为事务已经提交并持久化了(在这一步binlog就已经ready并且可以发送给订阅者了)。
第二阶段的第2步大部分都是内存操作,比如释放锁,释放MVCC相关的read view等等。
(思考:为什么必须有“两阶段提交”呢?)
undo log(撤消日志)
在数据修改的时候,不仅记录了redo log,同时也记录了undo log。(其他文章介绍了系统表空间(ibdata)、独立的Undo 表空间,其实对于面试来说暂时不用深度研究)
核心点:
undo log的作用:事务回滚 、 多版本控制(MVCC)、 崩溃恢复
下一章来说说undo log的作用
小结
从一个简单的SQL语句,我们仅仅把相关日志部分学习了一下,如果还对每个环节的展开细节存有疑问或者某个地方写的不对,欢迎留言评论和指正。