共计 15010 个字符,预计需要花费 38 分钟才能阅读完成。
这篇文章主要介绍了 mysql 事务的含义是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇 mysql 事务的含义是什么文章都会有所收获,下面我们一起来看看吧。
mysql 事务是指对数据库执行一批操作,在同一个事务当中,这些操作最终要么全部执行成功,要么全部失败,不会存在部分成功的情况;事务是一个原子操作,是一个最小执行单元,可以由一个或多个 SQL 语句组成。
什么是事务?
数据库中的事务是指对数据库执行一批操作,在同一个事务当中,这些操作最终要么全部执行成功,要么全部失败,不会存在部分成功的情况。
事务是一个原子操作。是一个最小执行单元。可以由一个或多个 SQL 语句组成
在同一个事务当中,所有的 SQL 语句都成功执行时,整 个事务成功,有一个 SQL 语句执行失败,整个事务都执行失败。
举个例子:
比如 A 用户给 B 用户转账 100 操作,过程如下:
从 A 账户扣 100
给 B 账户加 100
如果在事务的支持下,上面最终只有 2 种结果:
操作成功:A 账户减少 100;B 账户增加 100
操作失败:A、B 两个账户都没有发生变化
如果没有事务的支持,可能出现错:A 账户减少了 100,此时系统挂了,导致 B 账户没有加上 100,而 A 账户凭空少了 100。
事务的几个特性 (ACID) - 重点原子性 (Atomicity)
事务的整个过程如原子操作一样,最终要么全部成功,或者全部失败,这个原子性是从最终结果来看的,从最终结果来看这个过程是不可分割的。
一致性 (Consistency)
一个事务必须使数据库从一个一致性状态变换到另一个一致性状态。
首先回顾一下一致性的定义。所谓一致性,指的是数据处于一种有意义的状态,这种状态是语义上的而不是语法上的。最常见的例子是转帐。例如从帐户 A 转一笔钱到帐户 B 上,如果帐户 A 上的钱减少了,而帐户 B 上的钱却没有增加,那么我们认为此时数据处于不一致的状态。
从这段话的理解来看,所谓一致性,即,从实际的业务逻辑上来说,最终结果是对的、是跟程序员的所期望的结果完全符合的
隔离性 (Isolation)
一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
这里先提一下事务的隔离级别:
读未提交:read uncommitted
读已提交:read committed
可重复读:repeatable read
串行化:serializable
持久性 (Durability)
一个事务一旦提交,他对数据库中数据的改变就应该是永久性的。当事务提交之后,数据会持久化到硬盘,修改是永久性的。
Mysql 中事务操作
mysql 中事务默认是隐式事务,执行 insert、update、delete 操作的时候,数据库自动开启事务、提交或回滚事务。
是否开启隐式事务是由变量 autocommit 控制的。
所以事务分为隐式事务和显式事务。
隐式事务
事务自动开启、提交或回滚,比如 insert、update、delete 语句,事务的开启、提交或回滚由 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)
可以看到上面数据回滚了。
我们把 autocommit 还原回去:
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 种:
读未提交:READ-UNCOMMITTED
读已提交:READ-COMMITTED
可重复读:REPEATABLE-READ
串行: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 窗口 BT1start transaction;
T2select * from test1;
T3
start transaction;T4
insert into test1 values (1);T5
select * from test1;T6select * from test1;
T7
commit;T8commit;
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 窗口 BT1start transaction;
T2select * from test1;
T3
start transaction;T4
insert into test1 values (1);T5
select * from test1;T6select * from test1;
T7
commit;T8select * from test1;
T9commit;
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 窗口 BT1start transaction;
T2select * from test1;
T3
start transaction;T4
insert into test1 values (1);T5
select * from test1;T6select * from test1;
T7
commit;T8select * from test1;
T9commit;
T10select * 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 窗口 BT1start 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’;
T9commit;
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 窗口 BT1start transaction;
T2select * from test1;
T3
start transaction;T4
insert into test1 values (1);T5commit;
T6
commit;
按时间顺序运行上面的命令,会发现 T4- B 这样会被阻塞,直到 T5- A 执行完毕。
上面这个演示的是读写互斥产生的效果,大家可以自己去写一下写读、写写互斥的效果。
可以看出来,事务只能串行执行了。串行情况下不存在脏读、不可重复读、幻读的问题了。
小结
读未提交 (Read Uncommitted)
读未提交是隔离级别最低的一种事务级别。在这种隔离级别下,一个事务会读到另一个事务更新后但未提交的数据,如果另一个事务回滚,那么当前事务读到的数据就是脏数据,这就是脏读(Dirty Read)。
读已提交 (Read Committed)
在 Read Committed 隔离级别下,一个事务可能会遇到不可重复读(Non Repeatable Read)的问题。不可重复读是指,在一个事务内,多次读同一数据,在这个事务还没有结束时,如果另一个事务恰好修改了这个数据,那么,在第一个事务中,两次读取的数据就可能不一致。
可重复读 (Repeatable Read)
在 Repeatable Read 隔离级别下,一个事务可能会遇到幻读(Phantom Read)的问题。幻读是指,在一个事务中,第一次查询某条记录,发现没有,但是,当试图更新这条不存在的记录时,竟然能成功,并且,再次读取同一条记录,它就神奇地出现了。幻读就是没有读到的记录,以为不存在,但其实是可以更新成功的,并且,更新成功后,再次读取,就出现了。
可串行化 (Serializable)
Serializable 是最严格的隔离级别。在 Serializable 隔离级别下,所有事务按照次序依次执行,因此,脏读、不可重复读、幻读都不会出现。
虽然 Serializable 隔离级别下的事务具有最高的安全性,但是,由于事务是串行执行,所以效率会大大下降,应用程序的性能会急剧降低。如果没有特别重要的情景,一般都不会使用 Serializable 隔离级别。
默认隔离级别:如果没有指定隔离级别,数据库就会使用默认的隔离级别。在 MySQL 中,如果使用 InnoDB,默认的隔离级别是 Repeatable Read。
关于隔离级别的选择
需要对各种隔离级别产生的现象非常了解,然后选择的时候才能游刃有余
隔离级别越高,并发性也低,比如最高级别 SERIALIZABLE 会让事物串行执行,并发操作变成串行了,会导致系统性能直接降低。
具体选择哪种需要结合具体的业务来选择。
读已提交(READ-COMMITTED)通常用的比较多。
关于“mysql 事务的含义是什么”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“mysql 事务的含义是什么”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注丸趣 TV 行业资讯频道。
向 AI 问一下细节
丸趣 TV 网 – 提供最优质的资源集合!