更改mysql的事务隔离级别 mysql事务隔离级别 默认_更改mysql的事务隔离级别


对于数据库的隔离级别之前一直没有做详细整理,最近项目运行中发现了一个问题,所以抽时间对这块认真研究了下

业务场景:
服务A在处理流程中,会调用外部服务B,然后写入一条数据,服务B执行完成后,会回调服务C的接口更新服务A写入的数据。问题:
在服务B回调服务C的时候总是找不到服务A写入的数据,在服务C中添加延时重试,问题依然存在,但此时查看数据库,对应的数据是已经存在。

先说原因吧,是因为MySQL的事务默认隔离级别是:可重复读。
在服务A调用服务B后,还没有写入数据到数据库,服务B就已经回调服务C了,服务C此时肯定是找不到对应的数据的,由于MySQL默认隔离级别是可重复读(即在一个事务中,对于同一份数据读取都是一样的),所以即使服务A已经写入了数据,服务C依然读取不到。

解决方案:
在服务C中的查询,不要放到一个事务里面,单独提取一个方法,后面的更新逻辑放到同一个事务中

什么是事务?

数据库事务是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。
比如:某人在商店购买100商品,其中包括两个操作:
1.该人账户减少100元
2.商店账户增加100元
这两个操作要么同时执行成功,要么同时执行失败。

数据库事务的ACID性质

  • Atomic:原子性,将所有SQL作为原子工作单元执行,要么全部执行,要么全部不执行;
  • Consistent:一致性,事务完成后,所有数据的状态都是一致的,即该人的账户减少了100,商店的账户必须增加100
  • Isolation:隔离性,比如两个人从同一个账户取款,这两个事务对数据的修改必须相互隔离,具体隔离策略后面具体讲解。
  • Duration:持久性,事务完成后,对数据库数据的修改必须被持久化存储。

数据库的隔离级别

在单个事务中,不需要做隔离,所谓数据库隔离级别是针对在并发事务的情况下,解决导致的一系列问题,这些问题包括:脏读、不可重复读、幻读,具体隔离级别如下图:

隔离级别

脏读

不可重复读

幻读

SERIALIZABLE(串行化)

避免

避免

避免

REPEATABLE READ(可重复读)

避免

避免

允许

READ COMMITED(读已提交)

避免

允许

允许

READ UNCOMMITED(读未提交)

允许

允许

允许

SERIALIZABLE(串行化)

当两个事务同时操作数据库中相同数据时,如果第一个事务已经在访问该数据,第二个事务只能停下来等待,必须等到第一个事务结束后才能恢复运行。因此这两个事务实际上是串行化方式运行。

REPEATABLE READ(可重复读)

一个事务在执行过程中可以看到其他事务已经提交的新插入的记录,但是不能看到其他事务对已有记录的更新。

READ COMMITTED(读已提交数据)

一个事务在执行过程中可以看到其他事务已经提交的新插入的记录,而且还能看到其他事务已经提交的对已有记录的更新。

READ UNCOMMITTED(读未提交数据)

一个事务在执行过程中可以看到其他事务没有提交的新插入的记录,而且还能看到其他事务没有提交的对已有记录的更新。

没有事务隔离级别导致的问题

如果没有数据库的隔离级别,数据库的数据是实时变化的,即每个事务都可以读到其它事务修改后的数据,下面结合实例介绍每个场景的问题。

脏读

定义:读到未提交更新的数据

时间点

事务1

事务2

T1

开始事务

T2

开始事务

T3

查询账户余额为1000

T4

取出100后金额为900

T5

查询账户金额为900(脏读)

T6

撤销事务,余额恢复为1000

T7

存入100元后,金额变为1000

T8

提交事务

时间点

事务1

事务2

T1

开始事务

T2

开始事务

T3

查询账户余额为1000

T4

取出100后金额为900

T5

查询账户金额为900(脏读)

T6

撤销事务,余额恢复为1000

T7

存入100元后,金额变为1000

T8

提交事务

时间点

事务1

事务2

T1

开始事务

T2

开始事务

T3

查询账户余额为1000

T4

取出100后金额为900

T5

查询账户金额为900(脏读)

T6

撤销事务,余额恢复为1000

T7

存入100元后,金额变为1000

T8

提交事务

时间点

事务1

事务2

T1

开始事务

T2

开始事务

T3

查询账户余额为1000

T4

取出100后金额为900

T5

查询账户金额为900(脏读)

T6

撤销事务,余额恢复为1000

T7

存入100元后,金额变为1000

T8

提交事务

时间点

事务1

事务2

T1

开始事务

T2

开始事务

T3

查询账户余额为1000

T4

取出100后金额为900

T5

查询账户金额为900(脏读)

T6

撤销事务,余额恢复为1000

T7

存入100元后,金额变为1000

T8

提交事务

时间点

事务1

事务2

T1

开始事务

T2

开始事务

T3

查询账户余额为1000

T4

取出100后金额为900

T5

查询账户金额为900(脏读)

T6

撤销事务,余额恢复为1000

T7

存入100元后,金额变为1000

T8

提交事务

时间点

事务1

事务2

T1

开始事务

T2

开始事务

T3

查询账户余额为1000

T4

取出100后金额为900

T5

查询账户金额为900(脏读)

T6

撤销事务,余额恢复为1000

T7

存入100元后,金额变为1000

T8

提交事务

时间点

事务1

事务2

T1

开始事务

T2

开始事务

T3

查询账户余额为1000

T4

取出100后金额为900

T5

查询账户金额为900(脏读)

T6

撤销事务,余额恢复为1000

T7

存入100元后,金额变为1000

T8

提交事务

时间点

事务1

事务2

T1

开始事务

T2

开始事务

T3

查询账户余额为1000

T4

取出100后金额为900

T5

查询账户金额为900(脏读)

T6

撤销事务,余额恢复为1000

T7

存入100元后,金额变为1000

T8

提交事务

如上事务1取出金额100后又回滚了,即啥都没做,但事务2存入了100,但最终的金额确还是1000,正确应该是1100。
在T5时间节点出现了脏读,如果数据库配置了隔离级别为SERIALIZABLE、REPEATABLE READ、READ COMMITTED,在事务1没有提交的时候,事务2读取的都是原来的值就不会出现问题。

不可重复读

定义:在同一个数据中,两次读取到的数据不一致,读到了其他数据提交更新的数据

T1

开始事务

T2

开始事务

T3

查询账户余额为1000

T4

查询账户余额为1000

T5

取出100后金额为900

T6

提交事务

T7

查询账户余额为900(与T4读取的不一致)

T1

开始事务

T2

开始事务

T3

查询账户余额为1000

T4

查询账户余额为1000

T5

取出100后金额为900

T6

提交事务

T7

查询账户余额为900(与T4读取的不一致)

T1

开始事务

T2

开始事务

T3

查询账户余额为1000

T4

查询账户余额为1000

T5

取出100后金额为900

T6

提交事务

T7

查询账户余额为900(与T4读取的不一致)

T1

开始事务

T2

开始事务

T3

查询账户余额为1000

T4

查询账户余额为1000

T5

取出100后金额为900

T6

提交事务

T7

查询账户余额为900(与T4读取的不一致)

T1

开始事务

T2

开始事务

T3

查询账户余额为1000

T4

查询账户余额为1000

T5

取出100后金额为900

T6

提交事务

T7

查询账户余额为900(与T4读取的不一致)

T1

开始事务

T2

开始事务

T3

查询账户余额为1000

T4

查询账户余额为1000

T5

取出100后金额为900

T6

提交事务

T7

查询账户余额为900(与T4读取的不一致)

T1

开始事务

T2

开始事务

T3

查询账户余额为1000

T4

查询账户余额为1000

T5

取出100后金额为900

T6

提交事务

T7

查询账户余额为900(与T4读取的不一致)

T1

开始事务

T2

开始事务

T3

查询账户余额为1000

T4

查询账户余额为1000

T5

取出100后金额为900

T6

提交事务

T7

查询账户余额为900(与T4读取的不一致)

事务2的两次读取到的数据不一致,第二次读取到了事务1提交的数据

幻读

定义:读取到另一个事务已提交插入或删除的数据。

T1

开始事务

T2

开始事务

T3

统计一年级1班所有的学生人数为40人

T4

一年级1班新增一名学生

T5

提交事务

T6

再次统计一年级1班的所有学生人数为41人

T1

开始事务

T2

开始事务

T3

统计一年级1班所有的学生人数为40人

T4

一年级1班新增一名学生

T5

提交事务

T6

再次统计一年级1班的所有学生人数为41人

T1

开始事务

T2

开始事务

T3

统计一年级1班所有的学生人数为40人

T4

一年级1班新增一名学生

T5

提交事务

T6

再次统计一年级1班的所有学生人数为41人

T1

开始事务

T2

开始事务

T3

统计一年级1班所有的学生人数为40人

T4

一年级1班新增一名学生

T5

提交事务

T6

再次统计一年级1班的所有学生人数为41人

T1

开始事务

T2

开始事务

T3

统计一年级1班所有的学生人数为40人

T4

一年级1班新增一名学生

T5

提交事务

T6

再次统计一年级1班的所有学生人数为41人

T1

开始事务

T2

开始事务

T3

统计一年级1班所有的学生人数为40人

T4

一年级1班新增一名学生

T5

提交事务

T6

再次统计一年级1班的所有学生人数为41人

T1

开始事务

T2

开始事务

T3

统计一年级1班所有的学生人数为40人

T4

一年级1班新增一名学生

T5

提交事务

T6

再次统计一年级1班的所有学生人数为41人

事务2第一次统计人数为40人,第二次统计为41人,两次统计的结果不一致,同样如果T4时间节点转走一名学生,也会出现不一致

不可重复读和幻读看起来比较类似,都是一个事务里面读取到两次不同的结果
本质的区别是:不可重复读是由于数据更新导致数据不一致导致,幻读是由于插入或删除了数据导致的。