MVCC:多版本并发控制,不加锁就让多个事务并发读写,提高并发的读写性能
当前读&&快照读??
- 当前读:就是
它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁
;像 select .....lock in share mode (共享锁),
select ..... for update (排他锁) ;
update, insert ,delete (排他锁)
这些操作都是一种当前读。当前读实际上是一种加锁的操作,是悲观锁的实现。
- 快照读:快照读的实现是基于多版本并发控制,既然是基于多版本,即
快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本
。快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读;像不加锁的select * from 操作就是快照读
,即不加锁的非阻塞读,不涉及其他锁之间的冲突;
事务ID
当每个事务开启时,都会被分配一个ID, 这个ID是递增的,所以最新的事务,ID值越大
MVCC模型在MySQL中的具体实现原理:
1.是由 3个隐式字段,undo日志 ,Read View 等去完成的
2.旧数据存储在UNDO中,再通过DB_ROLL_PTR 回溯查找历史版本
3.通过read view判断行记录是否可见
隐藏字段
每行记录除了我们自定义的字段外,还有数据库隐式定义的字段
DB_TRX_ID
6byte,事务ID:记录创建这条记录/最后一次修改该记录的事务ID
DB_ROLL_PTR
7byte,回滚指针,指向这条记录的上一个版本
DB_ROW_ID
6byte,隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以DB\_ROW\_ID产生一个聚簇索引
例如下图:
(新insert记录的DB_ROLL_PTR指针为NULL。修改新值后,记录的 DB_ROLL_PTR 回滚指针指向原始值在Undo Log 日志的位置)
以一条SQL为例子:
读提交的逻辑和可重复读的逻辑类似,它们最主要的区别是:read view (一致性视图)
§ RR隔离级别下,在每个事务开始的时候,会将当前系统中的所有的活跃事务拷贝到一个列表中(read view)。
§ RC隔离级别下,在事务中的每个语句开始(select)时,会将当前系统中的所有的活跃事务拷贝到一个列表中(read view)
然后按照以下逻辑判断事务的可见性
如果上面的还是没有看懂,可以参看下面的逻辑图:
如果查询出来的结果是,数据不可见,那么就需要去undo log中查找最新的已提交的数据,通过undolog 中记录的事务id 和 read view做比较,不在readview且最大的事务id就是满足条件的数据了。
注意:事务更新数据的时候,只能用当前读。如果当前的记录的行锁被其他事务占用的话,就需要进入锁等待。
事务A
事务B
当执行事务A时,等到第2步执行完毕后,执行事务B,再接着执行事务A,发现数据update失败,就是应为每次更新数据都是当前读。
总结:
(1)说白了MVCC就是为了实现 读(select)-写冲突 不加锁,而这个读指的就是快照读。
在读(select)操作时不用阻塞写操作,写操作也不用阻塞读操作。
(2)MVCC只在 可重读 和 读已提交 两个隔离级别下工作,其他两个隔离级别都和MVCC不兼容。因为读未提交总是读取最新的数据行,而不是符合当前事务版本的数据行,而串行化则会对所有读取的行都加锁。
(3)事务更新数据的时候,只能用当前读。如果当前的记录的行锁被其他事务占用的话,就需要进入锁等待。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。