mysql 트랜잭션이 뭐야?

藏色散人
풀어 주다: 2023-04-04 10:47:33
원래의
5064명이 탐색했습니다.

Mysql 트랜잭션은 동일한 트랜잭션에서 일괄 작업을 수행하는 것을 의미하며, 이러한 작업은 결국 모두 성공하거나 실패하며 트랜잭션은 원자적 작업이며 최소 실행 단위입니다. 하나 이상의 SQL 문으로 구성될 수 있습니다.

mysql 트랜잭션이 뭐야?

이 튜토리얼의 운영 환경: Windows 10 시스템, MySQL 버전 5.7, Dell G3 컴퓨터.

거래에 대한 자세한 설명

거래란 무엇인가요?

데이터베이스의 트랜잭션은 데이터베이스에서 일괄 작업을 수행하는 것을 의미합니다. 동일한 트랜잭션에서 이러한 작업은 모두 성공하거나 부분적으로 성공하지 않습니다.

  • 트랜잭션은 원자적 작업입니다. 최소 실행 단위입니다. 하나 이상의 SQL 문으로 구성될 수 있습니다
  • 동일한 트랜잭션에서 모든 SQL 문이 성공적으로 실행되면 전체 트랜잭션이 성공합니다.

예:

예를 들어 사용자 A가 사용자 B에게 100을 이체합니다. 프로세스는 다음과 같습니다.

  1. 계정 A에서 100을 공제
  2. 계정 B에 100을 추가

거래 지원을 통해 위의 결과는 2개뿐입니다.

  1. 작업 성공: 계정 A가 100만큼 감소하고 계정 B가 100만큼 증가
  2. 작업 실패: 계정 A와 B 모두에 변화가 없습니다

거래를 지원하지 않는 경우 오류가 발생할 수 있습니다. A 계좌가 100개 추가되었는데 시스템이 다운되었기 때문에 B 계좌에는 100개가 추가되지 않았고 A 계좌는 허공에서 100이 손실되었습니다.

트랜잭션의 여러 특성(ACID) - 핵심 포인트

원자성(Atomicity)

트랜잭션의 전체 프로세스는 결국 원자성 작업과 같습니다. 이 원자성은 결국 모두 성공하거나 모두 실패합니다. 최종 결과에 따라 결정됩니다. 최종 결과의 관점에서 볼 때 이 프로세스는 분리될 수 없습니다.

Consistency

트랜잭션은 데이터베이스를 하나의 일관성 상태에서 다른 일관성 상태로 변경해야 합니다.

먼저 일관성의 정의를 살펴보겠습니다. 소위 일관성이란 데이터가 문법적 이 아닌 의미적 인 의미 있는 상태에 있음을 의미합니다. 가장 일반적인 예는 자금 이체입니다. 예를 들어, A계좌에서 B계좌로 금액을 이체했는데, A계좌의 자금은 감소하는데 B계좌의 자금은 증가하지 않는다면, 이때는 데이터가 일관성이 없는 상태라고 생각합니다.

이 단락의 이해에 따르면 소위 일관성이란 실제 비즈니스 논리에서 최종 결과가 정확하고 프로그래머의 예상 결과와 완전히 일치함을 의미합니다.

Isolation Isolation

트랜잭션 실행 다른 거래에 의해 방해받을 수 없습니다. 즉, 트랜잭션 내에서 사용되는 작업과 데이터는 다른 동시 트랜잭션과 격리되며, 동시에 실행되는 트랜잭션은 서로 간섭할 수 없습니다.

  • 트랜잭션의 격리 수준은 다음과 같습니다.
    • 커밋되지 않은 읽기: 커밋되지 않은 읽기
    • 커밋된 읽기: 커밋된 읽기
    • 반복 읽기: 반복 가능한 읽기
    • 직렬화 가능: 직렬화 가능

내구성

한 번은 트랜잭션이 커밋되면 데이터베이스의 데이터에 대한 변경 사항은 영구적이어야 합니다. 트랜잭션이 커밋되면 데이터는 하드 디스크에 유지되고 수정 사항은 영구적으로 유지됩니다.

Mysql의 트랜잭션 작업

MySQL의 트랜잭션은 기본적으로 암시적 트랜잭션입니다. 삽입, 업데이트 및 삭제 작업이 수행되면 데이터베이스가 자동으로 트랜잭션을 시작하고 트랜잭션을 커밋하거나 롤백합니다.

암시적 트랜잭션 활성화 여부는 autocommit 변수에 의해 제어됩니다.

그래서 트랜잭션은 암시적 트랜잭션명시적 트랜잭션으로 구분됩니다.

암시적 트랜잭션

삽입, 업데이트, 삭제 문과 같은 트랜잭션은 자동으로 열리거나 제출되거나 롤백됩니다. 트랜잭션의 열림, 제출 또는 롤백은 mysql에 의해 내부적으로 자동으로 제어됩니다.

자동 제출을 위해 변수 autocommit이 켜져 있는지 확인하세요.

mysql> show variables like 'autocommit';+---------------+-------+| Variable_name | Value |+---------------+-------+| autocommit   | ON   |+---------------+-------+1 row in set, 1 warning (0.00 sec)
로그인 후 복사

autocommit이 ON이라는 것은 자동 제출이 켜져 있다는 뜻입니다.

명시적 트랜잭션

트랜잭션은 수동으로 열고 제출하거나 롤백해야 하며 개발자가 직접 제어합니다.

트랜잭션을 수동으로 제어하는 ​​2가지 방법:

방법 1:

구문:

//设置不自动提交事务set autocommit=0;//执行事务操作commit|rollback;
로그인 후 복사

예 1: 다음과 같이 트랜잭션 작업 제출:

mysql> create table test1 (a int);Query OK, 0 rows affected (0.01 sec)mysql> select * from test1;Empty set (0.00 sec)mysql> set autocommit=0;Query OK, 0 rows affected (0.00 sec)mysql> insert into test1 values(1);Query OK, 1 row affected (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)mysql> select * from test1;+------+| a   |+------+|   1 |+------+1 row in set (0.00 sec)
로그인 후 복사

예 2: 다음과 같이 트랜잭션 작업 롤백:

mysql> set autocommit=0;Query OK, 0 rows affected (0.00 sec)mysql> insert into test1 values(2);Query OK, 1 row affected (0.00 sec)mysql> rollback;Query OK, 0 rows affected (0.00 sec)mysql> select * from test1;+------+| a   |+------+|   1 |+------+1 row in set (0.00 sec)
로그인 후 복사

위의 데이터가 롤백된 것을 볼 수 있습니다.

자동 커밋을 다시 복원합니다:

mysql> set autocommit=1;Query OK, 0 rows affected (0.00 sec)
로그인 후 복사

방법 2:

구문:

start transaction;//开启事务//执行事务操作commit|rollback;
로그인 후 복사

예 1: 다음과 같이 트랜잭션 작업 제출:

mysql> select * from test1;+------+| a   |+------+|   1 |+------+1 row in set (0.00 sec)mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> insert into test1 values (2);Query OK, 1 row affected (0.00 sec)mysql> insert into test1 values (3);Query OK, 1 row affected (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)mysql> select * from test1;+------+| a   |+------+|   1 ||   2 ||   3 |+------+3 rows in set (0.00 sec)
로그인 후 복사

上面成功插入了2条数据。

示例2:回滚事务操作,如下:

mysql> select * from test1;+------+| a   |+------+|   1 ||   2 ||   3 |+------+3 rows in set (0.00 sec)mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> delete from test1;Query OK, 3 rows affected (0.00 sec)mysql> rollback;Query OK, 0 rows affected (0.00 sec)mysql> select * from test1;+------+| a   |+------+|   1 ||   2 ||   3 |+------+3 rows in set (0.00 sec)
로그인 후 복사

上面事务中我们删除了test1的数据,显示删除了3行,最后回滚了事务。

savepoint关键字

在事务中我们执行了一大批操作,可能我们只想回滚部分数据,怎么做呢?

我们可以将一大批操作分为几个部分,然后指定回滚某个部分。可以使用savepoin来实现,效果如下:

先清除test1表数据:

mysql> delete from test1;Query OK, 3 rows affected (0.00 sec)mysql> select * from test1;Empty set (0.00 sec)
로그인 후 복사

演示savepoint效果,认真看:

mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> insert into test1 values (1);Query OK, 1 row affected (0.00 sec)mysql> savepoint part1;//设置一个保存点Query OK, 0 rows affected (0.00 sec)mysql> insert into test1 values (2);Query OK, 1 row affected (0.00 sec)mysql> rollback to part1;//将savepint = part1的语句到当前语句之间所有的操作回滚Query OK, 0 rows affected (0.00 sec)mysql> commit;//提交事务Query OK, 0 rows affected (0.00 sec)mysql> select * from test1;+------+| a   |+------+|   1 |+------+1 row in set (0.00 sec)
로그인 후 복사

从上面可以看出,执行了2次插入操作,最后只插入了1条数据。

savepoint需要结合rollback to sp1一起使用,可以将保存点sp1到rollback to之间的操作回滚掉。

只读事务

表示在事务中执行的是一些只读操作,如查询,但是不会做insert、update、delete操作,数据库内部对只读事务可能会有一些性能上的优化。

用法如下:

start transaction read only;
로그인 후 복사

示例:

mysql> commit;Query OK, 0 rows affected (0.00 sec)mysql> start transaction read only;Query OK, 0 rows affected (0.00 sec)mysql> select * from test1;+------+| a   |+------+|   1 ||   1 |+------+2 rows in set (0.00 sec)mysql> delete from test1;ERROR 1792 (25006): Cannot execute statement in a READ ONLY transaction.mysql> commit;Query OK, 0 rows affected (0.00 sec)mysql> select * from test1;+------+| a   |+------+|   1 ||   1 |+------+2 rows in set (0.00 sec)
로그인 후 복사

只读事务中执行delete会报错。

事务中的一些问题(重点)

这些问题主要是基于数据在多个事务中的可见性来说的。也是并发事务产生的问题。

更新丢失

丢失更新就是两个不同的事务(或者Java程序线程)在某一时刻对同一数据进行读取后,先后进行修改。导致第一次操作数据丢失。

第一类丢失更新 :A,B 事务同时操作同一数据,A先对改数据进行了更改,B再次更改时失败然后回滚,把A更新的数据也回滚了。(事务撤销造成的撤销丢失)

第二类丢失更新:A,B 事务同时操作同一数据,A先对改数据进行了更改,B再次更改并且提交,把A提交的数据给覆盖了。(事务提交造成的覆盖丢失)

脏读

一个事务在执行的过程中读取到了其他事务还没有提交的数据。 这个还是比较好理解的。

两个事务同时操作同一数据,A事务对该数据进行了修改还没提交的时候,B事务访问了该条事务,并且使用了该数据,此时A事务回滚,那么B事务读到的就是脏数据。

比如事务1,修改了某个数据 事务2,刚好访问了事务1修改后的数据

此时事务1,回滚了操作 事务2,读到还是回滚前的数据

读已提交

从字面上我们就可以理解,即一个事务操作过程中可以读取到其他事务已经提交的数据。

事务中的每次读取操作,读取到的都是数据库中其他事务已提交的最新的数据(相当于当前读)

不可重复读

在同一事务中,多次读取同一数据返回的结果有所不同,换句话说,后续读取可以读到另一事务已提交的更新数据。相反,“可重复读” 在同一事务中多次读取数据时, 能够保证所读数据一样, 也就是后续读取不能读到另一事务已提交的更新数据。

这种情况发生 在一个事务内多次读同一数据。A事务查询某条数据,该事务未结束时,B事务也访问同一数据并进行了修改。那么在A事务中的两 次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。

事务1,查询某个数据 事务2,修改了某个数据,提交

事务1,再次查询这个数据

这样事务1两次查询的数据不一样,称为不可重复读

可重复读

一个事务操作中对于一个读取操作不管多少次,读取到的结果都是一样的。

幻读

脏读、不可重复读、可重复读、幻读,其中最难理解的是幻读

以mysql为例:

  • 幻读现象例子:

    • 可重复读模式下,比如有个用户表,手机号码为主键,有两个事物进行如下操作
    • 事务A操作如下: 1、打开事务 2、查询号码为X的记录,不存在 3、插入号码为X的数据,插入报错(为什么会报错,先向下看) 4、查询号码为X的记录,发现还是不存在(由于是可重复读,所以读取记录X还是不存在的)
    • 事物B操作:在事务A第2步操作时插入了一条X的记录,所以会导致A中第3步插入报错(违反了唯一约束)
    • 上面操作对A来说就像发生了幻觉一样,明明查询X(A中第二步、第四步)不存在,但却无法插入成功
    • 幻读可以这么理解:事务中后面的操作(插入号码X)需要上面的读取操作(查询号码X的记录)提供支持,但读取操作却不能支持下面的操作时产生的错误,就像发生了幻觉一样。
  • 看第二种解释:

    • 事务A在操作一堆数据的时候,事务B插入了一条数据,A事务再次(第二次)查询,发现多了一条数据,像是幻觉。与不可重复读类似,不同的是一个是修改删除操作,一个是新增操作。

如果还是理解不了的,继续向下看,后面后详细的演示。

事务的隔离级别

当多个事务同时进行的时候,如何确保当前事务中数据的正确性,比如A、B两个事物同时进行的时候,A是否可以看到B已提交的数据或者B未提交的数据,这个需要依靠事务的隔离级别来保证,不同的隔离级别中所产生的效果是不一样的。

事务隔离级别主要是解决了上面多个事务之间数据可见性及数据正确性的问题。(或者说为了解决并发控制可能产生的异常问题,数据库定义了四种事务的隔离级别)

隔离级别分为4种:

  1. 读未提交:READ-UNCOMMITTED
  2. 读已提交:READ-COMMITTED
  3. 可重复读:REPEATABLE-READ
  4. 串行:SERIALIZABLE

上面4中隔离级别越来越强,会导致数据库的并发性也越来越低。

查看隔离级别

mysql> show variables like 'transaction_isolation';+-----------------------+----------------+| Variable_name     | Value      |+-----------------------+----------------+| transaction_isolation | READ-COMMITTED |+-----------------------+----------------+1 row in set, 1 warning (0.00 sec)
로그인 후 복사
로그인 후 복사

隔离级别的设置

分2步骤,修改文件、重启mysql,如下:

修改mysql中的my.init文件,我们将隔离级别设置为:READ-UNCOMMITTED,如下:

# 隔离级别设置,READ-UNCOMMITTED读未提交,READ-COMMITTED读已提交,REPEATABLE-READ可重复读,SERIALIZABLE串行transaction-isolation=READ-UNCOMMITTED
로그인 후 복사
로그인 후 복사

以管理员身份打开cmd窗口,重启mysql,如下:

C:\Windows\system32>net stop mysql
mysql 服务正在停止..mysql 服务已成功停止。

C:\Windows\system32>net start mysql
mysql 服务正在启动 .mysql 服务已经启动成功。
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

各种隔离级别中会出现的问题

隔离级别 脏读可能性 不可重复读可能性 幻读可能性
READ-UNCOMMITTED
READ-COMMITTED
REPEATABLE-READ
SERIALIZABLE

下面我们来演示一下,各种隔离级别中可见性的问题,开启两个窗口,叫做A、B窗口,两个窗口中登录mysql。

READ-UNCOMMITTED:读未提交

将隔离级别置为READ-UNCOMMITTED:

# 隔离级别设置,READ-UNCOMMITTED读未提交,READ-COMMITTED读已提交,REPEATABLE-READ可重复读,SERIALIZABLE串行transaction-isolation=READ-UNCOMMITTED
로그인 후 복사
로그인 후 복사

重启mysql:

C:\Windows\system32>net stop mysql
mysql 服务正在停止..mysql 服务已成功停止。

C:\Windows\system32>net start mysql
mysql 服务正在启动 .mysql 服务已经启动成功。
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

查看隔离级别:

mysql> show variables like 'transaction_isolation';+-----------------------+----------------+| Variable_name     | Value      |+-----------------------+----------------+| transaction_isolation | READ-UNCOMMITTED |+-----------------------+----------------+1 row in set, 1 warning (0.00 sec)
로그인 후 복사

先清空test1表数据:

delete from test1;select * from test1;
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

按时间顺序在2个窗口中执行下面操作:

时间 窗口A 窗口B
T1 start transaction;
T2 select * from test1;
T3
start transaction;
T4
insert into test1 values (1);
T5
select * from test1;
T6 select * from test1;
T7
commit;
T8 commit;

A窗口如下:

mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> select * from test1;Empty set (0.00 sec)mysql> select * from test1;+------+| a   |+------+|   1 |+------+1 row in set (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)
로그인 후 복사

B窗口如下:

mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> insert into test1 values (1);Query OK, 1 row affected (0.00 sec)mysql> select * from test1;+------+| a   |+------+|   1 |+------+1 row in set (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)
로그인 후 복사
로그인 후 복사

看一下:

T2-A:无数据,T6-A:有数据,T6时刻B还未提交,此时A已经看到了B插入的数据,说明出现了脏读

T2-A:无数据,T6-A:有数据,查询到的结果不一样,说明不可重复读

结论:读未提交情况下,可以读取到其他事务还未提交的数据,多次读取结果不一样,出现了脏读、不可重复读、幻读

READ-COMMITTED:读已提交

将隔离级别置为READ-COMMITTED

# 隔离级别设置,READ-UNCOMMITTED读未提交,READ-COMMITTED读已提交,REPEATABLE-READ可重复读,SERIALIZABLE串行transaction-isolation=READ-COMMITTED
로그인 후 복사

重启mysql:

C:\Windows\system32>net stop mysql
mysql 服务正在停止..mysql 服务已成功停止。

C:\Windows\system32>net start mysql
mysql 服务正在启动 .mysql 服务已经启动成功。
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

查看隔离级别:

mysql> show variables like 'transaction_isolation';+-----------------------+----------------+| Variable_name     | Value      |+-----------------------+----------------+| transaction_isolation | READ-COMMITTED |+-----------------------+----------------+1 row in set, 1 warning (0.00 sec)
로그인 후 복사
로그인 후 복사

先清空test1表数据:

delete from test1;select * from test1;
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

按时间顺序在2个窗口中执行下面操作:

时间 窗口A 窗口B
T1 start transaction;
T2 select * from test1;
T3
start transaction;
T4
insert into test1 values (1);
T5
select * from test1;
T6 select * from test1;
T7
commit;
T8 select * from test1;
T9 commit;

A窗口如下:

mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> select * from test1;Empty set (0.00 sec)mysql> select * from test1;Empty set (0.00 sec)mysql> select * from test1;+------+| a   |+------+|   1 |+------+1 row in set (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)
로그인 후 복사

B窗口如下:

mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> insert into test1 values (1);Query OK, 1 row affected (0.00 sec)mysql> select * from test1;+------+| a   |+------+|   1 |+------+1 row in set (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)
로그인 후 복사
로그인 후 복사

看一下:

T5-B:有数据,T6-A窗口:无数据,A看不到B的数据,说明没有脏读

T6-A窗口:无数据,T8-A:看到了B插入的数据,此时B已经提交了,A看到了B已提交的数据,说明可以读取到已提交的数据

T2-A、T6-A:无数据,T8-A:有数据,多次读取结果不一样,说明不可重复读

结论:读已提交情况下,无法读取到其他事务还未提交的数据,可以读取到其他事务已经提交的数据,多次读取结果不一样,未出现脏读,出现了读已提交、不可重复读、幻读

REPEATABLE-READ:可重复读

将隔离级别置为REPEATABLE-READ

# 隔离级别设置,READ-UNCOMMITTED读未提交,READ-COMMITTED读已提交,REPEATABLE-READ可重复读,SERIALIZABLE串行transaction-isolation=REPEATABLE-READ
로그인 후 복사
로그인 후 복사

重启mysql:

C:\Windows\system32>net stop mysql
mysql 服务正在停止..mysql 服务已成功停止。

C:\Windows\system32>net start mysql
mysql 服务正在启动 .mysql 服务已经启动成功。
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

查看隔离级别:

mysql> show variables like 'transaction_isolation';+-----------------------+----------------+| Variable_name     | Value      |+-----------------------+----------------+| transaction_isolation | REPEATABLE-READ |+-----------------------+----------------+1 row in set, 1 warning (0.00 sec)
로그인 후 복사
로그인 후 복사

先清空test1表数据:

delete from test1;select * from test1;
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

按时间顺序在2个窗口中执行下面操作:

时间 窗口A 窗口B
T1 start transaction;
T2 select * from test1;
T3
start transaction;
T4
insert into test1 values (1);
T5
select * from test1;
T6 select * from test1;
T7
commit;
T8 select * from test1;
T9 commit;
T10 select * from test1;

A窗口如下:

mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> select * from test1;Empty set (0.00 sec)mysql> select * from test1;Empty set (0.00 sec)mysql> select * from test1;Empty set (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)mysql> select * from test1;+------+| a   |+------+|   1 ||   1 |+------+2 rows in set (0.00 sec)
로그인 후 복사

B窗口如下:

mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> insert into test1 values (1);Query OK, 1 row affected (0.00 sec)mysql> select * from test1;+------+| a   |+------+|   1 ||   1 |+------+2 rows in set (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)
로그인 후 복사

看一下:

T2-A、T6-A窗口:无数据,T5-B:有数据,A看不到B的数据,说明没有脏读

T8-A:无数据,此时B已经提交了,A看不到B已提交的数据,A中3次读的结果一样都是没有数据的,说明可重复读

结论:可重复读情况下,未出现脏读,未读取到其他事务已提交的数据,多次读取结果一致,即可重复读。

幻读演示

将隔离级别置为REPEATABLE-READ

# 隔离级别设置,READ-UNCOMMITTED读未提交,READ-COMMITTED读已提交,REPEATABLE-READ可重复读,SERIALIZABLE串行transaction-isolation=REPEATABLE-READ
로그인 후 복사
로그인 후 복사

重启mysql:

C:\Windows\system32>net stop mysql
mysql 服务正在停止..mysql 服务已成功停止。

C:\Windows\system32>net start mysql
mysql 服务正在启动 .mysql 服务已经启动成功。
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

查看隔离级别:

mysql> show variables like 'transaction_isolation';+-----------------------+----------------+| Variable_name     | Value      |+-----------------------+----------------+| transaction_isolation | REPEATABLE-READ |+-----------------------+----------------+1 row in set, 1 warning (0.00 sec)
로그인 후 복사
로그인 후 복사

准备数据:

mysql> create table t_user(id int primary key,name varchar(16) unique key);Query OK, 0 rows affected (0.01 sec)mysql> insert into t_user values (1,'路人甲Java'),(2,'路人甲Java');ERROR 1062 (23000): Duplicate entry '路人甲Java' ***\*for\**** key 'name'mysql> select * from t_user;Empty set (0.00 sec)
로그인 후 복사

上面我们创建t_user表,name添加了唯一约束,表示name不能重复,否则报错。

按时间顺序在2个窗口中执行下面操作:

时间 窗口A 窗口B
T1 start transaction;
T2
start transaction;
T3
– 插入路人甲Java
insert into t_user values (1,‘路人甲Java’);
T4
select * from t_user;
T5 – 查看路人甲Java是否存在
select * from t_user where name=‘路人甲Java’;

T6
commit;
T7 – 插入路人甲Java
insert into t_user values (2,‘路人甲Java’);

T8 – 查看路人甲Java是否存在
select * from t_user where name=‘路人甲Java’;

T9 commit;

A窗口如下:

mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> select * from t_user where name='路人甲Java';Empty set (0.00 sec)mysql> insert into t_user values (2,'路人甲Java');ERROR 1062 (23000): Duplicate entry '路人甲Java' ***\*for\**** key 'name'mysql> select * from t_user where name='路人甲Java';Empty set (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)
로그인 후 복사

B窗口如下:

mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> insert into t_user values (1,'路人甲Java');Query OK, 1 row affected (0.00 sec)mysql> select * from t_user;+----+---------------+| id | name      |+----+---------------+|  1 | 路人甲Java   |+----+---------------+1 row in set (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)
로그인 후 복사

看一下:

A想插入数据路人甲Java,插入之前先查询了一下(T5时刻)该用户是否存在,发现不存在,然后在T7时刻执行插入,报错了,报数据已经存在了,因为T6时刻B已经插入了路人甲Java。

然后A有点郁闷,刚才查的时候不存在的,然后A不相信自己的眼睛,又去查一次(T8时刻),发现路人甲Java还是不存在的。

此时A心里想:数据明明不存在啊,为什么无法插入呢?这不是懵逼了么,A觉得如同发生了幻觉一样。

SERIALIZABLE:串行

SERIALIZABLE会让并发的事务串行执行(多个事务之间读写、写读、写写会产生互斥,效果就是串行执行,多个事务之间的读读不会产生互斥)。

读写互斥:事务A中先读取操作,事务B发起写入操作,事务A中的读取会导致事务B中的写入处于等待状态,直到A事务完成为止。

表示我开启一个事务,为了保证事务中不会出现上面说的问题(脏读、不可重复读、读已提交、幻读),那么我读取的时候,其他事务有修改数据的操作需要排队等待,等待我读取完成之后,他们才可以继续。

写读、写写也是互斥的,读写互斥类似。

这个类似于java中的java.util.concurrent.lock.ReentrantReadWriteLock类产生的效果。

下面演示读写互斥的效果。

将隔离级别置为SERIALIZABLE

# 隔离级别设置,READ-UNCOMMITTED读未提交,READ-COMMITTED读已提交,REPEATABLE-READ可重复读,SERIALIZABLE串行transaction-isolation=SERIALIZABLE
로그인 후 복사

重启mysql:

C:\Windows\system32>net stop mysql
mysql 服务正在停止..mysql 服务已成功停止。

C:\Windows\system32>net start mysql
mysql 服务正在启动 .mysql 服务已经启动成功。
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

查看隔离级别:

mysql> show variables like 'transaction_isolation';+-----------------------+--------------+| Variable_name     | Value     |+-----------------------+--------------+| transaction_isolation | SERIALIZABLE |+-----------------------+--------------+1 row in set, 1 warning (0.00 sec)
로그인 후 복사

先清空test1表数据:

delete from test1;select * from test1;
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

按时间顺序在2个窗口中执行下面操作:

时间 窗口A 窗口B
T1 start transaction;
T2 select * from test1;
T3
start transaction;
T4
insert into test1 values (1);
T5 commit;
T6
commit;

위 명령을 시간순으로 실행하면 T5-A가 완료될 때까지 T4-B가 차단되는 것을 확인할 수 있습니다.

위 시연은 읽기-쓰기 상호 배제 효과입니다. 쓰기-읽기 및 쓰기-쓰기 상호 배제 효과에 대해 직접 써보실 수 있습니다.

트랜잭션은 순차적으로만 실행될 수 있음을 알 수 있습니다. 직렬 상황에서는 더티 읽기, 반복 불가능 읽기 및 팬텀 읽기에 문제가 없습니다.

Summary

  • Read Uncommitted(Read Uncommitted)

    • Read Uncommitted는 가장 낮은 격리 수준 트랜잭션 수준입니다. 이 격리 수준에서는 한 트랜잭션이 다른 트랜잭션에서 업데이트되었지만 커밋되지 않은 데이터를 읽습니다. 다른 트랜잭션이 롤백되면 현재 트랜잭션에서 읽은 데이터를 더티 읽기(dirty read)라고 합니다.
  • Read Committed

    • Read Committed 격리 수준에서 트랜잭션은 Non Repeatable Read 문제에 직면할 수 있습니다. 반복 불가능 읽기는 트랜잭션 내에서 동일한 데이터를 여러 번 읽는 것을 의미합니다. 트랜잭션이 끝나기 전에 다른 트랜잭션이 이 데이터를 수정하는 경우 첫 번째 트랜잭션에서 데이터가 두 번 읽혀 일관성이 없을 수 있습니다.
  • 반복 읽기(Repeatable Read)

    • 반복 읽기 격리 수준에서 트랜잭션은 팬텀 읽기(Phantom Read) 문제가 발생할 수 있습니다. 팬텀 읽기란 트랜잭션에서 특정 레코드를 처음 쿼리했을 때 존재하지 않는 것을 발견했지만, 존재하지 않는 이 레코드를 업데이트하려고 하면 성공할 수 있고, 동일한 레코드를 다시 읽으면 성공한다는 의미입니다. , 마술처럼 작동합니다. 환상 읽기는 읽은 적이 없고 존재하지 않는 것으로 생각되는 기록이지만 실제로는 성공적으로 업데이트할 수 있습니다. 게다가 업데이트에 성공한 후 다시 읽으면 나타납니다.
  • 직렬화 가능

    • 직렬화 가능은 가장 엄격한 격리 수준입니다. 직렬화 가능 격리 수준에서는 모든 트랜잭션이 순서대로 실행되므로 더티 읽기, 반복 불가능 읽기 및 팬텀 읽기가 발생하지 않습니다.
    • 직렬화 가능 격리 수준의 트랜잭션은 보안이 가장 높지만 트랜잭션이 직렬로 실행되기 때문에 효율성이 크게 떨어지고 애플리케이션의 성능이 크게 저하됩니다. 특별히 중요한 상황이 아닌 이상 직렬화 가능 격리 수준은 일반적으로 사용되지 않습니다.

기본 격리 수준: 격리 수준이 지정되지 않으면 데이터베이스는 기본 격리 수준을 사용합니다. MySQL에서 InnoDB를 사용하는 경우 기본 격리 수준은 반복 읽기입니다.

격리 수준 선택에 대해

  1. 다양한 격리 수준으로 인해 발생하는 현상을 잘 이해하고 있어야 쉽게 선택할 수 있습니다
  2. 격리 수준이 높을수록 격리 수준이 낮아집니다. 예를 들어, 최고 수준의 SERIALIZABLE은 직렬 실행, 동시 작업을 직렬화하여 시스템 성능을 직접적으로 저하시킵니다.
  3. 구체적인 선택은 특정 비즈니스를 기반으로 해야 합니다.
  4. READ-COMMITTED는 일반적으로 더 자주 사용됩니다.

요약

  1. 트랜잭션의 4가지 특성인 원자성, 일관성, 격리성 및 내구성을 이해하세요.
  2. 트랜잭션 작업을 위한 일반적인 명령 소개를 숙지하세요
  3. set autocommit은 자동 트랜잭션 제출을 켤지 여부를 설정할 수 있습니다
  4. 트랜잭션 시작: 트랜잭션 시작
  5. 트랜잭션 시작 읽기 전용: 읽기 전용 트랜잭션 열기
  6. 커밋: 트랜잭션 커밋
  7. 롤백: 트랜잭션 롤백
  8. savepoint: 저장 지점 설정
  9. 저장 지점으로 롤백: 저장 위치로 롤백할 수 있습니다. point
  10. 4가지 격리 수준을 마스터하고 그 특성을 이해하세요
  11. 더티 읽기, 반복 불가능 읽기, 팬텀 읽기

[관련 권장 사항: mysql 비디오 튜토리얼]

위 내용은 mysql 트랜잭션이 뭐야?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!