MVCC

  • 多版本并发控制机制(Multi-Version Concurrency Control
  • MySQL在可重读的隔离级别下有较高的隔离性,同一个事务中,多次查询会得到相同的结果,即使其他事务修改了数据也会是相同的结果
  • 这个隔离性就是依赖MVCC机制实现,对一行数据的读写不会通过加互斥锁,相当于乐观锁实现来保证隔离性
  • 在读已提交和可重读隔离级别下实现了MVCC机制
  • 使用undo日志版本链与read view机制实现MVCC

    undo日志版本链与read view机制详解

  • undo日志版本链:一行数据被多个事务顺序修改后,MySql会保留修改前的数据记录在undo日志里,以方便进行回滚,

  • 使用两个隐藏字段trx_id和roll_pointer把这些undo日志串联起来形成一个历史数据记录链,可以通过查询历史记录查看修改情况而不用影响表中数据
  • 如下图生成的记录修改生成的版本链
  • clipboard.png
  • 可重复读的隔离级别下,事务开启时,执行查询sql时会生成当前事务的一致性视图read-view,在事务结束前视图不会发生变化
  • 读已提交的隔离级别下,每次查询都会生成新的read-view
  • read-view由执行查询时所有未提交事务id数据和已创建的最大事务id组成(就是最开始创建的事务id和最后创建的事务id)
  • 事务里的所有sql查询结果都要从对应版本链里的最新数据开始挨个比对跟read-view找到最终的结果

    版本链比对规则

  • 如上图,如果row的trx_id落在绿色的部分(trx_id < min_id),表示这个版本是已经提交的事务生成的,表示这个数据可见

  • row落在trx_id落在红色部分(trx_id > max_id)表示此版本是由将来启动的事务生成的,是不可见的(如果row的trx_id就是当前自己的事务是可见的)
  • 如果row的trx_id落在黄色部分(min_id <= trx_id <=max_id),就包括两种情况
    • 如果row的trx_id在视图数组中,表示此版本是由还没提交的事务生成的,是不可见的(若row的trx_id就是当前自己的事务id就是可见的);
    • 如果row的trx_id不在视图数组中,表示这个版本是已经提交的事务生成的,是可见的
  • 删除的情况可以认为是update的特殊情况,会将版本链最新的数据复制一份,然后将trx_id修改成删除操作的trx_id,同时在该条记录的头信息(record header)里的(delete_flag)标记位写上true,表示当前记录已被删除,在查询时按照上面规则查到对应的记录

    注意点

  • 注意:begin/start transaction 命令并不是一个事务的起点,在执行到它们之后的第一个修改操作InnoDB表的语句,事务才真正启动,才会向mysql申请事务id,mysql内部是严格按照事务的启动顺序来分配事务id的。

  • MVCC机制的实现就是通过read-view机制与undo版本链比对机制,使得不同的事务会根据数据版本链对比规则读取同一条数据在版本链上的不同版本数据。

    BuffPool缓存机制

  • InnoDB执行引擎对sql执行时会先改缓存再刷磁盘

  • 对磁盘随机读写,性能较低,吞吐量较低
  • 缓存机制可以保证更新请求都是更新buffPool缓存,然后顺序写日志文件,能保证各种异常情况下数据一致性,
  • 更新内存的性能比较高,顺序写磁盘上的日志文件性能也比较高的
  • 再通过日志文件更新磁盘上数据,保证性能

    数据更新过程如下图

  • mysql语句执行过程.png

  • 步骤:
  • sql首先经过分析优化后通过引擎接口开始执行
  • 先将修改数据所在的数据页从磁盘全加载进内存中
  • 将修改数据的旧值写入undolog日志中,事务回滚时查询旧值进行回滚
  • 更新内存中的数据(增删改操作直接修改缓存中数据),buffPool缓存大小一般设置机器内存60%
  • 准备提交事务,redolog写入磁盘,如果事务提交成功,buffpool缓存中数据还没写入磁盘,可以使用redolog日志恢复buffPool里的缓存数据
  • 准备提交事务,binlog写入磁盘,binlog日志主要用来恢复数据库磁盘数据
  • 写入commit提交事务标记到redolog日志文件中,提交事务完成此标记为了保证事务提交后redolog和binlog数据一致
  • 随机写磁盘数据,以数据页为单位更新磁盘数据