学习MySQL

49 阅读8分钟

三大日志

执行一条select语句要经过的步骤

  1. 连接器
    • 与客户端进行TCP三次握手进行连接
  2. 查询缓存
    • MySQL8.0以后就将这部分给删掉了
  3. 解析SQL
    • 词法分析(分析出Token和KeyWard(select和from等))
    • 语法分析根据语法规则判断出MySQL语句如果没问题会构建出SQL语法树,如果语法不对会报错(from写成form)
    • 但是表不存在或字段不存在不是在这检测出的
  4. 执行SQL
    • 预处理
      • 在这个阶段会检测出表不存在或者字段不存在等错误
    • 优化器
      • 如果查询时可以选择多个索引,那优化器会基于成本考虑去选择一个合适的执行方案
    • 执行器
      • 按照优化器选择的执行方案去执行SQL语句,从存储引擎读取记录返回给客户端

image.png

执行一条update语句

执行update的操作也会发生执行select的操作,但是新增了

  1. 去buffer Pool读取数据
    • 如果读取到,直接返回个执行器
    • 没读到,去磁盘中读入数据到buffer Pool中
  2. 比对更新前后的数据
    • 如果相同那就不进行更新流程
    • 不同的话那就把更新前的数据和更新后的数据都传给InnoDB层,让InnoDB层执行更新
  3. 开启事务,首先记录相应的undo log,把被更新的列的旧值记录下来,生成undo log语句,写入buffer pool的undo界面,后续的undo log更改都需要记录到redo log buffer里
  4. 开启更新,先更新内存(标记为脏页),再把记录写到redo log buffer里,这个时候更新就完成了。后续会选择一个合适的时机进行脏页刷盘,刷盘时会先刷redo log buffer里的数据再刷脏页的数据。
  5. 一条更新语句执行完后,此时记录的binlog会保存到binlog cache里并没有刷新到硬盘上的binlog文件里,在事务提交时才会将所有的binlog刷新到硬盘。
  6. 事务提交
    • prepare:将redo log状态设为prepare,并将redo log刷新到硬盘里(这个redo log写入磁盘的是数据的变更情况)
    • commit:将binlog刷新到磁盘,接着将redo log状态设置为commit(这个redo log写入磁盘的是事务的状态标记prepare -> commit)
  • undo log:InnoDB存储引擎层的逻辑日志,保证了事务的原子性,用于事务回滚和MVCC
  • redo log:InnoDB存储引擎层的物理日志,保证了事务的持久性,用于掉电等故障的数据恢复
  • bin log:Server层的逻辑日志,用于主从复制和数据备份

逻辑日志和物理日志的区别

  • 逻辑日志:描述的逻辑操作,记录的逻辑语句,恢复的时候需要重放逻辑。结构紧凑。
  • 物理日志:记录的数据的变化例如 对 XXX 表空间中的 YYY 数据页 ZZZ 偏移量的地方做了AAA 更新,占用的空间较大,但是数据恢复的速度快

Redo log

包含两部分一部分是InnoDB存储引擎层的redo log buffer另一部分是磁盘里的redo log file

  • 让事务有了持久性,让MySQL有了崩溃恢复的能力能够保证MySQL在任何时间崩溃,都能够恢复已提交的数据
  • WAL实现了让随机写变成了顺序写(写入redo log是顺序写),提高了IO的效率
    • 具体实现:写操作不立刻写到磁盘上,而是先写到日志上,然后再在合适的时间写到磁盘上
  • undo log页面被修改后要把修改列的旧值写入buffer Pool里的undo页面,同时也要把相关的语句写入redo log里进行持久化存储

undo log

  • 让事务有了原子性
  • 能够实现MVCC

bin log

  • 数据恢复和主从复制

redo log 和 undo log 的使用场景

  • 事务提交之前发生崩溃(不是宕机崩溃而是事务出现问题)重启后会根据undo log回滚事务。
  • 事务提交之后发生崩溃(宕机崩溃)重启后根据redo log恢复事务,不会造成数据丢失。

redo log和bin log的使用场景

  • redo log文件是循环写,边写边擦,只记录未被刷入磁盘的物理地址,已经刷入磁盘的数据会从redo log中擦除。redo log是存储引擎层的日志能够知道脏页刷没刷盘,从而在崩溃恢复的时候恢复那些没被刷盘的脏页数据。
  • binlog 保存的全量的日志,保存了所有数据的变更,只要记录在binlog上的数据都可以恢复,binlog是server层的日志不能知道哪写脏页还没有刷盘。

binlog 两阶段提交过程(为了redo log和binlog的日志一致性,开启了内部XA事务)

  • prepare阶段:将XID写入redo log里,redo log刷入磁盘
  • commit阶段:将XID写入binlog里,binlog刷入磁盘,将redo log状态设置为commit

image.png

两个日志出现数据不一致的节点

  • 当MySQL重启的时候会按顺序扫描redo log找到XID后会拿着XID去binlog里去找
    • redo log刷入了磁盘,而还没有将XID写入binlog里。
      • 这个时候redo log里有XID完成了刷盘而binlog里没有XID,发生了数据不一致,进行回滚事务。
    • binlog刷入磁盘,binlog的状态还没设置为commit。
      • 这个时候redo log里有XID完成刷盘并且binlog里也有XID完成刷盘,进行事务提交。
    • 所以对于处于prepare状态的redo log来说既可以进行提交事务也可以进行回滚事务,主要取决于binlog里有没有XID,也就是binlog的写成功为事务成功的标识。

学习MVCC

MVCC是为了当多个事务读写数据库时能够保持数据的一致性和隔离性,它是通过维护多个版本的数据来实现的,当事务对数据进行修改时,MVCC会创建个数据快照,而不是直接修改数据行。RC是每个select语句都会生成一个新的Read View,而RR是只会在第一个select执行时生成Read View。RC下,MVCC可以解决脏读。RR隔离等级下,MVCC解决了不可重复读和部分幻读。

一致性非锁定读:快照读 -> 读取数据行的历史数据

  • 普通的select语句

锁定读(对读取的记录加锁):当前读 -> 读取数据行的最新数据

  • select ... lock in share mode
  • select ... for update
  • update,delete,insert都属于当前读

RR隔离级别下如果执行的是快照读,那用MVCC就能避免幻读(select以后生成一个Read View并且后面的读取都用这个),而如果是当前读,那MVCC就解决不了,要用(记录锁+间隙锁)临键锁(Next-Key Lock)来锁定一个范围内的数据,防止其他事务在间隙间插入数据。

  • 不可重复读:一个事务前后的查询中发现数据进行了修改减少
  • 幻读:一个事务前后的查询中发现数据进行了新增
  • 幻读可以看作是一种特殊的不可重复读,那为什么要单拎出来呢,因为对于这两种情况的处理手段不同,对于deleteupdate操作可以直接对记录加锁保证事务安全,而对于insert操作需要单加记录锁只能锁住已存在的记录,需要额外用到间隙锁住一个范围,这样也就能控制一个范围不允许插入新值了。

但是RR下的幻读还是没有完全得到解决

  • T1时刻:A事务执行快照读,读出四条数据
  • T2时刻:B事务插入一条数据
  • T3时刻:A事务执行当前读,那就会发生幻读,读出四条数据

MVCC的三个关键知识点:隐藏字段,Read View,undo log

MVCC通过隐藏字段的DB_TRX_ID与Read View来判断数据的可见性,如果不可见那就通过数据行的DB_ROLL_PTR去找到可见的历史版本,每个事务读到的数据版本可能不一致,在同一个事务中用户只能看到这个事务创建Read View之前已经提交的修改和该事务本身做的修改。

隐藏字段

  • DB_TRX_ID:更新当前数据行的事务ID
  • DB_ROLL_PTR:指向当前数据行的undo log的历史版本
  • DB_ROW_ID:如果没有主键,那就使用此id作为主键

ReadView

  • m_low_limit_id:目前出现过的最大的事务ID+1,大于等于这个的都不可见
  • m_up_limit_id:活跃事务列表中最小的事务ID,小于这个的都可见
  • m_ids:创建Read View时其他未提交的活跃事务的ID列表,不包括当前事务自己和已提交的事务
  • m_creator_trx_id:创建该Read View的事务ID

undo log

  • 作用:
    • 用于数据的回滚恢复
    • 当读取记录时,如果该记录被其他事务占用或当前版本对该事务不可见,那就可以通过undo log读取历史版本
  • 分类:
    • insert undo log
    • update undo log

RC下的MVCC在进行快照读时的提取数据的过程

image.png

RR下的MVCC在进行快照读时的提取数据的过程

image.png

MVCC + 锁 = 隔离性