网站开发属于固定资产吗,加强网站建设的原因,自媒体平台,wordpress一级域名1.什么是MVCC MVCC #xff08;Multiversion Concurrency Control#xff09;#xff0c;多版本并发控制。MySQL通过MVCC来实现隔离性。隔离性本质上是因为同时存在多个并发事务可能会导致脏读、幻读等情况。要解决并发问题只有一种方案就是加锁。当然#xff0c;锁不可避免…1.什么是MVCC MVCC Multiversion Concurrency Control多版本并发控制。MySQL通过MVCC来实现隔离性。隔离性本质上是因为同时存在多个并发事务可能会导致脏读、幻读等情况。要解决并发问题只有一种方案就是加锁。当然锁不可避免的会导致性能下降但是锁也有乐观和悲观之分隔离级别中的串行化就是一种悲观的思想可以直接避免并发事务中所有的问题但是性能也是下降的非常严重。而MySQL是如何在性能和一致性中权衡的呢 MVCC全称多版本并发控制本质就是通过一种乐观锁的思想维护数据的多个版本以减少数据读写操锁的冲突做到即使有读写冲突时也能做到不加锁非阻塞并发读而这个读指的就是快照读 , 而非当前读这样就可以提高了 MySQL 的事务并发性能。换言之就是为了查询一些正在被另一个事务更新的行并且可以看到它们被更新之前的值这样在做查询的时候就不用等待另一个事务释放锁。
2.快照读和当前读
为什么要使用快照读而不是当前读 快照读 快照读又叫一致性读读取的是快照数据。不加锁的简单的 SELECT 都属于快照读即不加锁的非阻塞读比如这样
SELECT * FROM player WHERE ...之所以出现快照读是基于并发性能的考虑快照读的实现是基于MVCC,它在很多情况下避免了加锁操作降低了开销。 既然是基于多版本那么快照读可能读到的并不一定是数据的最新版本而有可能是之前的历史版本。快照读的前提是隔离级别不是串行级别串行级别下的快照读会退化成当前读。 当前读 当前读读取的是记录的最新版本最新数据而不是历史版本的数据读取时还要保证其他并发事务不能修改当前记录会对读取的记录进行加锁。加锁的 SELECT或者对数据进行增删改都会进行当前读。比如
SELECT * FROM student LOCK IN SHARE MODE; # 共享锁
SELECT * FROM student FOR UPDATE; # 排他锁
INSERT INTO student values ... # 排他锁
DELETE FROM student WHERE ... # 排他锁
UPDATE student SET ... # 排他锁3.MVCC机制
3.1 隐藏字段、undo日志版本链
回顾一下undo日志的版本链对于使用 InnoDB 存储引擎的表来说它的聚簇索引记录中都包含两个必要的隐藏列
trx_id 每次一个事务对某条聚簇索引记录进行改动时都会把该事务的 事务id 赋值给 trx_id 隐藏列。roll_pointer 每次对某条聚簇索引记录进行改动时都会把旧的版本写入到 undo日志 中然后这个隐藏列就相当于一个指针可以通过它来找到该记录修改前的信息 insert undo只在事务回滚时起作用当事务提交后该类型的undo日志就没用了它占用的Undo Log Segment也会被系统回收也就是该undo日志占用的Undo页面链表要么被重用要么被释放。 每次对记录进行改动都会记录一条undo日志每条undo日志也都有一个 roll_pointer 属性 INSERT 操作对应的undo日志没有该属性因为该记录并没有更早的版本可以将这些 undo日志都连起来串成一个链表我们把这个链表称之为 版本链 每个版本中还包含生成该版本时对应的 事务id 。
3.2 MVCC实现原理之ReadView
利用undo log日志我们已经保留下了数据的各个版本那么现在关键的问题是要读取哪个版本的数据呢
trx_ids: 指的是在创建 ReadView 时当前数据库中「活跃事务」的事务 id 列表注意是一个列表 “活跃事务”指的就是启动了但还没提交的事务。min_trx_id: 指的是在创建 ReadView 时当前数据库中「活跃事务」中事务 id 最小的事务也就是 m_ids 的最小值。max_trx_id这个并不是 m_ids 的最大值而是创建 ReadView 时当前数据库中应该给下一个事务的 id 值也就是全局事务中最大的事务 id 值 1 注意max_trx_id并不是trx_ids中的最大值事务id是递增分配的。比如现在有id为1 23这三个事务之后id为3的事务提交了。那么一个新的读事务在生成ReadView时 trx_ids就包括1和2min_trx_id的值就是1max_trx_id的值就是4。 creator_trx_id 指的是创建该 ReadView 的事务的事务 id, 只有在对表中的记录做改动时执行INSERT、DELETE、UPDATE这些语句时才会为 事务分配事务id否则在一个只读事务中的事务id值都默认为0。 对于当前事务的启动瞬间来说读取的一个数据版本的trx_id有以下几种可能 如果被访问版本的trx_id属性值与ReadView中的 creator_trx_id 值相同意味着当前事务在访问它自己修改过的记录所以该版本可以被当前事务访问。 如果落在绿色部分表示这个版本是已提交的事务或者是当前事务自己生成的这个数据是可见的 如果落在红色部分表示这个版本是由将来启动的事务生成的是肯定不可见的 如果落在黄色部分那就包括两种情况 若 数据的trx_id在trx_ids数组中表示这个版本是由还没提交的事务生成的不可见, 去读取这条数据的历史版本这条数据的历史版本中都包含了事务id信息去查找第一个不在活跃事务数组的版本记录。若 数据的trx_id不在trx_ids数组中表示这个版本是已经提交了的事务生成的可见。
这种通过版本链 一致性视图 来控制并发事务访问同一个记录时的行为就叫 MVCC多版本并发控制。 了解了这些概念之后我们来看下当查询一条记录的时候系统如何通过MVCC找到它
首先获取事务自己的版本号也就是事务 ID获取 ReadView查询得到的数据然后与 ReadView 中的事务版本号进行比较如果不符合 ReadView 规则就需要从 Undo Log 中获取历史快照最后返回符合规则的数据。
我们现在在MySQL的读已提交和可重复读隔离级别下MVCC机制的整个工作流程。
MySQL中的读未提交和序列化并不需要MVCC机制读未提交直接读取别人未提交的数据而序列化全程用加锁的方式也用不上MVCC。读已提交隔离级别的事务来说每一次执行查询语句都会生成一个ReadView可重复读REPEATABLE READ 隔离级别的事务来说只会在第一次执行查询语句时生成一个 ReadView之后的查询就不会重复生成了。
总结
READ COMMITED、REPEATABLE READ这两种隔离级别的事务在执行快照读时的不同点在于生成ReadView的时机不同
READ COMMITTD 在每一次进行普通SELECT操作前都会生成一个ReadViewREPEATABLE READ只在第一次进行普通SELECT操作之前省生成一个ReadView,之后的查询操作都重复使用这个ReadView就好了。