义乌网站建设方案详细,做会计题目的网站,做母亲节网站的素材,网站服务器租赁费用表格深入理解MVCC
1、MVCC定义
MVCC:Multi-Version Concurrency Control#xff0c;多版本并发控制机制。
在mysql中#xff0c;为了满足事务的四大特性之一的隔离性#xff0c;就是当前事务中的查询的数据不受其他事务的增删改操作的影响#xff0c;因此mysql主要是通过这个…深入理解MVCC
1、MVCC定义
MVCC:Multi-Version Concurrency Control多版本并发控制机制。
在mysql中为了满足事务的四大特性之一的隔离性就是当前事务中的查询的数据不受其他事务的增删改操作的影响因此mysql主要是通过这个可串行化的这种隔离级别和现在要谈的mvcc机制来实现。而可串行化就是将所有的操作由并行改为串行就是在每个增删改查操作上面都加了锁因此性能非常低因此mysql也并没有选择这个可串行化来作为mysql默认的隔离级别而是使用可重复读。接下来就是主要谈一下这个可重复读事务中的mvcc的机制和底层原理。
2、undolog日志
在讲mvcc机制之前首先要了解一下这个undolog日志。在mysql中如果使用的是默认的可重复读的这个隔离级别在一条更新语句中如果加了事务那么在这个事务启动之后提交之前那么这条数据是暂时不会添加到数据库的直到事务提交成功才会更新或者添加到数据库中。那么中间就需要实现数据的暂存那么这种存储方式就是通过这个undolog日志实现的。
CREATE TABLE product (id bigint(20) NOT NULL,product_id int(11) DEFAULT NULL COMMENT 商品id,version int(11) DEFAULT NULL COMMENT 版本,stock int(11) DEFAULT NULL COMMENT 商品数量,updated_time datetime DEFAULT NULL COMMENT 更新时间,created_time datetime DEFAULT NULL COMMENT 创建时间,is_deleted tinyint(4) DEFAULT NULL COMMENT 是否删除,PRIMARY KEY (id)
) ENGINEInnoDB DEFAULT CHARSETutf8;如上创建一张表新增一条数据
insert into stock values (1,1,0,100,now(),now(),0);拿一条更新语句来说如上面一张商品表接下来要扣减一件商品的库存一开始这件商品库存有100现在扣减20如果扣减成功那么数据库的值就是80。
update product set stock stock - 20 where id 1;如果在扣减库存时发生异常数据回滚那么此时数据需要回滚到之前的值就需要一个日志来记录扣减之前的值那么就是通过这个undolog来记录的。就是说在这个更新语句中在开启事务之后提交事务之前这个库存100就会记录到undolog日志里面减完后的80这个值如果整个事务没有发生异常那么就直接加入到数据库中如果出现异常那么就通过undolog里面的值作为回滚的数据。一句话说这个undolog日志就是用来记录被修改的值防止出现异常回滚的
3、undolog版本控制链
当然这个undolog日志也不是只记录一条如在一个或者多个事务对这个库存进行多次修改那么这个undolog就会形成一条历史版本控制链。在这个版本控制链中有一个隐藏的事务id和指针。事务id在新增或者更新都会生成一个事务id默认自增指针所指向的就是当前数据修改前的一个历史数据如果出现了回滚那么就会根据这个链路依次往前回滚直到找到上一个或者前面几个。 这个事务id在sql更新或者新增时生成并非事务提交生成因此可能出现事务大的id先提交那么版本链路里面的事务id的大小就是乱序的。
4、readView
1、readView简介
在上面的undolog日志里面可以发现确实记录了所有修改的值也知道undolog是用来回滚的但是会存在一个问题如果单纯使用undolog来解决这个回滚问题那么就会不知道回滚到链路中的哪一个节点因此就需要通过readView来和这个undolog结合使用通过readView来知道具体回滚到链路中的哪个节点。
在一个事务里面执行任何查询都会生成当前事务的一致性视图read-view在可重复读中的事务隔离级别中该视图在事务结束之前都不会发生变化当前如果是读已提交的隔离级别那么在每次执行sql时都会重新生成视图。
在可重复读的事务里面这个readView视图由未提交的事务id数组和已创建的最大事务id(max_id)组成因此这个最大的max_id可能在数组里面也可能不在因此事务最大的id可能先提交而数组里面的id都是未提交的。 如上图由四个事务ABCD同时开启事务同时去操作这个商品表的库存事务ABC在执行更新语句之后就会产生一个事务id因为事务id都是自增的因此从左往右事务依次递增。而事务D主要用来查询并无增删改操作主要是查询当前事务中的库存数量由于没有更新和新增语句因此也没有事务id。由于三个事务是同时开始因此commit提交时间取决于更新语句的时间谁先更新完谁先提交因此可能会出现事务大的id先提交。
上图只要是针对库存表id为1的那一行数据进行操作的而id2只是为了通过更新语句给这个事务生成一个事务id
并且mvcc机制主要是针对于可重复读的这个隔离级别因此在D中暂时只考虑查询有其他事务提交的数据未提交之前的数据暂时不做select查询考虑。
2、readView和undolog结合使用规则
在使用readview只能看到当前的几个事务并且不能得知事务的提交顺序因此需要结合undolog一起使用在两者结合使用之前需要有一个readview视图和undolog版本的对比规则接下来详细说明一下这个比对规则的一些命名:**假设这个数组中未提交的事务id数组的最小值假设为min_id 已创建事务的事务id的最大id为max_id,undolog版本控制链的头节点为head节点。**如在下面的第三个select查询中,min_id 102,max_id104。
然后以这个min_id和这个max_id为分界线小于min_id为已提交的事务在这个两个值之间的为未提交或者已提交的事务大于这个max_id的为未开始的事务。 那么规则如下
head节点的事务idmin_id已提交的事务该事务可见min_idhead节点的事务idmax_id未提交或者已提交的事务max_idhead节点的事务id未开始的事务不可见如果事务id在数组中表示事务未提交不可见如果事务id不在数组中表示事务已提交不可见
总结只要满足一个事务是可见的那么这个版本控制链路对应结点的值就是需要找的值
3、readview和undolog基本使用
1假设库存一开始为200由于事务id是自增那么可以暂时假设这个事务trx_id101的值对应的库存就是200那么在事务D中在第一次查询之后就会生成一个readview视图并且在事务提交之前这个视图的值不会改变。接下来主要研究一下在这个RR的默认级别事务中为何select查询的值可以不变化以及readview和undolog匹配的过程是咋样的。
2接下来看第一个select查询此时事务ABC都因为有了更新语句因此此时abc都有对应的事务并且事务A已经提交事务此时的事务readview组成如下而undolog链路中的值如下图因为主要是针对表中id为1的库存对应的版本链路因此暂时只有两个数据bc中两个更新语句只为了生成事务id数据并不在一个undolog版本链路上并且此时头节点head对应的事务id为102。
那么通过这个头结点head对应的事务和readview的视图进行对比此时的head事务id为102min_id为数组中最小值103max_id为已创建的最大值id104根据版本比对规则符合第一条head结点的事务id小于min_id即当前结点时可见的只要获取到的值是可见的那么查询到的值就是这个事务对应的值即100。
//第一个select查询语句的值,由未提交的事务id数组和已创建的最大的事务id组成
[103,104],1043接下来看第二个select查询语句此时的事务B提交了事务B是操作id为1的商品数据因此在更新时会将原始值加入到这个undolog的日志版本链路上。由于事务D并没有提交因此此时的readview如下和之前一样但是undolog日志链路会多一条数据其链路如下图
那么此时的头节点的值为事务id103即head对应的事务id为103min_id为103max_id为104。根据版本对比规则符合第二条但是此时的head对应的事务id还在数组中因此这个结点的数据并不可见。那么将继续对比下一个结点下一个结点的事务id为102符合第一条head结点的事务id小于min_id即事务id为102对应的结点时可见的那么查询到的值仍然时100
//第二个select查询语句的值,由未提交的事务id数组和已创建的最大的事务id组成
[103,104],1044接下来再看第三个select语句第三个select查询语句就是在事务C提交之后进行查询的那么此时的readview视图如下依旧不变因为操作的是id为1的值因此undolog版本链路上会多一条数据。
此时的头节点head的事务id为104min_id为103max_id为104。根据版本对比规则符合第二条但是此时的head对应的事务id还在数组中因此这个结点的数据并不可见那么将继续对比下一个结点下一个结点的事务id为103符合第二条但是此时的head对应的事务id还在数组中因此这个结点的数据也不可见接下来对比第三条head结点的事务id为102小于min_id103即事务id为102对应的结点时可见的那么查询到的值仍然时100
//第三个select查询语句的值,由未提交的事务id数组和已创建的最大的事务id组成
[103,104],104因此不管后面有再多的其他事务更改只要当前事务没有提交那么当前事务对应的readview就不会改变通过undolog的日志版本链路并且结合readview的版本比对规则就可以找到一个可见的事务对应的数据那并且这个值一定是最先获取的值就如上面商品的库存即使数据库中的值真的变了也可以通过这个mvcc机制来保证事务的隔离性从而解决使用读写锁效率低慢的问题。一句话总结就是根据数据版本链对比规则来读取同一条数据在版本链上的不同版本数据并且可以存在多个事务形成多个readview但是版本链undolog只有一条
5、总结
mvcc被称为多版本并发控制机制由于mysql中的事务默认的事可重复读在这个隔离级别中并没有解决幻读问题以此可以通过mvcc机制解决并且还可以解决并发中读写锁读写冲突问题从而提高并发读写的性能和效率。mcvv机制主要是通过undolog的日志版本控制链路和readview视图组成。undolog链路中的每个节点有一个事务id和一个指针组成事务id是在更新或者插入数据时生成指针是用来指向上一个版本在执行完更新语句时就会将这个事务id加入到版本链路中readview视图由未提交的事务id数组和已创建和最大事务id组成并且在一个事务中第一次select查询就会生成一个readview视图并且在该事务提交之前该事务的readview视图不会改变然后根据readview视图比对规则其规则就是将undolog链路中的头节点为head节点将数组中的最小id为min_id将已创建的id为最大max_id然后根据视图对比规则找到一个事务id是可见的那么找到的第一个可见的值该事务id对应的节点的值就是需要查询的值。主要是通过版本链对比规则来读取同一条数据版本链路上不同的数据。这样就可以保证在一个事务中查询的值可以一直不变不受其他事务的影响并且这种方案的效率远远高于读写锁。