一条SQL更新语句的执行
1,update T set c=c+1 where ID=2;
1)更新语句的流程跟查询语句一样,先连接到数据库;
2)当表上有更新时,跟表相关的查询缓存会失效,将表相关的缓存结果清空;
3)分析器分析语法和语法确定是更新语句;
4)优化器决定使用哪个索引;
5)执行器执行语句,找到目的行更新。
2,与查询流程不一样的是,更新流程还涉及两个重要的日志模块,redo log(重做日志)和binlog(归档日志)。
1)MySQL的每一次更新操作都需要写进磁盘,磁盘也需要先找到对应记录再更新,整个过程IO成本、查找成本都很高,所以MySQL使用WAL(Write-Ahead Logging)技术,先写日志,再写磁盘。
2)当有记录需要更新时,InnoDB引擎先把记录写到redo log,并更新内存,同时InnoDB引擎会在合适时候将redo log里的记录更新到磁盘,比如系统空闲时间。
日志模块redo log和binlog
1,InnoDB的redo log是固定大小,从头开始写,写到末尾再回到开头循环写,write pos记录当前写的位置,checkpoint记录擦除位置。
1)write pos与checkpoint之间部分可以记录新的操作,当write pos追上checkpoint时,不可以执行新的更新,需要先把擦除位置的记录更新到数据文件,执行擦除操作后再记录新的操作。
2)有了redo log,InnoDB可以保证数据库异常重启后操作记录不会丢失,称为crash-safe。
2,redo log是InnoDB引擎特有的日志,binlog则是Server层日志,所有引擎都可以使用。
1)redo log是物理日志,记录的是数据页上的修改,binlog是逻辑日志,记录的是语句的原始逻辑;
2)redo log是循环写,binlog是追加写,不会覆盖以前的日志。
3,binlog会记录所有的逻辑操作,且采用追加写的形式,如果系统定期做整库备份,那么数据库可以恢复到备份周期任意一刻的状态。
1)先将数据库恢复到指定时刻之前的备份状态;
2)再从备份时间点开始到需要恢复的时刻,取binlog里内容,按取出的binlog操作数据即能恢复到指定时刻。
两阶段提交
1,update T set c=c+1 where ID=2;
1)执行器通过引擎取ID=2的数据,如果ID=2所在的数据页在内存中,直接返回给执行器,如果不在则需要先从磁盘读到内存再返回。
2)执行器取到引擎返回的数据+1,再调用引擎接口写入更新后的数据。
3)引擎将新数据更新到内存,同时将更新操作记录到redo log,redo log处理prepare状态,告知执行器执行完成,事务可提交。
4)执行器生成binlog,并将binlog写入磁盘。
5)执行器调用引擎提交事务接口,引擎将redo log修改为commit状态,更新完成。
2,最后三步将redo log写入拆分为prepare和commit的两阶段提交。redo log和binlog是两个独立的逻辑,如果不用两阶段提交:
1)先写redo log后写binlog,假设在redo log写完,binlog还没有写完时,数据库异常重启,binlog未记录当前操作,之后在备份及恢复的过程中缺少此次操作,恢复出来的数据与原库不同。
2)先写binlog后写redo log,binlog写完之后crash,redo log未写,崩溃恢复后事务无效,但binlog已经记录了此次操作,在之后的备份与恢复中多了一项事务,恢复出来的数据与原库不同。
3,使用两阶段提交,根据日志恢复出来的库状态与原库一致。
4,不止在误操作需要用备份与binlog恢复数据,在需要扩容搭建备库的时候,常见的方法也是全量备份+应用binlog实现,如果操作不一致会导致主从数据库不一致的情况。
5,一天一备份对比一周一备份,最长恢复时间较短。但频繁全量备份需要更多的存储空间,所以备份时间的选择需要根据业务评估决定。
1)最好情况是应用一天的binlog恢复数据到某一刻;
2)最坏情况需要应用一周的binlog恢复数据到某一刻。