MySQL的事务隔离级别

事务

1. 原则(ACID)

atomicity 原子性
consistency 一致性
isolation 隔离性
durability 持久性

1.1 原子性

一组事务是不能被拆分的;

要么都执行成功,要么都失败。

1.2 一致性

例如转账:转之前和转之后,钱的总数是不会变的

1.3 隔离性

两组事务同时执行,彼此之间不会影响

1.4 持久性

如果事务执行失败,就是执行之前的状态;

如果事务执行成功,那么状态也会持久化到数据库。

2. 事务的隔离级别

现存在这样一张数据库表:

id name amount
1 张三 1000
2 李四 1000
3 王五 1000

2.1 ead UnCommitted(读取未提交)

有可能导致的问题:

脏读(dirty read)

  • 事务1:
-- 设置事务的隔离级别为 Read Uncommitted;
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
-- 启动一个事件;
START TRANSACTION;
-- 修改张三的金额为700;
UPDATE account SET amount = 700 WHERE id = 1;
-- 此时事务并没有提交
  • 事务2:
-- 设置事务的隔离级别为 Read Uncommitted;
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
-- 查询account表
SELECT * FROM account;
  • 此时查询到的结果,发现张三的钱已经变成了700;
  • 如果紧接着,由于某些原因,事务1出现了问题,然后回滚了
ROLLBACK;
  • 这个时候,事务2再去查询就发现,张三的金额又变回了1000;
  • 那么就说明,事务2之前读取到的数据,是一个脏数据(dirty data),是事务1没有提交之前的数据。

2.2 Read Committed(读取提交)

解决脏读问题:

将事务的隔离级别设置为 Read Committed之后,另外一个事务在读取数据时,就不会读取到脏数据,但是依然避免不了 ——

不可重复读:

  • 事务1开始:
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
UPDATE account SET amount = 700 WHERE id = 1;
-- 此时,事务1并未提交
  • 事务2开始:
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
SELECT * FROM account;
-- 此时,事务2读取到的结果还是1000
  • 但是,一旦事务1提交了
commit;
  • 事务2在接下来的语句中查询到的结果就会变成700;
  • 那么,就会在一个事务中,对于同一个数据,有不同的值;
  • 再如果,事务1没有执行完的时候,事务3又提交改变了这个数据,事务1中的该数据就有前后不一的3个值;
  • 以此类推;
  • 那么,我们希望要达到的效果就是,让我们的事务2在执行的时候,存在的那些数据,是可重复读的。
  • 它们不会随着事件2的提交而改变掉。

2.3 Repeatable Read(可重复读)

解决不可重复读问题:

就上述例子而言,我们可以将事务2的隔离级别设置为Repeatable Read;

那么,在事务2执行的过程中,就算事务1进行了提交commit,事务2中的数据也不会受到影响;

变成了可重复读的数据。

但是,依然不能解决——

幻读(虚读):

  • 事务1开始
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
INSERT INTO account value(4,'王二麻子',500);
-- 此时事务1插入了,主键id为4的一条数据
  • 事务2开始
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
SELECT * FROM account;
-- 此时事务2的查询结果中,没有该条数据
  • 如果对事务1进行提交;
  • 会在后来的查询中发现又添加显示了该条数据;
  • 那么还是会造成一个事务中数据不一致的问题(数据行数变了);
  • 注:在MySQL中,可能观察不到事务2中数据行数变多的情况,但这只是MySQL做的一些优化,实际上,它的数据行数还是变化了的,可以在事务2中添加一行主键id同样为4的数据,它会提示主键冲突。

2.4 Serializable(序列化)

解决上述的一切问题:

将事务的级别设置为Serializable后,就相当于,该事务就变成了同步的;

  1. 当有别的事务在执行插入等修改数据的操作,并且还没有提交时;被序列化的事务,启动后,就会休眠等待,一直到它所等待的事务提交完毕,自己才会执行。(等待的时候自己连查询也做不了)
  2. 当自己在启动后,别的事务就不能执行对于数据的改变操作(但是可以查询),如果另一个事务也是被序列化的,当然它也会等待;如果别的事务(未被序列化的)想要执行修改、插入等操作,就会排队等待。

3. MySQL事务的常用功能

3.1 MySQL默认

我们发现,在我们执行SQL语句的时候,即使没有手动开启和提交事务;数据的改变也是持久性的。

说明MySQL是默认开启事务的,并且在执行完后,会默认自动提交事务(除非手动开启事务,需要手动提交);

可以通过 SET autocommit = 0;来设置关闭自动提交;或者通过start transaction;手动开启一个事务。

3.2 开启事务

3.2.1 使用默认开启的事务

要想手动提交,必须设置 autocommit = 0; || autocommit = off;

3.2.2 手动开启(start transaction)

此方式必须手动提交

3.3 提交事务

3.3.1 使用默认提交

3.3.2 手动提交(commit;)

3.4 事务回滚(rollback;)

3.4.1 默认回滚到事务开始

3.4.2 设置回滚点,回滚指定位置

SET autocommit = 0;
UPDATE account SET amount = 700 WHERE id = 1;
savepoint p1;
UPDATE account SET amount = 500 WHERE id = 1;
savepoint p2;
UPDATE account SET amount = 300 WHERE id = 1;
-- 此时查询到的amount值应该为300

rollback to p1; -- 或者rollback to savepoint p1;两种写法都可以。
-- 回滚之后,我们再查询amount的值,就回到了p1回滚点,也就是700;

3.4.3 删除回滚点(一般没用)

release savepoint p1; -- 删除p1回滚点
posted @ 2021-01-09 22:12  兰陌  阅读(53)  评论(0)    收藏  举报