Mysql之二阶段从扫盲到详解
预备知识:
1、Mysql内存组件---Buffer Pool:
字面意思是缓冲池,实际上,MySQL增删改查的操作都是在Buffer Pool中执行的。但我们知道,数据是存储于磁盘当中的,但如果MySQL的操作都在磁盘中进行,就会出现频繁的IO事件,效率极低。所以MySQL的操作都是在内存中进行,即Buffer Pool这个组件。(可以类比到内存数据库Redis)
2、数据页
逻辑上来讲,我们操作的数据是以表+行的方式,但MySQL不会去操作行数据,而是抽象出一个数据页的概念,从磁盘文件拿出一行一行的数据,存放到数据页中。在MySQL执行增删改查之前会先寻找到该数据对应的数据页,然后将其加载到Buffer Pool中。
3、事务
定义:数据库的事务是指一组sql语句组成的数据库逻辑处理单元,从开始到结束的整个处理过程。支持事务的存储引擎InnoDB(以下讲解均以InnoDB为例,因为它是MySQL默认的存储引擎);不支持事务的存储引擎MyISAM、MEMORY。
特性ACID:
原子性A:事务是最小的工作单元,可以类比为物理学中的量子,不可进行分割。所以,事务的执行要么全部成功要么全部失败,因为其不可再分,所以不存在前几步成功,第几步失败的说法,一毁尽毁!一旦出现问题则回滚到事务开始之前的状态,就当没发生过一样。实现事务的原子性,是基于Redo/Undo机制。
一致性C:数据库总是从一个状态转换到另一个状态。指事务的完整性约束,一旦违反约定就会自动回滚。
隔离性I:一个事务的操作不会影响到另一个事务。其实,给MySQL的增删改查加上事务后,我们先输入命令begin开启事务,然后输入SQL语句,回车后并不会立即执行,直到我们输入了commit命令,之前输入的SQL语句才会生效。也就是说,再commit之前,即便你回车了SQL语句(你以为已经执行了,实则并没有),在另一终端打开查看MySQL数据库,数据表以及字段会发现和之前一模一样。
持久性D:一旦事务提交(commit)后,所作的修改会永久的保存在数据库中。即便系统崩溃,修改的数据也不会丢失。
原子性、隔离性与持久性都是为了保障一致性而存在,一致性也是最终目的。
4、脏页与脏读:
脏页:内存数据页与磁盘数据页内容不一致时,称内存页为脏页;内存页的数据写入磁盘后,数据一致了就变为干净页。平时的更新操作都是基于内存与日志的,并不会及时同步到磁盘,因此就产生了脏页。
脏读:在事务A修改了某一数据时,此时事务B正好读取了该数据,但后面事务A因某种原因回滚,还原了修改的数据,此时事务B读取到的数据就称为脏数据。
5、SQL语句执行流程(以SQL更新语句为例:update company set name='高启强' where id=1,目前company表中id为1的数据,name字段为安欣)
Step1:MySQL会先去存储引擎查询id为1的数据,找到id为1的数据所在的数据页并返回行数据,如果不存在则去磁盘中加载;
Step2:针对Step1返回的行数据,将name修改为'高启强',但此时还未将结果写入Buffer Pool中;
Step3:将id为1的旧数据的name字段(安欣)写入undo log中,以便后续的回滚;
Step4:将id为1的新数据写入Buffer Pool中;
Step5:InnoDB写redo log,并标记为Prepare;然后执行器写binlog;InnoDB写redo log并标记为commit。
6、三种日志:redo、undo、binlog
redo:InnoDB特有的物理逻辑日志,名叫重做日志,记录了哪个数据页上进行了更改,用于实现事务的持久化存储。分为两部分:内存中的重做日志、硬盘上的重做日志。
当数据页完成修改之后,将其写入redo日志。InnoDB执行WAL(日志先行:日志一定比数据先写回磁盘),在一个事务提交时,并不是直接对内存中的脏数据进行落盘(落盘:将数据保存于磁盘),先把重做日志缓冲区中的日志写入文件,然后提交成功,这样就保证了可以依靠redo进行数据的恢复与重做。(只要是提交的事务在redo中都会有记录)。
undo:主要用于事务回滚时恢复原来的数据,是逻辑记录,记录了行的操作内容。需要注意的是,undo的正确性由redo保证,在数据库恢复时,先恢复redo,再恢复undo。
binlog:称为归档日志,是一种只记录对表中数据以及对表结构产生更改操作的二进制文件,比如有insert、update、delete、create table、alter table等操作,不记录select、show,因为这些操作不会产生任何更改。不过就算一个update未产生数据变化,也是会被记录进去的。可以理解为binlog是直接记录sql语句,因此binlog属于逻辑日志。以追加模式写入的,一个文件写满,会重新创建一个文件继续写,文件名称是mysql-bin.xxxxxx。默认不开启
二阶段提交:
预备知识介绍完后,二阶段提交就很好描述了,在SQL语句执行流程中可以看到Step5,InnoDB在写redo log时,并不是一次性写完的,而是有两个阶段,分别是Prepare与Commit阶段,这就是所谓的二阶段提交。当update修改语句时,就会生成一条初始状态的redo log,当提交事务时,该redo log变为prepare状态,等待binlog写入成功后,redo log就切换为commit状态。
在进行数据恢复时,对于commit状态的redo log进行重放恢复,对于prepare状态的redo log,去查找binlog,恢复binlog,初始状态的redo log则直接跳过。
举例:
假设某时刻数据库崩溃,在崩溃之前有事务A和事务B在同时执行,此时事务A已经提交,而事务B未提交。当数据库重启进行数据恢复时,就会通过redo log 将已经提交的事务更改写到数据文件,而还没有提交的事务就根据undo log进行roll back。