一、MVCC机制

mysql再可重复读隔离级别下保证了事务的隔离性,同样的sql语句再同一个事务里多次执行查询结果相同,就算其它事务对数据有修改也不会影响当前sql语句的查询结果。这个隔离性就是靠MVCC(Multi-version Concurrency Control)机制来保证的,对一行数据的读和写两个操作默认是不会通过加锁互斥来保证隔离性,避免了频繁加锁互斥,而在串行化隔离级别下为了保证较高的隔离性是通过对所有操作加锁互斥来实现的。
mysql读已提交和可重复读隔离级别下都实现了mvcc机制。
undo日志

作用

解决了读写之间的阻塞问题,通过mvcc可以让读写互相不阻塞,即读不阻塞写,写不阻塞读,提高事务的并发处理能力。
解决了一致性读的问题,当我们查询数据库在某个时间点的快照时,只能看到这个时间点之前事务提交更新的结果,而不能看到这个时间点之后事务提交的更新结果。

MVCC原理

事务版本号
每开启一个事务,我们都会从数据库中获得一个事务ID(也就是事务版本号),这个事务ID是自增长的,通过ID大小,我们就可以判断事务的时间顺序
行记录的隐藏列
行记录中记录这一些重要的隐藏字段(行记录会对应一个叶子节点段)

  • DB_ROW_ID : 隐藏的行 ID,用来生成默认聚簇索引。如果我们创建数据表的时候没有指定聚簇索引,这时 InnoDB 就会用这个隐藏 ID 来创建聚集索引。采用聚簇索引的方式可以提升数据的查找效率。
  • DB_TRX_ID : 操作这个数据的事务ID, 也就是最后一个对该数据进行插入或更新的事务ID
  • DB_ROLL_PTR : 则表示指向该行回滚段的指针,该行上所有旧的版本,在undo中都通过链表的形式组织,而该值,正式指向undo中该行的历史记录链表

叶子节点段
image.png
undo log :InnoDB 将行记录快照保存在了 Undo Log 里
image.png

可重复读MVCC的工作机制

参考 : https://zhuanlan.zhihu.com/p/40208895
当前读
加锁的读select from table for update, update 等这些加排他锁的读是当前读,就是他读取的是记录的最新版本,读取时还要保证其它并发事务不能修改当前记录,会对读取的记录加锁
*快照读

不加锁的读,就是快照读,非阻塞读,不涉及其他锁的冲突

查询(select)
innoDB只查找版本早于当前事务版本的数据行(事务ID 小于 当前的事务ID
行的删除版本要么未定义,要么大于当前事务版本号, 这可以确保事务读取到的行,在事务开始之前未被删除
例子

  1. create table user(
  2. id int primary key auto_increment,
  3. name varchar(20)
  4. );
  5. begin; # 开始一个新的事务, 事务的版本号为1
  6. insert into user values(NULL,'zhangsan');
  7. insert into user values(NULL,'lisi');
  8. commit;

image.png
undo日志版本链与read view机制详解
undo日志版本链是指一行数据被多个事务依次修改过后,mysql会保留修改前的数据在undo回滚日志里,并且用两个隐藏字段trx_id和roll_pointer把这些undo日志串联起来形成一个历史版本链(如下图)
image.png

mysql每张表里有两个隐藏字段 trx_id事务id和roll_pointer回滚指针
trx_id:当前sql的事务id
roll_pointer: 上一个数据版本的回滚点
select是不会真正生成一个事务id的
事务的版本链只有一份,readview不同的事务均有一个

二、BufferPool缓存机制

image.png

一条mysql的执行流程

客户端执行 update t set name = ‘zhuge666’ where id = 1;

  • mysql的客户端与mysql服务端的连接器连接
  • mysql服务端进行语法分析
  • 对mysql进行优化,生成执行计划
  • 交给mysql执行器进行执行
  • 将id = 1的数据所在的页加载到内存
  • 讲id = 1的数据的旧值写入到undo日志中,如果事务执行失败用于回滚操作
  • 修改id = 1的数据的内存值,改为name = ‘zhuge666’
  • 将修改后的数据写入到redo log buffer中
  • 准备提交事务,将redo log日志刷盘(策略可配置)
  • mysql执行器将数据写入到binlog日志中(属于mysql server层),主要用于恢复数据库磁盘中的数据
  • 提交事务成功

注意:
binlog是server层的,所有存储引擎都会写binlog日志
redo日志和undo日志是innodb存储引擎的
undo日志是用来回滚buffer pool的,binlog日志是用来回滚磁盘的
https://mp.weixin.qq.com/s/PfFEyFdnnpphjytDaNl8_Q

redo日志落盘的步骤

  • 事务提交时,会写入log buffer,这里调用的是mysql自己的函数writeRedoLog
  • 只有当mysql发起系统调用写文件write时,log buffer里的数据,才会写到OS cache,注意:mysql系统调用完write之后,就认为文件已经写完,至于什么时候落盘,时操作系统决定的。
  • 操作系统将OS cache里的数据,刷到磁盘上(mysql也可以主动去刷)

优缺点

  • 将每次写优化为批量写,提高性能
  • 可能会造成数据丢失

    事务提交时,将redo log写入log buffer,认为事务提交成功,如果write OS cache之前或者时刷盘之前,系 统崩溃则会造成数据丢失

    刷盘的三种策略

    image.png
    注意:mysql通过innodb_flush_log_at_trx_commit参数设置redo log的刷盘策略
    策略一:innodb_flush_log_at_trx_commit = 0 (性能最佳)
    每隔1秒,将log buffer中的数据批量写到OS Cache中,同时mysql主动刷盘,如果数据库崩溃,有一秒的数据丢失
    策略二:innodb_flush_log_at_trx_commit = 1 (强一致)
    每次事务提交,都将log buffer中的数据write入OS Cache中,同时mysql主动刷盘,将数据写入磁盘,这种策略是innodb默认的策略,为的是保证ACID特性。
    策略三:innodb_flush_log_at_trx_commit = 2
    每次事务提交,都将log buffer中的数据写入os cache中,每隔一秒,mysql做一次刷盘,如果操作系统崩溃,会有1s的数据丢失

问:高并发业务,行业最佳实践,是使用第三种折中配置(=2)
配置为2和配置为0,性能差异并不大,因为将数据从log buffer拷贝到os cache,虽然跨越用户态与内核态,但毕竟是内存数据拷贝,速度很快
配置为2和配置为0,安全性差异巨大,操作系统崩溃的概率相比mysql应用程序崩溃的概率,小的多,设置为2,只要操作系统不崩溃,数据就不会丢失。