update T set c=c+1 where ID=2;
其实一条更新语句的执行操作和查询语句的执行操作基本相同->一条SQL查询语句是如何查询的?,唯一不同的是一条更新语句在执行过程中需要涉及到两个日志操作(redo log、binlog)。步骤如下:
连接器:建立一个链接
分析器:词法优化、语法优化
优化器:给出该条SQL语句执行方案
执行器:如下
首先我们思考一个问题,那就是当我们每执行一条更新操作,MySQL是否都会将更新的数据立即写入磁盘?答案是:否
因为如果每一次的更新操作都需要写进磁盘,然后磁盘也要找到对应的那条记录,然后再更新,整个过程 IO 成本、查找成本都很高。因此为了解决这个问题,MySQL设计者采用了WAL技术。具体来说就是当有一条记录需要更新时,InnoDB引擎就会先把这条记录写入到redo log中,并且更新内存,此时更新就算完成了。同时,InnoDB引擎会在合适的时候将记录写到磁盘中,而这个操作往往是在系统比较空闲的时候去做。
但是可能会存在一些问题,要是系统一直不空闲怎么办?redo log日志的容量是多大?
redo log日志容量是固定的,比如可以配置为一组 4 个文件,每个文件的大小是 1GB,那么这块日志总共就可以记录 4GB 的操作。当日志写不下了,那就会将最开始的那块日志更新到数据文件,同时对这块日志进行擦除,进而腾出空间继续写入。
有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe。
MySQL 整体来看,其实就有两块:一块是 Server 层,它主要做的是 MySQL 功能层面的事情;还有一块是引擎层,负责存储相关的具体事宜。上面我们聊到的 redo log (重做日志)是 InnoDB 引擎特有的日志,而 Server 层也有自己的日志,称为 binlog(归档日志)。
这两种日志有以下三点不同。
- redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用。
- redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”。
- redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
为什么需要两阶段提交呢?这是为了让两份日志之间的逻辑一致。
当写完redo log日志后,binlog 还没有写完的时候,MySQL 进程异常重启。这个时候我们仍然可以通过redo log日志将数据恢复,这样看起来并没有什么问题。但是我们可以想一下,当我们之后备份数据的时候,备份的是bin log日志,如果需要用这个 binlog 来恢复临时库的话,由于这个语句的 binlog 丢失,这个临时库就会少了这一次更新,数据就会与原值不一致。
如果在 binlog 写完之后 crash,由于 redo log 还没写,崩溃恢复以后这个事务无效。但是 binlog 里面已经记录了该条修改日志。所以,在之后用 binlog 来恢复的时候就多了一个事务出来,与原库的数据不一致。
Bin log 用于记录了完整的逻辑记录,所有的逻辑记录在 bin log 里都能找到,所以在备份恢复时,是以 bin log 为基础,通过其记录的完整逻辑操作,备份出一个和原库完整的数据。
redo log用于崩溃恢复,当MySQL崩溃重启以后,能够通过redo log将数据正确恢复到崩溃前的值。