MVCC原理及Mysql隔离级别

1 篇文章 0 订阅

1. mysql常见的三个存儲引擎:

  1. InnoDB存储引擎:InnoDB是事务型数据库的首选引擎,支持事务安全表(ACID)支持行锁定和外键,MySQL5.5以后默认使用InnoDB存储引擎
  2. MyISAM存储引擎:MyISAM基于ISAM存储引擎,并对其进行扩展。它是在Web、数据仓储和其他应用环境下最常使用的存储引擎之一。MyISAM拥有较高的插入、查询速度,但不支持事务不支持外键
  3. MEMORY存储引擎:MEMORY存储引擎将表中的数据存储到内存中,为查询和引用其他表数据提供快速访问,不支持事务不支持外键

显而易见,这些存储引擎都是有不同的使用场景的,如果表对事物的要求高,应该使用InnoDB引擎,如果表对事物的要求不高,同时是以查询和添加为主,我们考虑使用MyISAM存储引擎或者MEMORY存储引擎。

2. MVCC原理解析

mysql数据行隐藏字段

字段含义
DB_ROW_ID隐含的自增ID(隐藏主键),用于唯一标识表中的每一行数据,如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引。
DB_TRX_ID该字段存储了当前行数据所属的事务ID。每个事务在数据库中都有一个唯一的事务ID。通过 DB_TRX_ID 字段,可以追踪行数据和事务的所属关系。
DB_ROLL_PTR该字段存储了回滚指针(Roll Pointer),它指向用于回滚事务的Undo日志记录。

2.1 介绍

MVCC(Multi-Version Concurrency Control),即多版本并发控制。这里的多版本是指一条数据可能会有多个版本。
MVCC的主要目的是为了提高数据库并发性能,用更好的方式去处理读写冲突,即做到读写冲突时,也可以不加锁。
MVCC的多版本并发控制是通过undo logRead View来实现的。

2.2 原理

undo log

在执行insert,update,delete操作时,会插入undo log;Undo Log的作用主要有两个方面:

  • 当事务需要回滚时,MySQL可以通过Undo日志中的旧值将数据还原到事务开始之前的状态,保证了事务回滚的一致性。
  • 通过使用Undo日志,MySQL可以为每个事务提供独立的事务视图,使得事务读取数据时能看到一致且符合隔离级别要求的数据版本。

不同事务或者同一事务对同一条数据操作时会,会使记录行的undo log成为一条链表(版本链),链首为最新数据,链尾为最早的数据。
举例:
1、事务A插入了一条新记录:insert into user(id, name) values(1, "小明’)
在这里插入图片描述
2、现在来了一个事务B对该记录的name做出了修改,改为 “小王”。
在事务B对数据修改时,数据库会对该行加排它锁,然后把该行的数据复制到undo log中,拷贝完毕后,修改该行数据name=“小王”,并且修改该行事务ID为事务B的事务ID,并将回滚指针指向拷贝到undo log中的副本记录,表示上一个版本就是它,然后提交事务后,释放锁。
在这里插入图片描述
3、此时又来了个事务C修改同一个记录,将name修改为 “小红”。(同步骤2)
在这里插入图片描述

Read View

一致性视图,用来判断版本链中哪个版本对当前事务可见。
在事务进行快照读时,生成读视图,记录并维护当前时刻系统活跃的事务ID(每个事务都会分配一个事务ID,且是递增的)。值得注意的是,Read View只针对 RC(读已提交) 和 RR(可重复读)级别,RU(读未提交)与串行化用不上。

Read View可见性原则

Read View 会维护以下几个字段:

字段含义
m_idsRead View 创建时其他未提交的活跃事务 ID 列表。创建 Read View时,将当前未提交事务 ID 记录下来,后续即使它们修改了记录行的值,对于当前事务也是不可见的。m_ids 不包括当前事务自己和已提交的事务(正在内存中)。
m_creator_trx_id创建该 Read View 的事务 ID。
m_low_limit_id目前出现过的最大的事务 ID+1,即下一个将被分配的事务 ID。大于等于这个 ID 的数据版本均不可见。
m_up_limit_id活跃事务列表 m_ids 中最小的事务 ID,如果 m_ids 为空,则 m_low_limit_id为m_up_limit_id 。小于这个 ID 的数据版本均可见。

Read View 可见性具体判断如下:
db_trx_id为当前访问数据行上的事务ID

  • db_trx_id < up_limit_id || db_trx_id == creator_trx_id(显示)
    ​ 如果数据事务ID小于read view中的最小活跃事务ID,则可以肯定该数据是在当前事务启之前就已经存在了的,所以可以显示。

    或者数据的事务ID等于creator_trx_id ,那么说明这个数据就是当前事务自己生成的,自己生成的数据自己当然能看见,所以这种情 况下此数据也是可以显示的。

  • db_trx_id >= low_limit_id(不显示)
    如果数据事务ID大于read view 中的当前系统的最大事务ID,则说明该数据是在当前read view 创建之后才产生的,所以数据不显示。 如果小于则进入下一个判断

  • db_trx_id是否在活跃事务(trx_ids)中
    不存在:则说明read view产生的时候事务已经commit了,这种情况数据则可以显示。
    已存在:则代表我Read View生成时刻,你这个事务还在活跃,还没有Commit,你修改的数据,我当前事务也是看不见的。

3. 事务的隔离级别

  1. 读未提交(READ UNCOMMITTED):这是事务的最低隔离级别,即一个事务还没有提交,其他的读取操作就可以获取到事务的修改数据
    原始数据
    原始数据
事务1操作介绍事务2操作介绍
操作1、SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;设置隔离级别为读未提交
操作2、START TRANSACTION;开启事务1
操作3、UPDATE transational_test SET account = account + 50 WHERE id = 1;修改数据库 account+50
操作4、START TRANSACTION;开启事务2
操作5、SELECT * FROM transational_test查询数据库数据
操作6、ROLLBACK;回滚修改操作

由于我已经回滚,但是读到了回滚前的数据,造成了脏读
在这里插入图片描述

  1. 读已提交(READ COMMITTED):不存在脏读问题,但是存在不可重复读问题
事务1操作介绍事务2操作介绍
操作1、SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;设置隔离级别为读已提交
操作2、START TRANSACTION;开启事务1
操作3、UPDATE transational_test SET account = account + 50 WHERE id = 1;修改数据库 account+50
操作4、START TRANSACTION;开启事务2
操作5、SELECT * FROM transational_test;查询数据库数据
操作6、COMMIT;提交事务1
操作7、SELECT * FROM transational_test;查询数据库数据

步骤5的查询结果:
在这里插入图片描述
步骤7的查询结果: 事务提交后,再次查询,结果与上一步不一致,产生了不可重复读的问题
在这里插入图片描述

  1. 可重复读(REPEATABLE READ)(数据库默认使用):没有脏读,且可重复读,但是可能出现幻读
事务1操作介绍事务2操作介绍
操作1、SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;设置隔离级别为可重复读
操作2、START TRANSACTION;开启事务1
操作3、INSERT INTO transational_test VALUE(2,200);添加一条记录
操作4、START TRANSACTION;开启事务2
操作5、SELECT * FROM transational_test;查询数据库数据
操作6、COMMIT;提交事务1
操作7、SELECT * FROM transational_test;查询数据库数据
操作8、SELECT * FROM transational_test FOR UPDATE;查询数据库数据

步骤5执行结果:
在这里插入图片描述
步骤7执行结果:
在这里插入图片描述
步骤8执行结果:
在这里插入图片描述
发现了没,在没有update操作,或者select 上加for update时,是没有出现幻读的,但是一旦有,则会出现前后查询不一致的情况(幻读)

  1. 串行化(serializable):可以避免一切脏读,不可重复读,幻读
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值