目录
一、MySQL的架构设计
首先当我们通过数据库连接发送给MySQL一条sql语句时,MySQL会怎么操作呢?
1.1 SQL接口
他是一套执行SQL语句的接口,专门用于执行我们发送给MySQL的那些增删改查的SQL语句,当MySQL接收到我们发送的SQL语句时,会交给SQL接口去执行这条语句。
1.2 查询解析器
那么问题来了,SQL接口是怎么执行SQL语句的呢?
首先比如我们发送一条语句是这样的:
select id, name, age from user where id = 1;
我们能看得懂这条SQL的意思,但是MySQL要是想看懂,就需要查询解析器了。
所谓的SQL解析,就是按照既定的SQL语法,对我们按照SQL语法规则编写的SQL语句进行解析,然后理解这个SQL语句要干什么事情。
1.3 查询优化器
当我们通过解析器理解了SQL语句要干什么之后,接着会找查询优化器(Optimizer)来选择一个最优的查询路径。
就是你这条SQL怎么执行最好、最快、效率最高是由查询优化器决定的,我们都知道SQL语句加索引能优化查询,但你以为加了索引就一定更好吗?有的时候你加了索引并查询,MySQL的优化器依然没有用索引,而是通过全表扫描,因为优化器认为,有些时候全表扫描比你加了索引查询还要好。
1.4 存储引擎执行SQL语句
最后按照优化器给的最优路径,存储引擎执行SQL语句。
那么问题又来了,既然叫做存储引擎,那么我们update的数据,或者select的数据,都存在哪啊?无非就是内存或者硬盘,那到底是存在内存还是硬盘呢?接着听我分析下一章节。
二、InnoDB存储引擎的架构设计
MySQL最常用的就是InnoDB存储引擎,那么我们今天借助一条更新语句的执行,来初步的了解一
下InnoDB存储引擎的架构设计。
update users set name='xxx' where id=10;
那么这条语句是怎么执行的呢?我们接着上一章节的架构图来补充。
2.1 缓冲池(Buffer Pool)
InnoDB存储引擎中有一个非常重要的放在内存里的组件,就是缓冲池(Buffer Pool),这里面会缓存很多的数据,以便于以后在查询的时候,万一你要是内存缓冲池里有数据,就可以不用去查磁盘了。
引擎要执行更新语句的时候 ,比如对“id=10”这一行数据,他其实会先将“id=10”这一行数据看看是否在缓冲池里,如果不在的话,那么会直接从磁盘里加载到缓冲池里来。
2.2 undo日志
学过数据库的都知道事务,我们增删改的时候必须最后要提交事务,才能真正的修改数据,如果事务过程中出现了问题,我们可以进行回滚,将数据恢复原来的样子,undo日志就是用来干这个的。
假如“id=10”这一行数据缓冲池没有,那么就从磁盘上获取,并加载到缓冲池中,并且把旧值写入undo日志文件里,防止出现问题可以及时回滚。当把旧值写入undo日志文件中时,更新缓冲池里的数据,这时候属于“脏数据”,因为缓冲池是新数据,但磁盘却是旧数据。
架构图如下:
2.3 redo日志(Redo Log Buffer)
那么按上面的架构图来说,第三步更新缓冲池数据完毕,这时要把对内存所做的修改写入到一个Redo Log Buffer里去,这也是内存里的一个缓冲区,是用来存放redo日志的。
所谓的redo日志,就是记录下来你对数据做了什么修改,比如对“id=10这行记录修改了name字段的值为xxx”,这就是一个日志。
那么问题来了,这时MySQL宕机了,内存数据全部丢失,该怎么办?
其实不用慌,内存数据虽然丢失了,但你磁盘上真实数据还是旧的数据,你顶多这次操作不成功,数据不会有什么影响的。
2.4 提交事务的时候将redo日志写入磁盘中
接着我们想要提交一个事务了,此时就会根据一定的策略把redo日志从redo log buffer里刷入到磁盘文件里去。
此时这个策略是通过innodb_flush_log_at_trx_commit来配置的,他有几个选项。
等于0时
当这个参数的值为0的时候,那么你提交事务的时候,不会把redo log buffer里的数据刷入磁盘文件的,此时可能你都提交事务了,结果mysql宕机了,然后此时内存里的数据全部丢失。相当于你提交事务成功了,但是由于MySQL突然宕机,导致内存中的数据和redo日志都丢失了。
等于1时
当这个参数的值为1的时候,你提交事务的时候,就必须把redo log从内存刷入到磁盘文件里去,那么只要提交事务成功之后,redo日志一定在磁盘文件里,就算这个时候宕机,等重启MySQL后,自动读取磁盘里的redo日志,恢复之前做的修改。
注意:这时只是redo写入到redo日志磁盘文件中,并没有直接修改磁盘里的数据。
等于2时
提交事务的时候,把redo日志写入磁盘文件对应的os cache缓存里去,而不是直接进入磁盘文件,可能1秒后才会把os cache里的数据写入到磁盘文件里去。
这种模式下,你提交事务之后,redo log可能仅仅停留在os cache内存缓存里,没实际进入磁盘文件,万一此时你要是机器宕机了,那么os cache里的redo log就会丢失,同样会让你感觉提交事务了,结果数据丢了。
三、binlog日志
实际上我们之前说的redo log,他是一种偏向物理性质的重做日志,因为他里面记录的是类似这样的东西,“对哪个数据页中的什么记录,做了个什么修改”。
而且redo log本身是属于InnoDB存储引擎特有的一个东西。
而binlog叫做归档日志,他里面记录的是偏向于逻辑性的日志,类似于“对users表中的id=10的一行数据做了更新操作,更新以后的值是什么”。
binlog不是InnoDB存储引擎特有的日志文件,是属于mysql server自己的日志文件。
所以其实我们上一讲讲到,在我们提交事务的时候,会把redo log日志写入磁盘文件中去。然后其实在提交事务的时候,我们同时还会把这次更新对应的binlog日志写入到磁盘文件中去。
3.1 binlog刷盘策略
sync_binlog等与0时
他的默认值是0,此时你把binlog写入磁盘的时候,其实不是直接进入磁盘文件,而是进入os cache内存缓存。
所以跟之前分析的一样,如果此时机器宕机,那么你在os cache里的binlog日志是会丢失的。
sync_binlog等与1时
如果要是把sync_binlog参数设置为1的话,那么此时会强制在提交事务的时候,把binlog直接写入到磁盘文件里去,那么这样提交事务之后,哪怕机器宕机,磁盘上的binlog是不会丢失的。
3.2 基于binlog和redo log完成事务的提交
当我们把binlog写入磁盘文件之后,接着就会完成最终的事务提交,此时会把本次更新对应的binlog文件名称和这次更新的binlog日志在文件里的位置,都写入到redo log日志文件里去,同时在redo log日志文件里写入一个commit标记。在完成这个事情之后,才算最终完成了事务的提交
那么标记的意义是什么?
说白了,他其实是用来保持redo log日志与binlog日志一致的。
比如第五步完成后宕机了,那么事务就算没有完成,就会回滚。同理第六步完成后宕机了,一样没有在redo log日志中写入commit标记,一样算没有完成,同样会回滚。
3.3 后台IO线程随机将内存更新后的脏数据刷回磁盘
如果所有步骤都完成了,redo log日志中也写入了commit标记,那么就等MySQL后台的IO线程,会在之后某个时间里,随机的把内存buffer pool中的修改后的脏数据给刷回到磁盘上的数据文件里去。
最终版架构图: