MySQL-事务

1.什么是事务?

事务把⼀组SQL语句打包成为⼀个整体,在这组SQL的执⾏过程中,要么全部成功,要么全部失

败。这组SQL语句可以是⼀条也可以是多条。来看⼀个转账的例⼦,如图:

在这个例子中,涉及了两条更新语句:

# ===================账⼾表====================
2 CREATE TABLE `bank_account` (
3 `id` bigint PRIMARY KEY AUTO_INCREMENT, 
4 `name` varchar(255) NOT NULL, # 姓名
5 `balance` decimal(10, 2) NOT NULL # 余额
6 );
7 INSERT INTO bank_account(`name`, balance) VALUES('张三', 1000);
8 INSERT INTO bank_account(`name`, balance) VALUES('李四', 1000);
# ================更新操作===================
# 张三余额减少100
UPDATE bank_account set balance = balance - 100 where name = '张三';
# 李四余额增加100
UPDATE bank_account set balance = balance + 100 where name = '李四';

• 如果转账成功,应该有以下结果:

  1. 张三的账⼾余额减少 100 ,变成 900 ,李四的账⼾余额增加了 100 ,变成 1100 ,不能出现张

三的余额减少⽽李四的余额没有增加的情况;

  1. 张三和李四在发⽣转账前后的总额不变,也就是说转账前张三和李四的余额总数为

1000+1000=2000 ,转账后他们的余额总数为 900+1100=2000 ;

  1. 转账后的余额结果应当保存到存储介质中,以便以后读取;

  2. 还有⼀点需要要注意,在转账的处理过程中张三和李四的余额不能因其他的转账事件⽽受到⼲扰;

以上这四点在事务的整个执⾏过程中必须要得到保证,这也就是事务的 ACID 特性

2. 事务的ACID特性

事务的ACID特性指的是 Atomicity (原⼦性), Consistency (⼀致性), Isolation (隔离性)和 Durability (持久性)。

• Atomicity (原⼦性):⼀个事务中的所有操作,要么全部成功,要么全部失败,不会出现只执⾏了⼀半的情况,如果事务在执⾏过程中发⽣错误,会回滚( Rollback )到事务开始前的状态,就像这个事务从来没有执⾏过⼀样;

• Consistency (⼀致性):在事务开始之前和事务结束以后,数据库的完整性不会被破坏。这表⽰写⼊的数据必须完全符合所有的预设规则,包括数据的精度、关联性以及关于事务执⾏过程中服务器崩溃后如何恢复;

• Isolation (隔离性):数据库允许多个并发事务同时对数据进⾏读写和修改,隔离性可以防⽌多个事务并发执⾏时由于交叉执⾏⽽导致数据的不⼀致。事务可以指定不同的隔离级别,以权衡在不同的应⽤场景下数据库性能和安全;

• Durability (持久性):事务处理结束后,对数据的修改将永久的写⼊存储介质,即便系统故障也不会丢失。

3. 为什么要使⽤事务?

事务具备的ACID特性,是我们使⽤事务的原因,在我们⽇常的业务场景中有⼤量的需求要⽤事务来保证。⽀持事务的数据库能够简化我们的编程模型, 不需要我们去考虑各种各样的潜在错误和并发问题,在使⽤事务过程中,要么提交,要么回滚,不⽤去考虑⽹络异常,服务器宕机等其他因素,因此我们经常接触的事务本质上是数据库对 ACID 模型的⼀个实现,是为应⽤层服务的。

4.如何使用事务

4.1查看支持事务的存储引擎

• 要使⽤事务那么数据库就要⽀持事务,在MySQL中⽀持事务的存储引擎是InnoDB,可以通过

**show engines;**语句查看:

4.2语法

• 通过以下语句可以完成对事务的控制:

# 开始⼀个新的事务
START TRANSACTION;
# 或
BEGIN;

# 提交当前事务,并对更改持久化保存
COMMIT;

# 回滚当前事务,取消其更改
ROLLBACK;

• START TRANSACTION 或 BEGIN 开始⼀个新的事务;

• COMMIT 提交当前事务,并对更改持久化保存;

• ROLLBACK 回滚当前事务,取消其更改;

• ⽆论提交还是回滚,事务都会关闭

4.3开启一个事务,执行修改后回滚

# 开启事务
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
# 在修改之前查看表中的数据
mysql> select * from bank_account;
+----+------+---------+
| id | name | balance |
+----+------+---------+
| 1 | 张三 | 1000.00 |
| 2 | 李四 | 1000.00 |
+----+------+---------+
2 rows in set (0.00 sec)

# 张三余额减少100
mysql> UPDATE bank_accountset balance = balance - 100 where name = '张三';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

# 李四余额增加100
mysql> UPDATE bank_accountset balance = balance + 100 where name = '李四';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

# 在修改之后,提交之前查看表中的数据,余额已经被修改
mysql> select * from bank_account;
+----+------+---------+
| id | name | balance |
+----+------+---------+
| 1 | 张三 | 900.00 |
| 2 | 李四 | 1100.00 |
+----+------+---------+
2 rows in set (0.00 sec)

# 回滚事务
mysql> ROLLBACK;
Query OK, 0 rows affected (0.00 sec)

# 再查询发现修改没有⽣效
mysql> select * from bank_account;
+----+------+---------+
| id | name | balance |
+----+------+---------+
| 1 | 张三 | 1000.00 |
| 2 | 李四 | 1000.00 |
+----+------+---------+
2 rows in set (0.00 sec)

4.4开启一个事务,执行修改后提交

# 开启事务
mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)
# 在修改之前查看表中的数据
mysql> select * from bank_account;
+----+------+---------+
| id | name | balance |
+----+------+---------+
| 1 | 张三 | 1000.00 |
| 2 | 李四 | 1000.00 |
+----+------+---------+
2 rows in set (0.00 sec)

# 张三余额减少100
mysql> UPDATE bank_accountset balance = balance - 100 where name = '张三';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

# 李四余额增加100
mysql> UPDATE bank_accountset balance = balance + 100 where name = '李四';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

# 在修改之后,提交之前查看表中的数据,余额已经被修改
mysql> select * from bank_account;
+----+------+---------+
| id | name | balance |
+----+------+---------+
| 1 | 张三 | 900.00 |
| 2 | 李四 | 1100.00 |
+----+------+---------+
2 rows in set (0.00 sec)

# 提交事务
mysql> COMMIT;
Query OK, 0 rows affected (0.01 sec)

# 再查询发现数据已被修改,说明数据已经持久化到磁盘
mysql> select * from bank_account;
+----+------+---------+
| id | name | balance |
+----+------+---------+
| 1 | 张三 | 900.00 |
| 2 | 李四 | 1100.00 |
+----+------+---------+
2 rows in set (0.00 sec)

4.5保存点

在事务执⾏的过程中设置保存点,回滚时指定保存点可以把数据恢复到保存点的状态

# 开启事务
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

# 在修改之前查看表中的数据
mysql> select * from bank_account;
+----+------+---------+
| id | name | balance |
+----+------+---------+
| 1 | 张三 | 900.00 |
| 2 | 李四 | 1100.00 |
+----+------+---------+
2 rows in set (0.00 sec)

# 张三余额减少100
mysql> UPDATE bank_accountset balance = balance - 100 where name = '张三';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

# 李四余额增加100
mysql> UPDATE bank_accountset balance = balance + 100 where name = '李四';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

# 余额已经被修改
mysql> select * from bank_account;
+----+--------+---------+
| id | name | balance |
+----+--------+---------+
| 1 | 张三 | 800.00 |
| 2 | 李四 | 1200.00 |
+----+--------+---------+
2 rows in set (0.00 sec)

# 设置保存点
mysql> SAVEPOINT savepoint1;
Query OK, 0 rows affected (0.01 sec)

# 再次执⾏,张三余额减少100
mysql> UPDATE bank_accountset balance = balance - 100 where name = '张三';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

# 再次执⾏,李四余额增加100
mysql> UPDATE bank_accountset balance = balance + 100 where name = '李四';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

# 余额已经被修改
mysql> select * from bank_account;
+----+--------+---------+
| id | name | balance |
+----+--------+---------+
| 1 | 张三 | 700.00 |
| 2 | 李四 | 1300.00 |
+----+--------+---------+
2 rows in set (0.00 sec)

# 设置第⼆个保存点
mysql> SAVEPOINT savepoint2;
Query OK, 0 rows affected (0.00 sec)

# 插⼊⼀条新记录
mysql> insert into bank_account values (null, '王五', 1000);
Query OK, 1 row affected (0.01 sec)

# 查询结果,新记录写⼊成功
mysql> select * from bank_account;
+----+--------+---------+
| id | name | balance |
+----+--------+---------+
| 1 | 张三 | 700.00 |
| 2 | 李四 | 1300.00 |
| 3 | 王五 | 1000.00 |
+----+--------+---------+
3 rows in set (0.00 sec)

# 回滚到第⼆个保存点
mysql> ROLLBACK TO savepoint2;
Query OK, 0 rows affected (0.00 sec)

# 回滚成功
mysql> select * from bank_account;
+----+--------+---------+
| id | name | balance |
+----+--------+---------+
| 1 | 张三 | 700.00 |
| 2 | 李四 | 1300.00 |
+----+--------+---------+
2 rows in set (0.00 sec)

# 回滚到第⼀个保存点
mysql> ROLLBACK TO savepoint1;
Query OK, 0 rows affected (0.00 sec)

# 回滚成功
mysql> select * from bank_account;
+----+--------+---------+
| id | name | balance |
+----+--------+---------+
| 1 | 张三 | 800.00 |
| 2 | 李四 | 1200.00 |
+----+--------+---------+
2 rows in set (0.00 sec)

# 回滚时不指定保存点,直接回滚到事务开始时的原始状态,事务关闭
mysql> ROLLBACK;
Query OK, 0 rows affected (0.01 sec)

# 原始状态
mysql> select * from bank_account;
+----+--------+---------+
| id | name | balance |
+----+--------+---------+
| 1 | 张三 | 900.00 |
| 2 | 李四 | 1100.00 |
+----+--------+---------+
2 rows in set (0.00 sec)

4.6自动/手动提交事务

• 默认情况下,MySQL是⾃动提交事务的,也就是说我们执⾏的每个修改操作,⽐如插⼊、更新和删除,都会⾃动开启⼀个事务并在语句执⾏完成之后⾃动提交,发⽣异常时⾃动回滚。

• 查看当前事务是否⾃动提交可以使⽤以下语句

mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
|    autocommit |    ON | # ON 表⽰⾃动提交开启
+---------------+-------+
1 row in set, 1 warning (0.04 sec)

• 可以通过以下语句设置事务为⾃动或⼿动提交

# 设置事务⾃动提交
mysql> SET AUTOCOMMIT=1; # ⽅式⼀
mysql> SET AUTOCOMMIT=ON; # ⽅式⼆

# 设置事务⼿动提交
mysql> SET AUTOCOMMIT=0; # ⽅式⼀
mysql> SET AUTOCOMMIT=OFF; # ⽅式⼆

注意:

• 只要使⽤ START TRANSACTION 或 BEGIN 开启事务,必须要通过 COMMIT 提交才会持久化,与是否设置 SET autocommit ⽆关。

• ⼿动提交模式下,不⽤显⽰开启事务,执⾏修改操作后,提交或回滚事务时直接使⽤ commit 或 rollback

• 已提交的事务不能回滚

5. 事务的隔离性和隔离级别

5.1 什么是隔离性

MySQL服务可以同时被多个客⼾端访问,每个客⼾端执⾏的DML语句以事务为基本单位,那么不同的客⼾端在对同⼀张表中的同⼀条数据进⾏修改的时候就可能出现相互影响的情况,为了保证不同的事务之间在执⾏的过程中不受影响,那么事务之间就需要要相互隔离,这种特性就是隔离性。

5.2 隔离级别

事务具有隔离性,那么如何实现事务之间的隔离?隔离到什么程度?如何保证数据安全的同时也要兼顾性能?这都是要思考的问题。

事务间不同程度的隔离,称为事务的隔离级别;不同的隔离级别在性能和安全⽅⾯做了取舍,有的隔离级别注重并发性,有的注重安全性,有的则是并发和安全适中;在MySQL的InnoDB引擎中事务的隔离级别有四种,分别是:

• READ UNCOMMITTED ,读未提交

• READ COMMITTED ,读已提交

• REPEATABLE READ ,可重复读(默认)

• SERIALIZABLE ,串⾏化

5.3 查看和设置隔离级别

• 事务的隔离级别分为全局作⽤域和会话作⽤域,查看不同作⽤域事务的隔离级别,可以使⽤以下的⽅式:

# 全局作⽤域
SELECT @@GLOBAL.transaction_isolation;
# 会话作⽤域
SELECT @@SESSION.transaction_isolation;
# 可以看到默认的事务隔离级别是REPEATABLE-READ(可重复读)
+---------------------------------+
| @@SESSION.transaction_isolation |
+---------------------------------+
| REPEATABLE-READ | # 默认是可重复读
+---------------------------------+
1 row in set (0.00 sec)

• 设置事务的隔离级别和访问模式,可以使⽤以下语法:

# 通过GLOBAL|SESSION分别指定不同作⽤域的事务隔离级别
SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL level|access_mode;

# 隔离级别
level: {
 REPEATABLE READ # 可重复读
 | READ COMMITTED # 读已提交
 | READ UNCOMMITTED # 读未提交
 | SERIALIZABLE # 串⾏化
}

# 访问模式
access_mode: {
 READ WRITE # 表⽰事务可以对数据进⾏读写
 | READ ONLY # 表⽰事务是只读,不能对数据进⾏读写
}

# ⽰例
# 设置全局事务隔离级别为串⾏化,后续所有事务⽣效,不影响当前事务
SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;
# 设置会话事务隔离级别为串⾏化,当前会话后续的所有事务⽣效,不影响当前事务,可以在任何时候
执⾏
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
# 如果不指定任何作⽤域,设置只针对下⼀个事务,随后的事务恢复之前的隔离级别
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;



# ⽅式⼀
SET GLOBAL transaction_isolation = 'SERIALIZABLE';
# 注意使⽤SET语法时有空格要⽤"-"代替
SET SESSION transaction_isolation = 'REPEATABLE-READ'; 

# ⽅式⼆
SET @@GLOBAL.transaction_isolation='SERIALIZABLE';
# 注意使⽤SET语法时有空格要⽤"-"代替
SET @@SESSION.transaction_isolation='REPEATABLE-READ';

5.4 不同隔离级别存在的问题

5.4.1 READ UNCOMMITTED - 读未提交与脏读

5.4.1.1 存在问题

出现在事务的READ UNCOMMITTED 隔离级别下,由于在读取数据时不做任何限制,所以并发性能很⾼,但是会出现⼤量的数据安全问题,⽐如在事务A中执⾏了⼀条INSERT 语句,在没有执⾏COMMIT 的情况下,会在事务B中被读取到,此时如果事务A执⾏回滚操作,那么事务B中读取到事务A写⼊的数据将没有意义,我们把这个理象叫做**"脏读"**。

5.4.1.2 问题重现

• 在⼀个客⼾端A中先设置全局事务隔离级别为 READ UNCOMMITTED 读未提交:

# 设置隔离级别为READ UNCOMMITTED读未提交
mysql> SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
Query OK, 0 rows affected (0.00 sec)

# 查看设置是否⽣效
mysql> SELECT @@GLOBAL.transaction_isolation;
+--------------------------------+
| @@GLOBAL.transaction_isolation |
+--------------------------------+
| READ-UNCOMMITTED | # 已⽣效
+--------------------------------+
1 row in set (0.00 sec)

• 打开另⼀个客⼾端B并确认隔离级别

# 查看设置是否⽣效
mysql> SELECT @@GLOBAL.transaction_isolation;
+--------------------------------+
| @@GLOBAL.transaction_isolation |
+--------------------------------+
| READ-UNCOMMITTED | # 已⽣效
+--------------------------------+
1 row in set (0.00 sec)

• 在不同的客⼾端中执⾏事务

|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 会话A开启事务A | 会话B开启事务B |
| # 选择数据库 use test_db; # 开启事务 START TRANSACTION; | |
| # 写⼊⼀条新数据 insert into bank_account values (null, '王五', 2000); | |
| # 查询结果, select * from bank_account; +----+------+---------+ | id | name | balance | +----+------+---------+ | 1 | 张三 | 800.00 | | 2 | 李四 | 1100.00 | | 5 | 王五 | 2000.00 | +----+------+---------+ 3 rows in set (0.00 sec) # 新记录已写⼊,但是此时事务A并没有提交 | |
| | # 选择数据库 use test_db; # 开启事务 START TRANSACTION; |
| | # 查询结果 select * from bank_account; +----+------+---------+ | id | name | balance | +----+------+---------+ | 1 | 张三 | 800.00 | | 2 | 李四 | 1100.00 | | 5 | 王五 | 2000.00 | +----+------+---------+ 3 rows in set (0.00 sec) # 发现查到了事务A没有提交的数据 |
| # 回滚 rollback; # 查询结果,数据正常回滚 select * from bank_account; +----+------+---------+ | id | name | balance | +----+------+---------+ | 1 | 张三 | 800.00 | | 2 | 李四 | 1100.00 | +----+------+---------+ 2 rows in set (0.00 sec) | |
| | # 再次查询,刚才"王五"这条记录不存在了 select * from bank_account; +----+------+---------+ | id | name | balance | +----+------+---------+ | 1 | 张三 | 800.00 | | 2 | 李四 | 1100.00 | +----+------+---------+ 2 rows in set (0.00 sec) |

• 由于 READ UNCOMMITTED 读未提交 会出现**"脏读"** 现象,在正常的业务中出现这种问题会产⽣⾮常危重后果,所以正常情况下应该避免使⽤ READ UNCOMMITTED 读未提交这种的隔离级别.

5.4.2 READ COMMITTED - 读已提交与不可重复读

5.4.2.1 存在问题

为了解决脏读问题,可以把事务的隔离级别设置为 READ COMMITTED ,这时事务只能读到了其他事务提交之后的数据,但会出现不可重复读的问题,⽐如事务A先对某条数据进⾏了查询,之后事务B对这条数据进⾏了修改,并且提交( COMMIT )事务,事务A再对这条数据进⾏查询时,得到了事务B修改之后的结果,这导致了事务A在同⼀个事务中以相同的条件查询得到了不同的值,这个现象要**"不可重复读"**。

5.4.2.2 问题重现

• 在⼀个客⼾端A中先设置全局事务隔离级别为 READ COMMITTED 读未提交

# 设置隔离级别为READ COMMITTED读未提交
mysql> SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
Query OK, 0 rows affected (0.00 sec)
# 查看设置是否⽣效
mysql> SELECT @@GLOBAL.transaction_isolation;
+--------------------------------+
| @@GLOBAL.transaction_isolation |
+--------------------------------+
| READ-COMMITTED | # 已⽣效
+--------------------------------+
1 row in set (0.00 sec)

• 打开另一个客户端B并确认隔离级别

# 查看设置是否⽣效
mysql> SELECT @@GLOBAL.transaction_isolation;
+--------------------------------+
| @@GLOBAL.transaction_isolation |
+--------------------------------+
| READ-COMMITTED | # 已⽣效
+--------------------------------+
1 row in set (0.00 sec)

• 在不同的客⼾端中执⾏事务

|------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 会话A开启事务A | 会话B开启事务B |
| | # 选择数据库 use test_db; # 写⼊⼀条新测试数据 insert into bank_account values (null, '王五', 2000); Query OK, 1 row affected (0.00 sec) |
| | # 开启事务 START TRANSACTION; # 查询王五的记录,余额是2000 select * from bank_account where name='王五'; +----+------+---------+ | id | name | balance | +----+------+---------+ | 6 | 王五 | 2000.00 | +----+------+---------+ 1 row in set (0.01 sec) |
| # 选择数据库 use test_db; # 开启事务 START TRANSACTION; | |
| # 修改王五的余额为1000 update bank_account set balance=1000 where name = '王五'; Query OK, 1 row affected (0.00 sec) | |
| # 提交事务 commit; | |
| | # 此时事务并没有提交或回滚 # 再次查询王五的记录发现余额变成了 1000 # 与上⼀个查询结果不⼀致 select * from bank_account where name='王五'; +----+------+---------+ | id | name | balance | +----+------+---------+ | 6 | 王五 | 1000.00 | # 出现 问题 +----+------+---------+ 1 row in set (0.00 sec) |

5.4.3 REPEATABLE READ - 可重复读与幻读

5.4.3.1 存在问题

为了解决不可重复读问题,可以把事务的隔离级别设置为REPEATABLE READ ,这时同⼀个事务中读取的数据在任何时候都是相同的结果,但还会出现⼀个问题,事务A查询了⼀个区间的记录得到结果集A ,事务B向这个区间的间隙中写⼊了⼀条记录并提交,事务A再查询这个区间的结果集时会查到事务B新写⼊的记录得到结果集B ,两次查询的结果集不⼀致 ,这个现象就是**"幻读"**。

MySQL的InnoDB存储引擎使⽤了Next-Key锁解决了⼤部分幻读问题

5.4.3.2 问题重现

• 由于REPEATABLE READ 隔离级别默认使⽤了 Next-Key 锁,为了重现幻读问量,我们把隔离级回退到更新时只加了排他锁的READ COMMITTED .

# 设置隔离级别为READ COMMITTED读未提交
mysql> SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
Query OK, 0 rows affected (0.00 sec)

# 查看设置是否⽣效
mysql> SELECT @@GLOBAL.transaction_isolation;
+--------------------------------+
| @@GLOBAL.transaction_isolation |
+--------------------------------+
| READ-COMMITTED | # 已⽣效
+--------------------------------+
1 row in set (0.00 sec)

• 在不同的客⼾端中执⾏事务

|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 会话A开启事务A | 会话B开启事务B |
| | # 选择数据库 use test_db; # 开启事务 START TRANSACTION; |
| | # 更新五五的余额,使该记录加排他锁 update account set bank_account=2000 where name='王五'; Query OK, 1 row affected (0.01 sec) |
| | # 查询结果集,更新成功 select * from bank_account; +----+------+---------+ | id | name | balance | +----+------+---------+ | 1 | 张三 | 800.00 | | 2 | 李四 | 1100.00 | | 6 | 王五 | 2000.00 | # 更新成 功 +----+------+---------+ 3 rows in set (0.00 sec) |
| # 选择数据库 use test_db; # 开启事务 START TRANSACTION; | |
| # 在李"四与"和"王五"之间的间隙写⼊ # ⼀条数据"赵六" insert into bank_account values (3, '赵六', 5000); | |
| # 查询结果集,写⼊成功 select * from bank_account; +----+------+---------+ | id | name | balance | +----+------+---------+ | 1 | 张三 | 800.00 | | 2 | 李四 | 1100.00 | | 3 | 赵六 | 5000.00 | | 6 | 王五 | 1000.00 | +----+------+---------+ 4 rows in set (0.01 sec) | |
| # 提交事务 commit; | |
| | # 查询结果集, # 发现⽐上⼀次查询的结果集 # 多出了其他事务写⼊的数据 select * from bank_account; +----+------+---------+ | id | name | balance | +----+------+---------+ | 1 | 张三 | 800.00 | | 2 | 李四 | 1100.00 | | 3 | 赵六 | 5000.00 | # 幻像⾏ | 6 | 王五 | 2000.00 | +----+------+---------+ 4 rows in set (0.01 sec) |
| | # 提交事务 commit; |

把隔离级别设置为REPEATABLE-READ后,在ID的间隙中插⼊新数据观察现象,⽐如插⼊ID = 4的记录

5.4.4 SERIALIZABLE - 串⾏化

进⼀步提升事务的隔离级别到 SERIALIZABLE ,此时所有事务串⾏执⾏,可以解决所有并发中的安全问题。

5.5 不同隔离级别的性能与安全

相关推荐
苹果醋31 小时前
React源码02 - 基础知识 React API 一览
java·运维·spring boot·mysql·nginx
了一li2 小时前
Qt中的QProcess与Boost.Interprocess:实现多进程编程
服务器·数据库·qt
码农君莫笑2 小时前
信管通低代码信息管理系统应用平台
linux·数据库·windows·低代码·c#·.net·visual studio
别致的影分身3 小时前
使用C语言连接MySQL
数据库·mysql
过过过呀Glik3 小时前
在 Ubuntu 上安装 MySQL 的详细指南
mysql·ubuntu
京东零售技术4 小时前
“慢”增长时代的企业数据体系建设:超越数据中台
数据库
sdaxue.com4 小时前
帝国CMS:如何去掉帝国CMS登录界面的认证码登录
数据库·github·网站·帝国cms·认证码
o(╥﹏╥)5 小时前
linux(ubuntu )卡死怎么强制重启
linux·数据库·ubuntu·系统安全
阿里嘎多学长5 小时前
docker怎么部署高斯数据库
运维·数据库·docker·容器
Yuan_o_6 小时前
Linux 基本使用和程序部署
java·linux·运维·服务器·数据库·后端