MVCC多版本控制

我们回顾一下之前所讲解的事务,那么mysql是如何解决事务的原子性和一致性呢?(加锁,保证事务执行时不被干扰,在一个原子单位内的操作,必须保证这个原子操作后的数据前后一致。)
首先我们要明确一个事情,MVVC是什么,是为了解决什么?

我们基本都清楚数据库的锁机制可以控制并发,为了保护安全对于读写操作一般都会加锁,就是读的时候不能写,写的时候不能读.这样虽然对于数据库的数据来说很安全,但是也从性能上导致了消耗,为了解决这种现象推出了MVVC多版本控制
追其深意,也就是说MVCC是为了保存数据在某个时间点的快照来实现的,不同存储引擎的MVCC的实现是不同的.

为了解决这种加锁导致mysql性能降低并且难以进行同时读写操作,mvvc版本控制脱颖而出

为了更详细的讲解mvvc版本控制,我们需要先去了解活跃在mvvc中的两个定义.
innodb事务日志包括redo log和undo log.

redo log

这个日志是重做日志,主要是提供前滚操作 ,并且记录数据修改后的记录
redo log通常是物理日志,记录的是数据页的物理修改,而不是某一行或某几行修改成怎样,他用来恢复提交后的物理数据页(恢复数据页,且只能恢复到最后一次提交的位置)

在网上查看攻略时看到了redo log 和二进制日志的区别,咱们一起看看~

首先要声明的是 redo log并不是二进制日志,虽然二进制日志中也记录了innodb表的很多操作,也能实现重做的功能,但是两者之间还是有很多区别.

  1. 二进制日志是在存储引擎上层产生,无论是什么类型的引擎,对数据库进行了修改那么都会产生一个二进制日志,但是针对于redo log来说 他只是innodb层产生的,只记录了该引擎中表的修改,并且二进制日志先于redo log被记录
  2. 二进制日志记录操作的方法是逻辑性的语句,即便他是基于行格式的记录方式,其本质也还是逻辑的sql设置,如该记录的每列的值是多少,而redo log是在物理格式上的日志,他记录的是数据每页的修改
  3. 二进制日志只在每次事务提交的时候一次性写入缓存中的日志文件,但是redo log在数据准备修改前写入缓存的redo log中,然后才对缓存中的数据执行修改操作,而且保证在发出事务提交指令时,先向缓存中的redo log写入日志,写入完成后才执行提交动作
  4. 因为二进制日志只在提交的时候一次性写入,所以二进制日志中的记录方式和提交顺序有关,且一次性提交对应一次记录,而redo log中是记录的物理页的修改,redo log文件中同一个事物可能多次记录,最后一个提交的事务记录会覆盖所有未提交的事务记录,比如事务T1,可能在redo中记录了
T1-1,T1-2,T1-3,T1*

四个操作,其中T1*标示最后提交时的日志记录,所以对应的数据页的最终状态是T1 *对应的操作结果,而且redo log是并发写入的,不同事务之间的不同版本的记录会穿插写入到redo log文件中,力图可能redo log的记录方式如下 :

T1-1,T1-2,T2-1,T2-2,T2*,T1-3,T1*

5.事务日志记录的是物理页的情况,它具有幂等性,因此记录日志的方式及其简练,幂等性的意思是多次操作前后的状态是一样的,例如新插入一行后又删除该行,前后状态没有变化,而二进制日志记录的是所有影响数据的操作,记录的内容较多,例如插入一行记录,删除该行又记录了一次.

究其含义

当我们对数据库进行操作的时候,比如修改一些数据,此时需要把数据页信息从磁盘读取到buffer pool中,在buffer pool中进行修改时,此时的buffer pool中的数据页就与磁盘上的数据页内容不一致,称buffer pool的数据页为dirty page脏数据,如果此时发生非正常的db服务重启,那么这些数据还在内存,并没有同步到磁盘文件中(注意此时同步到磁盘文件是一个随机io),也就是会发生数据丢失,如果这个时候,能够在有一个文件,当buffer pool中的data page变更结束后,把相应的修改记录记录到这个文件(注意此时的记录是顺序io),那么当db服务发生crash的情况,恢复db的时候,用于记录数据修改后的记录,顺序记录.
他可以带来下面这些好处:

  • 当buffer pool中的dirty page 还没有刷新到磁盘的时候,发生crash,启动服务后,可以通过redo log找到需要重新刷新到磁盘文件的记录;
  • buffer pool 中的数据直接flush到disk file是一个随机io,效率交叉,而把buffer pool中的数据记录到redo log,是一个顺序io,可以提高事务提交的速度;
    假设修改student表中的sid=2的行数据,把sname='钱电’修改为sname=‘小哲’,那么redo 日志就会用来存放sname='小哲’的记录,如果这个修改在flush到磁盘文件时出现异常,可以使用redo log实现重做操作,保证事务的持久性.
    在这里插入图片描述
innodb_log_files_in_group

redo log 文件的个数,命名方式如:ib_logfile0,iblogfile1… iblogfilen。默认2个,最大100个。

innodb_log_file_size

文件设置大小,默认值为 48M,最大值为512G,注意最大值指的是整个 redo log系列文件之和,即(innodb_log_files_in_group * innodb_log_file_size )不能大于最大值512G。

innodb_log_group_home_dir

文件存放路径

innodb_log_buffer_size

Redo Log 缓存区,默认8M,可设置1-8M。延迟事务日志写入磁盘,把redo log 放到该缓冲区,然后根据 innodb_flush_log_at_trx_commit参数的设置,再把日志从buffer 中flush 到磁盘中。

innodb_flush_log_at_trx_commit

innodb_flush_log_at_trx_commit=1,每次commit都会把redo log从redo log buffer写入到system,并fsync刷新到磁盘文件中。
innodb_flush_log_at_trx_commit=2,每次事务提交时MySQL会把日志从redo log buffer写入到system,但只写入到file system buffer,由系统内部来fsync到磁盘文件。如果数据库实例crash,不会丢失redo log,但是如果服务器crash,由于file system buffer还来不及fsync到磁盘文件,所以会丢失这一部分的数据。
innodb_flush_log_at_trx_commit=0,事务发生过程,日志一直激励在redo log buffer中,跟其他设置一样,但是在事务提交时,不产生redo 写操作,而是MySQL内部每秒操作一次,从redo log buffer,把数据写入到系统中去。如果发生crash,即丢失1s内的事务修改操作。
注意:由于进程调度策略问题,这个“每秒执行一次 flush(刷到磁盘)操作”并不是保证100%的“每秒”。

undo log

这个日志是回滚日志,提供回滚操作
undo log用来进行回滚记录到某个版本,undo log一般也就是逻辑日志,根据每行记录进行记录
通俗的来讲 undo log用于存放数据被修改之前的值,假设修改student表中的sid=2的行数据,把sname=‘钱电’ 改为 sname=‘小哲’, 那么此时undo log就会用来存放sname='小哲’的记录,如果这个修改出现异常,可以使用undo log日志进行回滚操作,保证事务的一致性.
在这里插入图片描述
对数据的变更操作,主要是来自insert update delete,而undo log 中分为两种类型,一种是insert_undo(insert操作),记录插入的唯一键值;一种是update_undo(包含update及delete操作),记录修改的唯一键值以及old column记录

undo参数
mysql> show global variables like '%undo%';
+--------------------------+------------+
| Variable_name            | Value      |
+--------------------------+------------+
| innodb_max_undo_log_size | 1073741824 |
| innodb_undo_directory    | ./         |
| innodb_undo_log_truncate | OFF        |
| innodb_undo_logs         | 128        |
| innodb_undo_tablespaces  | 3          |
+--------------------------+------------+
 
mysql> show global variables like '%truncate%';
+--------------------------------------+-------+
| Variable_name                        | Value |
+--------------------------------------+-------+
| innodb_purge_rseg_truncate_frequency | 128   |
| innodb_undo_log_truncate             | OFF   |
+--------------------------------------+-------+
innodb_max_undo_log_size

控制最大undo tablespace文件的大小,当启动了innodb_undo_log_truncate时,undo tablespace 超过innodb_max_undo_log_size阀值时才会尝试truncate,该值默认大小为1g,truncate后的大小默认为10m.

innodb_undo_tablespaces

设置undo独立表空间个数,范围为0-128, 默认为0,0表示不开启独立undo表空间 且 undo日志存储在ibdata文件中。该参数只能在最开始初始化MySQL实例的时候指定,如果实例已创建,这个参数是不能变动的,如果在数据库配置文 件 .cnf 中指定innodb_undo_tablespaces 的个数大于实例创建时的指定个数,则会启动失败,提示该参数设置有误。

innodb_undo_log_truncate

InnoDB的purge线程,根据innodb_undo_log_truncate设置开启或关闭、innodb_max_undo_log_size的参数值,以及truncate的频率来进行空间回收和 undo file 的重新初始化。

innodb_purge_rseg_truncate_frequency

用于控制purge回滚段的频度,默认为128。假设设置为n,则说明,当Innodb Purge操作的协调线程 purge事务128次时,就会触发一次History purge,检查当前的undo log 表空间状态是否会触发truncate。

换而言之我们回归主题mvcc多版本控制

多版本并发控制只针对innodb的repeatable read可重复读和read committed读已提交两种隔离级别,多版本并发控制的原理就是在每个记录行后面增加两个标示列来存储该行的状态,分别存储改行的新系统版本号和删除系统版本号,系统的版本号会随着每增加一个事务递增.
MVCC的全称是"多版本并发控制",这项技术使得innodb的事务隔离级别下执行一致性读操作有了保证,换言之,就是为了查询一些正在被另一个事务更新的行,并且可以看到他们被更新之前的值,这是一个可以用来增强并发性的强大技术,解决了mysql并发性能问题,这样一来查询就不用等待另一个事务释放锁,这项技术在数据库并不是普遍使用的,一些其他的数据库产品以及mysql其他的存储引擎并不支持他.
mysql的innodb采用的是行锁,并且采用了多版本并发控制来提高读操作的性能.
那么我们如何去理解这个多版本并发控制呢?
其实就是在每一个行记录的后面增加两个隐藏列,记录创建版本号和删除版本号.
而每一个事务在启动的时候,都有一个唯一的递增的版本号 .
在innodb中,给每行增加两个隐藏字段来实现mvcc,两个列都用来存储事务的版本号,每开启一个新事务,事务的版本号就会递增.
于是,我们所熟悉的默认的隔离级别,增删改查变成了这样:

  • SELECT
    读取创建版本小于或等于当前事务版本号,并且删除版本为空或大于当前事务版本号的记录。这样可以保证在读取之前记录是存在的。
  • INSERT
    将当前事务的版本号保存至行的创建版本号
  • UPDATE
    新插入一行,并以当前事务的版本号作为新行的创建版本号,同时将原记录行的删除版本号设置为当前事务版本号
  • DELETE
    将当前事务的版本号保存至行的删除版本号
    在读入操作时:记录的创建版本号保存至行的删除版本号
我们来模拟多版本并发控制流程
在插入操作时:记录的创建版本号就是事务版本号
  1. 首先我们在一张表中插入一条数据
    在这里插入图片描述
    此时事务id为1,并且隐藏列版本号也就是1
未完待续…
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值