MySQL整体概述
1、MySQL架构图
MySQL逻辑架构图(林晓斌MYSQL45讲)
2、select语句的执行过程
3、insert语句的执行过程
如上图所示,insert 产生的insert undo log比较特殊,它只对本事务起作用,而且当事务提交后,就会被删除。
4、update语句的执行过程
5、delete语句的执行过程
MySQL删除其实是个“假删除”
MySQL删除记录并不会立马从磁盘删除,只是将该记录打上删除标记(delete mark),并加入垃圾链中。等到后台purge线程执行回收时,但是只是将此处空间标记为可使用,并不会真正释放磁盘空间;后面再有数据插入的时候,就可以复用这块空间,而不用再额外分配新的磁盘空间。
总结:MySQL删除,只是标记删除,等到purge来将空间标记为可用;所以delete操作是不会释放表空间,换言之,delete之后,磁盘该占用多大空间还是多大,如果想要释放表空间,需要执行表空间重整,alter table 表名 engine = innodb;需要注意的是,该操作比较耗性能,慎用;
页合并
如果两个相邻数据页的“数据空洞”比较多,换言之,页的使用率较低,MySQL会触发页合并操作,顾名思义就是将其中一个页的数据搬迁到另一个页上,另一个页整体置为可使用;(与页分裂刚好相反)
6、崩溃恢复的执行过程
MySQL crash recovery的大致流程如下:
graph LR
A[找到last_checkpoint_no] --> B[若有需要则修复损坏数据页]
B --> C[读取redo-log]
C --> D[利用redo log binlog undo log修复数据]
- 找到last_checkpoint_no
从redo log中获取上次checkpoint的lsn值,确定本次需要数据恢复lsn的起点值
redo log文件的前四个block示意图
checkpoint_no为奇数时,写入checkpoint1;checkpoint为偶数时,写入checkpoint2; 设计两个checkpoint的原因,也可以提高可用性,即使某一次checkpoin_no写入失败,仍然还存在另一个,只是数据恢复时,多读一次而已,并不会导致数据丢失;
- 若有需要则修复损坏数据页
MySQL刷盘时,都是以页数据为最小单位来处理。例如某一次刷盘,将内存中某一页的数据做落盘处理时,刚好写了一半到磁盘中,此时MySQL崩溃了,那么这时候磁盘中这一页的数据是有损的,不可用。此时做数据恢复时,就需要修复受损的页数据。
MySQL的应对策略:double write。就是在将内存脏页刷盘之前,先将脏页数据写到内存中的double write buffer,然后再写入磁盘中.dblwr文件,之后再将数据写入对应表的磁盘空间。
如果写入.dblwr文件的过程中,MySQL崩溃了,此时磁盘表空间的数据页仍然时完整的,未被损坏,数据恢复时没有必要修复页数据;
如果写入.dblwr文件之后,刷盘过程中,MySQL崩溃时,此时磁盘页数据写了一半,页数据损坏。数据恢复时,只需要加载.dblwr文件也就能修复对应损坏的页数据。.dblwr文件也是顺序写,性能要比往具体表刷盘的随机写,快很多;个人觉得某种意义上讲也是WAL思想的一种体现。
- 读取redo log
从第一步所取得last_checkpoint_no起点逐步读取redo log记录,为了提高效率,MySQL并非按顺序逐条处理,而是利用hash表,将同一页的数据置于一处,后面一起处理,减少随机写磁盘的次数。hash表结构如下:
- 利用redo log、undo log、bin log恢复数据 如果redo log完整且处于prepare状态,那么拿着事务ID(XID)去binlog中查找是否存在对应事务,如果能够找到,那么说明该事务已经被完整执行完,将redo-log commit,完成事务提交;如果在bin log中没有找到对应事务,那么依据undo log执行回滚操作。 如果redo log不完整,则依据undo log做回滚操作
以update语句为例,sql执行过程如下图所示,MySQL可能发生奔溃的大致可以分为以下四个时刻
- 时刻A(刚在内存中修改完数据,还没开始写redo log,此时MySQL崩溃)
此时脏页刚生成,还没开始刷盘,且redo log、binlog也没有开始写,所以此时崩溃不会影响MySQL数据的一致性 - 时刻B(写完redo log prepare状态(可能落盘也可能没落盘),尚未写binlog,此时MySQL崩溃)
- 时刻C(写完bin log,尚未commit,此时MySQL崩溃)
- 时刻D(redo log已经变更为commit状态,尚未返回给客户端结果,此时MySQL崩溃)
上诉时刻B、C、D,恢复流程都相似。
先检查redo log日志是否完整,如果不完整,则根据undo log做回滚操作;
如果完整,看redo log是否为prepare状态,如果是,则看下bin log中对应XID是否存在,存在则继续讲redo log commit,将数据页变化刷到对应数据页去;如果不存在,则依据undo log执行回滚。如果redo log 已经是commit状态,则将数据页变化刷到对应数据页去。
redo log刷数据页,大致过程: 从数据页对应的头信息中取出lsn, 如果比redo log小,则刷盘;如果比redo log大则说明这段redo log已经被刷过盘了,丢弃。
7、主从同步
主从同步的大致流程如上图所示
中继日志的作用:从机从主服务拉取日志之后,先行写入一个中间日志,也就是中继日志。然后由从机的SQL线程异步的将中继日志的内容在写入磁盘中。这么做的目的,主要是写磁盘时,会发生随机IO,这个过程比较慢,所以MySQL同步主机的binlog先顺序写落中间日志,然后再慢慢异步写入库中,提高性能。
7.1、常用的几种主从同步架构
- 一主一从/多从
生产中用的都是这种架构,适用于读多写少的场景,利用从库减少读的压力
- 双主
- 级联复制
目前碰到的做数据库扩容或者迁移,好像采用的就是这种方案。用来解决从机多同步主机,从而因主从同步而拉高主机压力,改为从从机同步数据,减轻主机压力。
7.2、主从复制方式
- 全同步复制
- 异步复制
- 半同步复制
MySQL主要使用这种复制方式,相比于异步复制,主库需要等收到从库写中继日志成功的回执后再提交事务,这样就可以确保从库已经收到同步数据。