首页 > 编程知识 正文

新闻源收录是什么意思,mvcc

时间:2023-05-04 17:50:49 阅读:166630 作者:4521

MVCC简单地理解MVCC,全名是多版本控制,即同时控制多个版本。 MVCC是一种并发控制的方法,在数据库管理系统中一般实现对数据库的并发访问,在编程语言中实现事务内存。

这是百度百科的标准回答

转换为自己的语言:

多版本意味着数据库中同时存在多个版本的数据。 不是数据库整体的多个版本,而是同时存在某个记录的多个版本,某个事务处理对其进行操作时,查看该记录的隐藏列事务处理版本id,与事务处理id进行对照,实现事务处理的独立性

这些名词请记住。 如果能在博客上理解他们就okok了

事务隔离级别还原日志重做日志mvcc完整数据记录的好处作用

MySQL InnoDB中MVCC的实现主要是为了提高数据库的并发性,以便更好地处理读/写冲突,即使存在读/写冲突也不会锁定。 对于非块并行读取,已知并发访问数据库存在四个问题:脏写入(丢失修复)、脏读取、不可重复读取和幻读取。 MVCC通过最大限度地减少锁的使用来提高效率

隔离边界污染读取不可重复幻读READ UNCOMMITTED :可能发生未提交读取的READ COMMITTED :可能发生已提交读取解决方案的REPEATABLE READ :发生可重复读取解决方案

四个问题按严重性排序。 脏写脏读不能重复幻读

肮脏的书写这个问题太严重了,不管是什么隔离级别,都不允许肮脏的书写。

是的! 那么进入正题……

MVCC的实现原理数据库的四种隔离级别

我在以前的博客中谈到过InnoDB下的Compact行结构,有三个隐藏的列

列名是否必须描述row_ID行id,是否唯一标识记录,如果定义了主键,则没有。 transaction_id是事务IDroll_pointer,DB_ROLL_PTR是一个回滚指针,与还原日志配合使用,指向以前的版本http://ww

假设您首先添加了数据。 图:

两个事务同时更新信息,事务执行进程。

为什么两个事务的执行顺序有偏差?

很简单,如果可以同时交叉修正相同的数据,那不是“修正丢失(脏光)”的同时问题吗? mysql在执行操作时锁定,另一个事务暂时挂起

那么,更新了这么多次数据,他还是一个数据吗? 不,他在roll_pointer处记录了最近的更新记录,指向上一个更新数据:

每次更新此记录时,旧值都会保存在还原日志中。 即使是该记录的旧版本,随着更新次数的增加,所有版本都将通过roll_pointer属性连接到链表中。 这个链表称为版本链。 版本链中的第一个节点是当前记录的最新值。 此外,每个版本还包含生成该版本时对应的事务id

为什么没有脏写?

读视图是什么?

简而言之,读视图是当事务执行快照读取操作时生产的读取视图,当事务执行快照读取时数据库系统的当前快照每次事务开始时,都会分配一个ID,此ID会递增,从而导致最新事务的ID值较大。)

一、依赖于隐藏的两个列

您可以确定当前事务中显示的是版本链中的哪个版本。 他是怎么判断的呢? 详细介绍。

其中最重要的四个部分:

1、m _ ids :事务id列表,指示生成readview时当前系统上的活动读/写事务。

2、min _ Trx _ id :表示生成readview时当前系统中活动读写事务中最小的事务id,即m_ids中最小的事务id。

3、max _ Trx _ id :表示生成readview时系统应该分配给下一个事务的id值。

4、creator_trx_id :表示生成此ReadView的事务处理的事务处理id。

使用ReadView确定哪些版本的数据可读的过程:

如果被访问版本的trx_id属性值与ReadView的creator_trx_id值相同,则该版本可以通过当前事务访问,因为当前事务正在访问自己更改的记录如果被访问版本的trx_id属性值小于ReadView的min_trx_id值,则由于当前事务在生成ReadView之前已提交,因此生成版本的事务处理为当前事务处理如果被访问版本的trx_id属性值大于或等于ReadView的max_trx_id值,则访问权限将从当前事务生成ReadView后打开生成版本的事务如果访问的版本的trx_id属性值介于ReadView的min_trx_id和max_trx_id之间,则必须确定trx_id属性值

是不是在m_ids列表中,如果在,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问。

但是对于READ COMMITTED(读取已提交) 和REPEATABLE READ(可重复读)两种隔离界别来说产生ReadViem是不同的,下面我们来就这两个隔离级别说一下,如何判断是否可读某个历史版本记录

READ COMMITTED(读取已提交)— 每次读取数据前都生成一个ReadView

下面过程一定认真看(我第一遍粗略看没有挨着读,结果什么也不明白,后来认真读了一遍,尽可能明白每一个步骤所描述的,看下来之后就会有恍然大明白的感觉!!!)

1、比方说现在系统里有两个事务id分别为100、200的事务在执行:

Transaction 100
BEGIN;

UPDATE hero SET name = ‘关羽’ WHERE number = 1;

UPDATE hero SET name = ‘lkdfk’ WHERE number = 1;

Transaction 200
BEGIN;

更新了一些别的表的记录 …

注意此时两个事务都没有进行提交
2、此刻,表hero中number为1的记录得到的版本链表如下所示:

3、假设现在有一个使用READ COMMITTED隔离级别的事务开始执行:

BEGIN;

SELECT1:Transaction 100、200未提交
SELECT * FROM hero WHERE number = 1;
得到的列name的值为’刘备’

那这个select的语句能都读取到的数据就是我们最关心的啦!
过程:

在执行SELECT语句时会先生成一个ReadView,ReadView的m_ids列表的内容就是[100,
200],min_trx_id为100,max_trx_id为201,creator_trx_id为0。

然后从版本链中挑选可见的记录,从图中可以看出,最新版本的列name的内容是’lkdfk’,该版本的trx_id值为100,在m_ids列表内,所以不符合可见性要求,根据roll_pointer跳到下一个版本。

下一个版本的列name的内容是’关羽’,该版本的trx_id值也为100,也在m_ids列表内,所以也不符合要求,继续跳到下一个版本。

下一个版本的列name的内容是’刘备’,该版本的trx_id值为80,小于ReadView中的min_trx_id值100,所以这个版本是符合要求的,最后返回给用户的版本就是这条列name为’刘备’的记录。

4、之后,我们把事务id为100的事务提交一下
5、然后再到事务id为200的事务中更新一下表hero中number为1的记录(只有事务一执行完之后事务2才能执行,这个原因上面已经说过啦)

Transaction 200
BEGIN;

更新了一些别的表的记录 …

UPDATE hero SET name = ‘赵云’ WHERE number = 1;

UPDATE hero SET name = ‘axdtd’ WHERE number = 1;

此刻版本链是这样的:

6、然后再到刚才使用READ COMMITTED隔离级别的事务中继续查找这个number为1的记录(我们知道上面读取了一次,本次和上次的属于同一个事务不同次操作),如下

BEGIN;

SELECT1:Transaction 100、200均未提交
SELECT * FROM hero WHERE number = 1;
得到的列name的值为’刘备’

SELECT2:Transaction 100提交,Transaction 200未提交
SELECT * FROM hero WHERE
number = 1; # 得到的列name的值为’lkdfk’

7、这个SELECT2的执行过程如下:

在执行SELECT语句时会又会单独生成一个ReadView,该ReadView的m_ids列表的内容就是[200](事务id为100的那个事务已经提交了,所以再次生成快照时就没有它了),min_trx_id为200,max_trx_id为201,creator_trx_id为0。然后从版本链中挑选可见的记录,从图中可以看出,最新版本的列name的内容是’axdtd’,该版本的trx_id值为200,在m_ids列表内,所以不符合可见性要求,根据roll_pointer跳到下一个版本。下一个版本的列name的内容是’赵云’,该版本的trx_id值为200,也在m_ids列表内,所以也不符合要求,继续跳到下一个版本。下一个版本的列name的内容是’lkdfk’,该版本的trx_id值为100,小于ReadView中的min_trx_id值200,所以这个版本是符合要求的,最后返回给用户的版本就是这条列name为’lkdfk’的记录。

你认真看完了吗?如果明白了过程,你就记住这句话就行,以后遇到类似的场景就没有问题:
使用READ COMMITTED隔离级别的事务在每次查询开始时都会生成一个独立的ReadView。

REPEATABLE READ —— 在第一次读取数据时生成一个ReadView

我们还用上面的一样的场景看一下,进行对比,区别就显而易见啦

我们从事务100提交之后开始说(因为前面的操作是一样的,一样会读到“刘备”这一条数据,不过需要注意前面已经建立了一次ReadView

1、然后再到事务id为200的事务中更新一下表hero中number为1的记录:
此时版本链是这样的:

2、然后再到刚才使用REPEATABLE READ隔离级别的事务中继续查找这个number为1的记录,如下:

使用REPEATABLE READ隔离级别的事务
BEGIN;

SELECT1:Transaction 100、200均未提交
SELECT * FROM hero WHERE number = 1;
得到的列name的值为’刘备’

SELECT2:Transaction 100提交,Transaction 200未提交
SELECT * FROM hero WHEREnumber = 1;
得到的列name的值仍为’刘备’

过程:

因为当前事务的隔离级别为REPEATABLE READ,而之前在执行SELECT1时已经生成过ReadView了,所以此时直接复用之前的ReadView,之前的ReadView的m_ids列表的内容就是[100, 200],min_trx_id为100,max_trx_id为201,creator_trx_id为0。

然后从版本链中挑选可见的记录,从图中可以看出,最新版本的列name的内容是’axdtd’,该版本的trx_id值为200,在m_ids列表内,所以不符合可见性要求,根据roll_pointer跳到下一个版本。

下一个版本的列name的内容是’赵云’,该版本的trx_id值为200,也在m_ids列表内,所以也不符合要求,继续跳到下一个版本。

下一个版本的列name的内容是’lkdfk’,该版本的trx_id值为100,而m_ids列表中是包含值为100的事务id的,所以该版本也不符合要求,同理下一个列name的内容是’关羽’的版本也不符合要求。继续跳到下一个版本。

下一个版本的列name的内容是’刘备’,该版本的trx_id值为80,小于ReadView中的min_trx_id值100,所以这个版本是符合要求的,最后返回给用户的版本就是这条列c为’刘备’的记录。

也就是说两次SELECT查询得到的结果是重复的

下面个人的理解,如有错误可以指正:
ReadView可以查到又名“快照读”,每次执行“快照读”,就好像给数据库拍了一个照片,你拍到了什么就可以读到什么,当然,如果你自己要在照片上画一个“小狗”,那这个“小狗”你也是可以看到了,但是如果你拍了一个杯子,在你拍完之后,别人往杯子里加了点水,你能在照片上看到吗?当然不能。ReadView中的m_ids事务id列表就是你拍照前在你面前跃跃欲试的人,所以你要记录好他们。所以就是利用事务id的先后顺序来判断能否读到某个版本信息。

上面信息和图片借鉴掘金小册《MySQL 是怎样运行的:从根儿上理解 MySQL》

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。