目录
一、什么是事务
二、事务特性——即ACID特性
三、事务的隔离级别
1、脏读
2、不可重复读
3、幻读
Read uncommitted:
Read committed:
Repeatable read:
Serializable:
一、什么是事务
事务(Transaction)——一个最小的不可再分的工作单元
一组逻辑操作单元,使数据从一种状态变换到另一种状态
一个完整的业务需要批量的DML(insert、update、delete)语句共同联合完成。事务只和DML语句有关,或者说DML语句才有事务。这个和业务逻辑有关,业务逻辑不同,DML语句的个数不同。
数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成
二、事务特性——即ACID特性
A Atomicity 原子性
C Consistency 一致性
I Isolation 隔离性
D Durability 持久性
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性是指事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
三、事务的隔离级别
为什么要考虑事务的隔离性,如果不考虑隔离性,可能会引起幻读、不可重复读、脏读等问题。
因此多个线程开启各自事务操作数据库中数据时,数据库系统要负责隔离操作,以保证各个线程在获取数据时的准确性。
1、脏读
指一个事务读取了另外一个事务未提交的数据。
2、不可重复读
指在数据库访问中,一个事务范围内两个相同的查询却返回了不同数据。
在一个事务内,多次读同一个数据。在这个事务还没有结束时,另一个事务也访问该同一数据并修改数据。那么,在第一个事务的两次读数据之间。由于另一个事务的修改,那么第一个事务两次读到的数据可能不一样,这样就发生了在一个事务内两次读到的数据是不一样的,因此称为不可重复读,即原始读取不可重复。
3、幻读
是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。
如:事务A 按照一定条件进行数据读取, 期间事务B 插入了相同搜索条件的新数据,事务A再次按照原先条件进行读取时,发现了事务B 新插入的数据 称为幻读。
幻读和不可重复读的区别:
不可重复读:是同一条SQL查询的内容不同(被修改了)
幻读是:查询的结果条数不同(增加了、或者删除了记录)
幻读的影响
会造成一个事务中先产生的锁,无法锁住后加入的满足条件的行。
产生数据一致性问题,在一个事务中,先对符合条件的目标行做变更,而在事务提交前有新的符合目标条件的行加入。这样通过binlog恢复的数据是会将所有符合条件的目标行都进行变更的。
幻读产生的原因
行锁只能锁住行,即使把所有的行记录都上锁,也阻止不了新插入的记录。
如何解决幻读
- 将两行记录间的空隙加上锁,阻止新记录的插入;这个锁称为间隙锁。
- 间隙锁与间隙锁之间没有冲突关系。跟间隙锁存在冲突关系的,是往这个间隙中插入一个记录这个操作。
为处理脏读、不可重复读、幻读这些问题,SQL标准定义了四种事务隔离机制:
-
Serializable:可避免脏读、不可重复读、虚读情况的发生。(串行化)
-
Repeatable read:可避免脏读、不可重复读情况的发生。(可重复读)
-
Read committed:可避免脏读情况发生。(读已提交)
-
Read uncommitted:最低级别,以上情况均无法保证。(读未提交)
实例操作:
先通过命令set transaction命令设置事务隔离级别:
set transaction isolation level --设置事务隔离级别
例如:set session transaction isolation level READ UNCOMMITTED;
或者:
set session transaction_isolation='READ-UNCOMMITTED';
select @@transaction_isolation; --查询当前事务隔离级别
Read uncommitted:
set session transaction_isolation='READ-UNCOMMITTED';
设置两个事务的会话都是读未提交的隔离级别(以class表作为例子)
事务A 更改class表中class_name的值,然后回滚
事务B 更改前、更改后、回滚后都查一次class_name的值
从右图可看出:
在未提交情况下事务B读取到了事务A未提交的数据,多次读取结果不一致,出现了脏读、不可重复读等问题。
Read committed:
设置两个事务的会话都是读已提交的隔离级别(以class表作为例子)
set session transaction isolation level READ COMMITTED;
事务A 更改class表中class_name的值,然后提交数据
事务B 更改前、更改后、提交后都查一次class_name的值
从右图中可看出:
在读已提交情况下,事务B在事务A未提交之前无法读取不到事务A的数据,解决了脏读问题,但可以读取到事务A以及提交的数据,多次读取结果不一致,未出现脏读,出现了不可重复读的问题
Repeatable read:
set session transaction isolation level REPEATABLE READ;
事务A 更改class表中class_name的值,然后提交数据
事务B 更改前、更改后、提交后都查一次class_name的值
由右图可看出:
在可重复读情况下,未出现脏读,未读取事务B已提交的数据,多次读取结果都是一致的,即可重复读,解决了脏读、不可重复读的问题
Serializable:
set session transaction isolation level SERIALIZABLE;
事务A | 事务B |
start transaction; | |
select *from class; | |
start transaction; | |
insert into class values(1003,'计算机03'); | |
select *from class; | |
commit; | |
commit; |
按顺序运行上面的命令,会发现B事务插入语句时会被阻塞,直至A事务执行完毕
因此可看出,事务在此隔离级别中只能串行执行
因此串行情况下不存在脏读、不可重复读、幻读的问题。
文章参考:
玩转Mysql系列 - 第13篇:详解事务-腾讯云开发者社区-腾讯云