>  기사  >  데이터 베이스  >  MySQL 파트 4에 대한 나의 이해: 트랜잭션, 격리 수준 및 MVCC

MySQL 파트 4에 대한 나의 이해: 트랜잭션, 격리 수준 및 MVCC

coldplay.xixi
coldplay.xixi앞으로
2020-10-27 17:03:222660검색

mysql tutorial 컬럼에서는 MySQL 관련 트랜잭션, 격리 수준 및 MVCC를 소개합니다.

MySQL 파트 4에 대한 나의 이해: 트랜잭션, 격리 수준 및 MVCC

MySQL 시리즈의 네 번째 부분인 주요 내용은 트랜잭션 ACID 특성, 격리 수준, 더티 읽기 이해, 반복 불가능 읽기, 팬텀 읽기 및 다중 버전 동시성 제어(MVCC)를 포함한 트랜잭션입니다. ).

트랜잭션은 분할할 수 없는 원자 작업 집합이 모두 실행되거나 어느 것도 실행되지 않도록 보장할 수 있습니다. MySQL에서 일반적으로 사용되는 스토리지 엔진 중 InnoDB는 트랜잭션을 지원하지만 기본 MyISAM 엔진은 트랜잭션을 지원하지 않습니다.

본 글에서는 별도로 명시하지 않는 한, 사용된 데이터 테이블과 데이터는 다음과 같습니다.

CREATE TABLE `user`  (  `id` int(11) DEFAULT NULL,  `name` varchar(12) DEFAULT NULL) ENGINE = InnoDB;insert into user values(1, '刺猬');复制代码

1. ACID의 4가지 주요 특징

가장 먼저 이해해야 할 것은 거래 ACID의 4가지 주요 특징, 즉 원자성(Atomicity), 일관성, 격리성, 내구성은 트랜잭션의 네 가지 기본 요소입니다.

ACID 특성을 자세히 설명하기 위해 시나리오는 다음과 같습니다. 100위안을 귀하에게 이체합니다.

이 작업을 다음 단계로 나눌 수 있다고 가정합니다(나의 계좌 잔고와 귀하의 계좌 잔액이 모두 100위안이라고 가정):

  1. 내 계좌 잔액 확인
  2. 내 계좌에서 100위안이 인출됩니다
  3. 100위안 이체가 시작됩니다
  4. 확인 귀하의 계정 잔액
  5. 100위안이 귀하의 계정에 적립되었습니다

1.1 원자성

거래의 원자성은 다음을 의미합니다. 거래는 재분배될 수 없는 최소 작업 단위여야 합니다. 거래의 작업은 둘 다 성공합니다. 또는 둘 다 실패합니다. 트랜잭션에서 작업의 일부만 수행하는 것은 불가능합니다.

위 이체 시나리오에서 원자성은 이 5단계가 모두 실행되거나 전혀 실행되지 않아야 합니다. 내 계좌에서 100위안을 인출하는 것은 불가능하지만 귀하의 계좌에는 100위안이 입금되지 않습니다.

1.2 일관성

트랜잭션의 일관성은 데이터베이스가 항상 하나의 일관성 상태에서 다른 일관성 상태로 전환되는 것을 의미하며, 데이터의 중간 상태는 외부에 표시되지 않습니다.

同时,事务的一致性要求符合开发人员定义的约束,如金额大于0、身高大于0等。

在上述的转账场景中,一致性能够保证最终执行完整个转账操作后,我账户的扣款金额与你账户到账金额是一致的,同时如果我和你的账户余额不满足金额的约束(如小于0),整个事务会回滚。

1.3 隔离性(Isolation)

事务的隔离性是指:在一次状态转换过程中不会受到其他状态转换的影响。

假设我和你都有100元,我发起两次转账,转账金额都是50元,下面使用伪代码来表示的操作步骤:

  1. 查询我的账户余额 read my
  2. 我的账户扣款50元 my=my-50
  3. 50元开始转移
  4. 查询你的账户余额 read yours
  5. 你的账户到账50元 yours=yours+50

如果未保证隔离性就可能发生下面的情况:

时刻 第一次转账 第二次转账 我的账户余额 你的账户余额
1 read my(100)
my=100 yours=100
2
read my(100) my=100 yours=100
3 my=my-50=100-50=50
my=50 yours=100
4 read yours(100) my=my-50=100-50=50 my=50 yours=100
5 yours=yours+50=100+50=150
my=50 yours=150
6
read yours(150) my=50 yours=150
7
yours=yours+50=150+50=200 my=50 yours=200
7 end end my=50 yours=200

两次转账后,最终的结果是我的账户余额为50元,你的账户余额为200元,这显然是不对的。

而如果在保证事务隔离性的情况下,就不会发生上面的情况,损失的只是一定程度上的一致性。

1.4 持久性(Durability)

事务的持久性是指:事务在提交以后,它所做的修改就会被永久保存到数据库。

在上述的转账场景中,持久性就保证了在转账成功之后,我的账户余额为0,你的账户余额为200。

2. 自动提交与隐式提交

2.1 自动提交

在 MySQL 中,我们可以通过 begin 或 start transaction 来开启事务,通过 commit 来关闭事务,如果 SQL 语句中没有这两个命令,默认情况下每一条 SQL 都是一个独立的事务,在执行完成后自动提交

比如:

update user set name='重塑' where id=1;复制代码

假设我只执行这一条更新语句,在我关闭 MySQL 客户端然后重新打开一个新的客户端后,可以看到 user 表中的 name 字段值全变成了「重塑」,这也印证了这条更新语句在执行后已被自动提交。

自动提交是 MySQL 的一个默认属性,可以通过 SHOW VARIABLES LIKE 'autocommit' 语句来查看,当它的值为 ON 时,就代表开启事务的自动提交。

mysql> SHOW VARIABLES LIKE 'autocommit';
+---------------+-------+| Variable_name | Value |
+---------------+-------+| autocommit    | ON    |
+---------------+-------+1 row in set (0.00 sec)复制代码

我们可以通过 SET autocommit = OFF 来关闭事务的自动提交。

2.2 隐式提交

然而,即便我们已经将 autocommit 变量的值改为 OFF 关闭事务自动提交了,在执行某些 SQL 语句的时候,MySQL 还是会将事务自动提交掉,这被称为隐式提交

会触发隐式提交的 SQL 语句有:

  • DDL(Data definition language,数据定义语言),如 create, drop, alter, truncate
  • 修改 MySQL 自带表数据的语句,如 create/drop user, grant, set password
  • 在一个事务中,开启一个新的事务,会隐式提交上一个事务,如:
时刻 事务A 事务B
1 begin;
2 update user set name='重塑' where id=1;
3
select name from user where id=1;(N1)
4 begin;
5
select name from user where id=1;(N2)

트랜잭션 B에는 두 개의 쿼리문 N1과 N2가 있습니다. 실행 결과는 N1=hedgehog 및 N2=reshape이며 이는 증명될 수 있습니다.

  • 그밖에도 경영방침이 있어서 하나하나 예를 들진 않겠습니다. 직접 바이두하시면 됩니다.

3. 격리 수준

트랜잭션의 격리 수준은 트랜잭션 내부 및 트랜잭션 간 수정 사항의 가시성을 지정합니다. 격리 수준이 낮을수록 일반적으로 동시성이 높아지고 시스템 오버헤드가 낮아집니다.

SQL 표준에는 커밋되지 않은 읽기, 커밋된 읽기, 반복 가능한 읽기 및 직렬화 가능이라는 네 가지 트랜잭션 격리 수준이 정의되어 있습니다.

4가지 격리 수준과 각각의 현상을 자세히 설명하기 위해 두 개의 트랜잭션이 실행될 예정이라고 가정합니다. 실행 내용은 다음과 같습니다.

4id=1;(N1)5commit;6id=1;(N2)인 사용자에서 이름 선택7commit;8id=1;(N3)
Time Transaction A Transaction B
1 begin;
2
begin;
3
사용자 세트 업데이트 이름='리모델', 여기서 ID=1





에서 이름 선택 트랜잭션 A와 트랜잭션 B의 실행 , N1, N2, N3에 대한 세 가지 쿼리가 있으며 각 격리 수준마다 해당 값이 다르며 이에 대해서는 아래에서 설명합니다.

3.1 커밋되지 않은 읽기

커밋되지 않은 읽기 격리 수준에서

트랜잭션의 수정 사항은 커밋되지 않은 경우에도 다른 트랜잭션에 표시됩니다.

위 시나리오에서 데이터베이스의 격리 수준이 uncommitted로 읽혀지면 트랜잭션 A는 커밋되지 않은 트랜잭션 B의 수정된 데이터를 읽을 수 있으므로 즉, 시간 3에서 트랜잭션 B의 수정 사항이 트랜잭션 A에 표시되므로 N1 = 모양 변경 , N2=모양 변경, N3=모양 변경.

3.2 커밋된 읽기

커밋된 읽기 격리 수준에서

트랜잭션의 수정 사항은 커밋된 후에만 다른 트랜잭션에 표시됩니다.

위 시나리오에서 데이터베이스의 격리 수준이 커밋된 경우 트랜잭션 A는 트랜잭션 B가 제출된 후에만 데이터를 읽을 수 있으므로 시간 3에서 트랜잭션 B의 수정 사항은 트랜잭션 A에 표시되지 않으며 쿼리는 N2에서는 트랜잭션 B가 제출된 후 트랜잭션 A에 표시됩니다. 따라서 N1=고슴도치, N2=재발명, N3=재발명입니다.

3.3 반복 읽기

반복 읽기는 MySQL의 기본 트랜잭션 격리 수준입니다. 반복 읽기 격리 수준에서는 트랜잭션에서 동일한 레코드를 여러 번 쿼리하는 경우 결과가 항상 일관됩니다. 위 시나리오에서 데이터베이스의 격리 수준이 반복 읽기인 경우 쿼리 N1과 N2가 하나의 트랜잭션에 있으므로 해당 값은 모두 "고슴도치"이고 N3은 트랜잭션 A가 수행된 후에 수행되는 쿼리입니다. 제출된 트랜잭션 B의 수정 사항이 표시되므로 N3 = 모양이 변경됩니다.

3.4 직렬화 가능

직렬화 가능 격리 수준에서는

트랜잭션이 순차적으로 실행됩니다. 읽기는 읽기 잠금을 추가하고 쓰기는 쓰기 잠금을 추가합니다

따라서 이상이 발생하지 않습니다.

위 시나리오에서 데이터베이스의 격리 수준이 직렬화 가능한 경우 트랜잭션 A가 먼저 열리고 트랜잭션 B가 열리면 트랜잭션 B는 트랜잭션 A가 제출될 때까지 열리지 않으므로 N1=Hedgehog, N2= 고슴도치. N3의 쿼리는 트랜잭션 B가 제출된 후에 실행됩니다(트랜잭션 B가 먼저 차단되고 실행 순서가 N3 쿼리 문 이전임). 따라서 N3=reshape입니다.

4. 격리 수준으로 인한 문제

다른 트랜잭션 격리 수준에서 트랜잭션이 동시에 실행되면 더티 읽기(Dirty Read), 반복 불가능 읽기(Non-Repeatable Read), 팬텀 읽기(Phantom Read) 등 많은 문제가 발생합니다. 등. 아래에서는 이러한 문제를 자세히 설명하기 위해 다양한 예를 사용합니다.

4.1 Dirty Read

Dirty Read

(Dirty Read)는 트랜잭션이 커밋되지 않은 다른 트랜잭션에 의해 수정된 데이터를 읽을 수 있음을 의미합니다.

격리 수준이 커밋되지 않은 상태로 읽힌다고 가정하고 다음 사례를 살펴보세요.

2begin;3id=1;(N1)인 사용자에서 이름 선택 56id=1;(N2)com에서 이름 선택 미트;

읽기 커밋되지 않은 격리 수준에서 N1의 값은 "reshape"이고, 트랜잭션 B의 롤백으로 인해 N2의 값은 "hedgehog"입니다. 여기 N1에서 더티 읽기가 발생했습니다. 분명히 N1의 쿼리 결과는 더티 데이터이며 이는 정상적인 비즈니스에 영향을 미칩니다.

더티 읽기는 커밋되지 않은 읽기 격리 수준에서 발생합니다.

4.2 반복 불가능 읽기

반복 불가능 읽기는 동일한 쿼리를 두 번 실행하면 다른 결과가 발생할 수 있음을 의미합니다.

격리 수준을 도입할 때 AB 트랜잭션 사례를 계속 사용하고 격리 수준이 읽기 커밋되었다고 가정합니다.

update user set name='reshape' where id=1; 4

rollback;
7

2begin;에서 사용자 이름 선택 커밋된 읽기 격리 수준에서 트랜잭션은 다른 트랜잭션이 제출한 데이터를 읽을 수 있습니다. 위의 경우 결과는 N1=hedgehog, N2=reshape, N3=reshape입니다. 트랜잭션 A에는 두 개의 동일한 쿼리 N1과 N2가 있지만 이 두 쿼리의 결과는 동일하지 않습니다. 자꾸. 반복 불가능한 읽기는 커밋되지 않은 읽기와 커밋된 읽기의 격리 수준에서 발생합니다. Phantom Read이때 격리 수준이 반복 읽기 가능하다고 가정하여 다음 사례를 살펴보세요.
3
update user set name='reshape' 여기서 id=1;
4 사용자 이름을 선택하세요. id= 1;(N1)
5
commit;
6 id=1;(N2)
7 commit ;
8 id=1;(N3)

4.3 Phantom Read
(Phantom Read)는 트랜잭션이 특정 범위의 레코드를 읽을 때 다른 트랜잭션이 해당 범위 에 새 레코드 를 삽입하는 것을 의미하며, 이전 트랜잭션이 이 범위의 레코드를 읽을 때 다시 한 번, 이 새 레코드를 읽게 됩니다.

time

Transaction A

Transaction B

1

begin;

2 사용자에서 이름 선택;(N1) (N3) 8커밋;

트랜잭션 A에는 N1과 N2 사이에 3개의 쿼리가 있습니다. 트랜잭션 B는 insert 문을 실행하고 이를 제출했습니다. for update.

트랜잭션 A가 트랜잭션 B보다 먼저 열렸기 때문에 N1의 결과는 분명히 "고슴도치"일 뿐입니다. N3의 결과는 이론적으로 반복 읽기 격리에서 "고슴도치"여야 합니다. 레벨은 "고슴도치"이지만 실제로 N2의 결과는 "고슴도치"와 "오천인"이며 환상 판독이 발생합니다.

이것은 반복 읽기 격리 수준이 트랜잭션에서 동일한 레코드를 여러 번 쿼리하고 결과가 항상 일관되도록 보장할 수 있다는 것을 의미하지 않습니까? 이 결과는 반복 읽기의 정의를 충족하지 않습니다.

실제로 반복 읽기 격리 수준에서 현재 읽기를 사용하면 팬텀 읽기가 발생할 수 있습니다.

현재 읽기와 스냅샷 읽기는 나중에 트랜잭션과 MVCC의 구현 원리를 소개할 때 논의할 예정입니다.

팬텀 읽기는 커밋되지 않은 읽기, 커밋된 읽기 및 반복 가능한 읽기의 격리 수준에서 발생합니다.

여기서 특별히 주의해야 할 점: 팬텀 읽기와 반복 불가능 읽기는 모두 트랜잭션에서 동일한 쿼리 문의 결과가 다르다는 것을 의미하지만 팬텀 읽기는 다른 트랜잭션에서 새로 삽입된 데이터를 쿼리하는 데 더 중점을 둡니다(삽입 ) 또는 다른 트랜잭션에 의해 삭제된 데이터(삭제)이며, 반복 불가능한 읽기는 범위가 더 넓지만 결과가 다르기 때문에 반복 불가능한 읽기로 간주할 수 있지만 일반적으로 우리는 반복 불가능한 읽기가 더 중점을 둔다고 생각합니다. 다른 거래에 의한 데이터 업데이트(업데이트).

4.4 요약

위의 설명을 통해 우리는 이미 네 가지 격리 수준의 개념과 각각 발생하는 문제를 알고 있습니다. 트랜잭션의 격리 수준이 높을수록 격리가 강해지고 문제가 더 많이 발생합니다. 그러나 동시에 격리 수준이 높을수록 동시성 기능은 약해집니다.

다음 표는 격리 수준의 개념과 다양한 격리 수준에서 발생할 수 있는 문제를 요약한 것입니다.

3
begin;
4
사용자 값에 삽입( 2, '五条人') ;


격리 수준 dirty read non-repeatable read phantom read concept
커밋된 읽기 트랜잭션의 수정 사항은 커밋되지 않은 경우에도 다른 트랜잭션에 표시됩니다.
커밋되지 않은 읽기
transaction의 수정 사항은 제출된 후에만 다른 거래에 표시됩니다.
반복 읽기

한 거래에서 동일한 기록이 여러 번 쿼리되고 결과는 항상 일관됩니다
Serialized


트랜잭션은 순차적으로 실행되며, 읽으면 읽기 잠금이 추가되고, 쓰면 쓰기 잠금이 추가됩니다

5. MVCC

MVCC(Multi-Version Concurrency Control)即多版本并发控制,这是 MySQL 为了提高数据库并发性能而实现的。它可以在并发读写数据库时,保证不同事务的读-写操作并发执行,同时也能解决脏读、不可重复读、幻读等事务隔离问题。

在前文讨论幻读的时候提到过当前读的概念,正是由于当前读,才会在可重复读的隔离级别下也会发生幻读的情况。

在解释可重复读隔离级别下发生幻读的原因之前,首先介绍 MVCC 的实现原理。

5.1 MVCC 的实现原理

首先我们需要知道,InnoDB 的数据页中每一行的数据是有隐藏字段的:

  • DB_ROW_ID: 隐式主键,若表结构中未定义主键,InnoDB 会自动生成该字段作为表的主键
  • DB_TRX_ID: 事务ID,代表修改此行记录的最后一次事务ID
  • DB_ROLL_PTR: 回滚指针,指向此行记录的上一个版本(上一个事务ID对应的记录)

每一条修改语句都会相应地记录一条回滚语句(undo log),如果把每一条回滚语句视为一条数据表中的记录,那么通过事务ID和回滚指针就可以将对同一行的修改记录看作一个链表,链表上的每一个节点就是一个快照版本,这就是 MVCC 中多版本的意思。

举个例子,假设对 user 表中唯一的一行「刺猬」进行多次修改。

update user set name='重塑' where id=1;update user set name='木马' where id=1;update user set name='达达' where id=1;复制代码

那么这条记录的MySQL 파트 4에 대한 나의 이해: 트랜잭션, 격리 수준 및 MVCC就是:

MySQL 파트 4에 대한 나의 이해: 트랜잭션, 격리 수준 및 MVCC

在这个MySQL 파트 4에 대한 나의 이해: 트랜잭션, 격리 수준 및 MVCC中,头结点就是当前记录的最新版本。DB_TRX_ID 事务ID 字段是非常重要的属性,先 Mark 一下。

除此之外,在读已提交(RC,Read Committed)和可重复读(RR,Repeatable Read)的隔离级别中,事务在启动的时候会创建一个读视图(Read View),用它来记录当前系统的活跃事务信息,通过读视图来进行本事务之间的可见性判断

在读视图中有两个重要的属性:

  • 当前事务ID:表示生成读视图的事务的事务ID
  • 事务ID列表:表示在生成读视图时,当前系统中活跃着的事务ID列表
  • 最小事务ID:表示在生成读视图时,当前系统中活跃着的最小事务ID
  • 下一个事务ID:表示在生成读视图时,系统应该分配给下一个事务的事务ID

需要注意下一个事务I的值,并不是事务ID列表中的最大值+1,而是当前系统中已存在过的事务的最大值+1。例如当前数据库中活跃的事务有(1,2),此时事务2提交,同时又开启了新事务,在生成的读视图中,下一个事务ID的值为3。

我们通过将MySQL 파트 4에 대한 나의 이해: 트랜잭션, 격리 수준 및 MVCC与读视图两者结合起来,来进行并发事务间可见性的判断,判断规则如下(假设现在要判断事务A是否可以访问到事务B的修改记录):

  • 若事务B的当前事务ID小于事务A的最小事务ID的值,代表事务B是在事务A生成读视图之前就已经提交了的,所以事务B对于事务A来说是可见的。
  • 若事务B的当前事务ID大于或等于事务A下一个事务ID的值,代表事务B是在事务A生成读视图之后才开启,所以事务B对于事务A来说是不可见的。
  • 若事务B的当前事务ID在事务A的最小事务ID下一个事务ID之间(左闭右开,[最小事务ID, 下一个事务ID)),需要分两种情况讨论:
    • 若事务B的当前事务ID在事务A的事务ID列表中,代表创建事务A时事务B还是活跃的,未提交,所以事务B对于事务A来说是不可见的。
    • 若事务B的当前事务ID不在事务A的事务ID列表中,代表创建事务A时事务B已经提交,所以事务B对于事务A来说是可见的。

如果事务B对于事务A来说是不可见的,就需要顺着修改记录的MySQL 파트 4에 대한 나의 이해: 트랜잭션, 격리 수준 및 MVCC,从回滚指针开始往前遍历,直到找到第一个对于事务A来说是可见的事务ID,或者遍历完MySQL 파트 4에 대한 나의 이해: 트랜잭션, 격리 수준 및 MVCC也未找到(表示这条记录对事务A不可见)。

这就是 MVCC 的实现原理。

5.2 읽기 뷰 생성 시점

여기서 주목해야 할 것은 읽기 뷰 생성 시점입니다. 위의 논의에서 우리는 이미 트랜잭션이 읽기 뷰(읽기 뷰)를 생성한다는 것을 알고 있습니다. ) 트랜잭션이 시작되고 시작될 때 두 가지 방법이 있습니다. 하나는 트랜잭션 시작/시작이고, 다른 하나는 일관된 스냅샷으로 트랜잭션 시작입니다. start transaction, 읽기 뷰 생성 시점도 다릅니다. :

  • begin/start transaction으로 트랜잭션이 시작되면 읽기 뷰가 생성됩니다. 첫 번째 스냅샷 읽기 문을 실행할 때begin/start transaction,二是start transaction with consistent snapshot,通过这两种方式开启事务,创建读视图的时机也是不同的:
    • 如果是以 begin/start transaction 方式开启事务,读视图会在执行第一个快照读语句时创建
    • 如果以 start transaction with consistent snapshot 方式开启事务,同时便会创建读视图

    5.3 MVCC 的运行过程

    为了详细说明 MVCC 的运行过程,下面举个例子,假设当前存在有两个事务(事务隔离级别为 MySQL 默认的可重复读):

    这里需要注意的是事务的启动时机,在上面的论述中我们已经知道事务在启动时会创建一个读视图(Read View),而开启一个事务有两种方式,一是 begin/start transaction,二是start transaction with consistent snapshot,通过这两种方式开启事务,创建读视图的时机也是不同的:

    • 如果是以 begin/start transaction 方式开启事务,读视图会在执行第一个快照读语句时创建
    • 如果以 start transaction with consistent snapshot
    • >일관된 스냅샷으로 트랜잭션 시작 메서드를 사용하여 트랜잭션을 시작하면 동시에 읽기 뷰가 생성됩니다

    5.3 MVCC 실행 프로세스

    MVCC의 실행 프로세스를 자세히 설명하기 위해 다음과 같이 설명합니다. 예를 들어 현재 두 개의 트랜잭션이 있다고 가정합니다(트랜잭션 격리 수준은 MySQL의 기본 반복 읽기 수준임).
    여기서 주목해야 할 것은 트랜잭션 시작 시간Read View
  • 일관적인 스냅샷으로 트랜잭션 시작으로 트랜잭션이 시작되면 동시에 읽기 보기가 생성됩니다
  • 12일관적인 스냅샷으로 거래 시작; 34id=1;(N1)56에서 이름 선택 사용자의 ID =1;(N2)7
    입니다. 위의 논의에서 우리는 트랜잭션이 시작될 때 를 생성한다는 것을 이미 알고 있으며, 두 가지가 있습니다. 트랜잭션을 시작하는 방법 중 하나는 트랜잭션 시작/시작이고, 다른 하나는 일관된 스냅샷으로 트랜잭션 시작입니다. 이 두 가지 방법을 통해 트랜잭션을 시작하는 방법, 읽기 뷰도 다릅니다:
    • begin/start transaction 메서드로 트랜잭션이 시작되면 첫 번째 스냅샷 읽기 문이 실행될 때 읽기 뷰가 생성됩니다.
    트랜잭션 A 트랜잭션 B

    일관적인 스냅샷으로 거래 시작;

    사용자 세트 이름 업데이트 ='reshape' 여기서 ID =1;

    commit;

    🎜커밋;🎜🎜🎜🎜🎜🎜🎜

    그런 다음 위에서 설명한 버전 체인과 두 트랜잭션이 시작될 때 읽기 보기를 기반으로 MVCC의 실행 프로세스를 분석합니다.

    MySQL 파트 4에 대한 나의 이해: 트랜잭션, 격리 수준 및 MVCC

    위 그림은 두 개의 트랜잭션이 시작되었을 때의 읽기 뷰입니다. 트랜잭션 B의 업데이트 문이 실행되면 행 id=1의 버전 체인은 다음과 같습니다.

    mvcc2-MySQL 파트 4에 대한 나의 이해: 트랜잭션, 격리 수준 및 MVCC

    먼저 N1의 쿼리문을 살펴보겠습니다. 트랜잭션 B의 현재 트랜잭션 ID=2이고 해당 값은 트랜잭션 B의 다음 트랜잭션 ID와 같습니다. 따라서 위에서 논의한 가시성 판단에 따르면 트랜잭션 B는 트랜잭션 A에 보이지 않으며 현재 행의 버전 체인을 따라 온라인으로 검색되어야 합니다. 事务ID=2,其值等于事务A的下一个事务ID,所以按照上文中所论述的可见性判断,事务B对于事务A来说是不可见的,需要循着当前行的MySQL 파트 4에 대한 나의 이해: 트랜잭션, 격리 수준 및 MVCC网上检索。

    于是循着MySQL 파트 4에 대한 나의 이해: 트랜잭션, 격리 수준 및 MVCC来到DB_TRX_ID=1事务ID=1的历史版本,恰巧等于事务A的事务ID值,也就是事务A开启时该行的版本,此版本对于事务A来说当然是可见的,所以读取到了id=1行的name='刺猬',即最终N1=刺猬。

    再来看N2处的查询语句,此时事务B已提交,MySQL 파트 4에 대한 나의 이해: 트랜잭션, 격리 수준 및 MVCC还是如上图所示,由于当前版本的事务ID等于事务A读视图中的下一个事务ID,所以当前版本的记录对于事务A来说是不可见的,所以同样N2=刺猬。

    这里需要注意的是,若例子中事务A的时刻4语句变更为对该行的更新语句,那么事务A便会等待事务B提交之后再执行更新语句,这是因为事务B未提交,即事务B对于id=1行的写锁未释放,而事务A也要更新该行,必须是更新当前的最新版本(当前读)才可以,所以事务A就被阻塞了,必须等待事务B对该行的写锁释放,才会继续执行更新语句。

    5.4 RC 与 RR 生成读视图的时机对比

    上面所讨论的 MVCC 运行过程都是针对可重复读(RR, Repeatable Read)隔离级别的,如果是读已提交(RC, Read Committed)级别呢?

    上文中已经讨论过读已提交隔离级别中关于不可重复读的情况了,这里就不再举例,直接给出结论就可以了。

    • 可重复读(RR, Repeatable Read)隔离级别下生成读视图(Read View)的时机是开启事务的时候
    • 读已提交(RC, Read Committed)隔离级别下生成读视图(Read View)的时机是每一条语句执行前

    对于上文中描述 MVCC 执行过程中的例子,如果隔离级别是读已提交(RC, Read Committed):

    • N1处的查询语句,由于事务B还未提交,事务A可见的版本依旧是事务ID=1的版本,所以N1=刺猬
    • N2处的查询语句,事务B已提交,N2处查询语句执行时也会生成读视图,其当前事务ID=3,而在该记录的MySQL 파트 4에 대한 나의 이해: 트랜잭션, 격리 수준 및 MVCC中,当前版本的事务ID DB_TRX_ID=2
    • 그래서 우리는 버전 체인을 따라 DB_TRX_ID=1 트랜잭션 ID=1의 기록 버전을 따랐습니다. 이는 트랜잭션 A의 트랜잭션 ID 값과 같습니다. 트랜잭션 A가 시작되었습니다. 물론 트랜잭션 A에 표시되므로 id=1행의 name='hedgehog', 즉 최종 N1=hedgehog를 읽습니다.

    N2의 쿼리문을 보면 이때 트랜잭션 B가 제출되었으며, 현재 버전의 트랜잭션 ID가 다음 트랜잭션과 동일하므로 버전 체인은 여전히 ​​​​위 그림과 같습니다. ID는 트랜잭션 A의 읽기 보기에 있으므로 레코드의 현재 버전은 트랜잭션 A에 표시되지 않으므로 N2 = 고슴도치입니다.

      여기서 주목해야 할 점은 예제에서 트랜잭션 A의 time 4 문이 해당 행에 대한 업데이트 문으로 변경되면 트랜잭션 A는 업데이트 문을 실행하기 전에 트랜잭션 B가 커밋될 때까지 기다린다는 것입니다. 이는 트랜잭션 B가 아직 제출하지 않았기 때문입니다. 즉, 트랜잭션 B의 행 id=1에 대한 쓰기 잠금이 해제되지 않았고 트랜잭션 A도 행을 업데이트해야 하며 이를 최신 버전(현재 읽기)으로 업데이트해야 하기 때문입니다. 따라서 트랜잭션 A는 차단되고 업데이트 문이 계속 실행되기 전에 해당 행에 대한 트랜잭션 B의 쓰기 잠금이 해제될 때까지 기다려야 합니다.

    5.4 RC와 RR 간 읽기 뷰 생성 타이밍 비교위에서 설명한 MVCC 실행 프로세스는 모두 Repeatable Read(RR, Repeatable Read)에 대한 것입니다. 격리 수준 예. Read Committed(RC, Read Committed) 수준이면 어떻게 되나요?

  • 읽기 커밋 격리 수준에서 반복 불가능한 읽기에 관한 상황은 위에서 논의되었습니다. 여기서는 예제를 제공하지 않고 직접 결론을 제시합니다.
  • 반복 읽기(RR, Repeatable Read) 격리 수준에서 읽기 보기(Read View)가 생성되는 시간은 트랜잭션이 시작될 때입니다.

읽기 보기(Read View)는 Read Committed(RC, Read Committed) 격리 수준 타이밍은 각 문이 실행되기 전입니다

위에서 설명한 MVCC 실행 프로세스의 예에서 격리 수준이 읽기 커밋(RC, Read Committed)인 경우:N1의 쿼리 문, 트랜잭션 B가 아직 제출되지 않았기 때문에 트랜잭션 A의 표시 가능한 버전은 여전히 ​​트랜잭션 ID=1인 버전이므로 N1=HedgehogN2의 쿼리 문, 트랜잭션 B가 제출되었으며 읽기 보기도 생성됩니다. N2의 쿼리 문이 실행될 때 현재 트랜잭션 ID는 3입니다. 레코드의 버전 체인에서 현재 버전의 트랜잭션 ID DB_TRX_ID=2는 레코드의 트랜잭션 ID 앞에 표시됩니다. N2 쿼리 문이므로 N2 = Reshape Current Read: 레코드의 최신 버전 읽기: 레코드를 읽을 때 트랜잭션 표시 버전 특정 규칙에 따라 레코드를 읽습니다5.6 반복 읽기에서 팬텀 읽기가 발생합니다. 이유 MVCC를 이해한 후 반복 읽기 격리 수준에서 팬텀 읽기가 발생하는 이유를 살펴보겠습니다. 위에서 언급했듯이 반복 읽기의 격리 수준에서 팬텀 읽기가 발생하는 것은 바로 현재 읽기 때문입니다. 먼저 예제를 검토해 보겠습니다. ( 1) 345 사용자 이름 선택;(N2)업데이트할 사용자 이름 선택;(N3)커밋;
5.5 현재 읽기(스냅샷 읽기 포함)
Snapshot Read

begin;
사용자 값에 삽입(2, '五条人');
커밋;
6

7

8

N1과 N2의 쿼리는 모두 "고슴도치"라는 것이 매우 분명했을 것입니다. N3에서 사용된 쿼리 문은 for update입니다. 이를 쿼리에 사용하면 대상 레코드에 "행 수준 잠금"이 추가됩니다. 이제 행 수준 잠금의 의미에 대해 설명하겠습니다. 업데이트만 알면 대상 레코드를 잠글 수 있습니다. for update,使用它进行查询就会对目标记录添加一把「行级锁」,行级锁的意义以后再说,现在只需要知道for update能够锁住目标记录就可以了。

加锁自然是防止别人修改,那么理所当然,锁住的当然也就是记录的最新版本了。所以,在使用for update进行查询的时候,会使用当前读

잠금은 당연히 다른 사람이 수정하지 못하도록 하기 위한 것이므로, 당연히 잠긴 것은 최신 버전의 기록입니다. 따라서 for update를 사용하여 쿼리할 때 현재 읽기는 대상 레코드의 최신 버전을 읽는 데 사용되므로 N3의 쿼리 문은 트랜잭션 B 레코드를 넣습니다. 트랜잭션 A에 표시되지 않는 항목도 쿼리되고 팬텀 읽기가 발생합니다.

현재 읽기 문은 다음과 같습니다.
  • select ... for update
  • select ... 공유 모드에서 잠금(공유 읽기 잠금)
  • update ...
  • insert ...
  • delete ... .

6. 과거를 검토하고 새로운 것을 배웁니다
  1. 트랜잭션의 네 가지 주요 ACID 특성
  2. 자동 제출 및 암시적 제출(암시적 커밋 문)
  3. 트랜잭션 격리 수준
  4. 각 격리에서 발생할 수 있는 문제 level of transaction
  5. MVCC
  6. RC 및 RR 읽기 보기 생성 타이밍

구현 원리 및 프로세스 관련 무료 학습 권장사항: mysql tutorial(동영상)

🎜

위 내용은 MySQL 파트 4에 대한 나의 이해: 트랜잭션, 격리 수준 및 MVCC의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 juejin.im에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제