MySQL事务以及并发问题
- 事务
- 1.什么是事务
- 2.MySQL如何开启事务
- 3.事务提交方式
- 4.事务原理
- 5.事务的四大特性(ACID)
- 事务并发问题
- 1.并发引起的三个问题
- 2.事务隔离级别
事务
在 MySQL 中,事务支持是在引擎层实现的。MySQL 是一个支持多引擎的系统,但并不是所有的引擎都支持事务。比如 MySQL 原生的 MyISAM 引擎就不支持事务,下面介绍是以InnoDB 引擎来的。
1.什么是事务
在实际的业务开发中,有些业务操作要多次访问数据库。一个业务要发送多条SQL语句给数据库执行。需要将多次访问数据库的操作视为一个整体来执行,要么所有的SQL语句全部执行成功。如果其中有一条SQL语句失败,就进行事务的回滚,所有的SQL语句全部执行失败。
简而言之,事务指的是逻辑上的一组操作,组成这组操作的各个单元要么全都成功,要么全都失败。
eg: 银行转账事务
用户A向用户B转200块钱(事务包含两条sql语句);
无异常发生状况下,A账户减掉200块钱,B账户增加200块钱;
如果发生异常导致A账户扣掉钱,而B账户没有收到钱。或者A账户没有扣钱,但B账户收到钱了。这显然不符合业务逻辑;
注:在数据库中查询不会涉及到使用事务,事务都是 增删改。
2.MySQL如何开启事务
MySQL事务有关的SQL语句:
SQL语句 | 描述 |
---|---|
start transaction; | 开启手动控制事务 |
commit; | 提交事务 |
rollback; | 回滚事务 |
# 创建一个表:账户表.
create database transaction_test;
# 使用数据库
use transaction_test;
# 创建账号表
create table account(
id int primary key auto_increment,
name varchar(20),
money double
);
# 初始化数据
insert into account values (null,'A',1000);
insert into account values (null,'B',1000);
案例演示1:需求:演示提交事务,a给b转账200元。(成功)
-- 需求:A给B转账200元(演示提交事务)
-- 1.开启事务
start transaction;
-- 2.执行一组sql语句
-- 2.1 A账户扣去200
update account set money=money-200 where name='A';
-- 2.2 B账户添加200
update account set money=money+200 where name='B';
-- 3.提交事务(使sql生效)
commit;
案例演示2:演示回滚事务,a给b转账200元。(失败)
MySQL本身无法演示出现异常的状况
-- 需求:A给B转账200元(演示回滚事事务)
-- 1.开启事务
start transaction;
-- 2.执行一组sql语句
-- 2.1 A账户扣去200
update account set money=money-200 where name='A';
# 假设A给B转账中间发生异常,之后选择回滚sql语句
-- 2.2 B账户添加200
update account set money=money+200 where name='B';
-- 3.回滚事务(撤销sql语句)
rollback;
3.事务提交方式
自动提交事务(MySQL默认的事务操作方式)
手动提交事务(上方例子)
MySQL的每一条DML(增删改)语句都是一个单独的事务,每条语句都会自动开启一个事务,执行完毕自动提交事务。
MySQL默认开始自动提交事务。如果我们想将几条DML语句视为同一个事务,需要开启手动提交事务。比如上面的转账案例
-
如果我们想修改MySQL默认事务方式,将其改为手动提交。可以使用下面命令
-
-- 查看当前autocomit模式 show variables like %commit%;
-
-- 设置自动提交的参数为OFF: set autocommit = 0; -- 0:OFF 1:ON
注:设置autocommit为off状态,只是
临时性
的,下次重新连接MySQL,autocommit依然变为on状态。
-
4.事务原理
- 一个用户登录成功以后,服务器会创建一个临时日志文件。日志文件用来保存用户事务状态。
- 如果没有使用事务,则所有的操作直接写到数据库中,不会使用日志文件。
- 如果开启事务,将所有的写操作写到日志文件中。
- 如果这时用户提交了事务,则将日志文件中所有的操作写到数据库中。
- 如果用户回滚事务,则日志文件会被清空,不会影响到数据库的操作。
5.事务的四大特性(ACID)
1、隔离性(Isolation)
-
多个用户并发的访问数据库时,一个用户的事务不能被其他用户的事务干扰,多个并发的事务之间要相互隔离。
-
一个事务的成功或者失败对于其他的事务是没有影响。2个事务应该相互独立。
eg:
a 给b转账 -----> 叫做事务A
c 给d 转账 -----> 叫做事务B
事务A和事务B之间不会相互影响。
2、持久性(Durability)
- 指一个事务一旦被提交,它对数据库的改变将是永久性的,哪怕数据库发生异常,重启之后数据亦然存在。
eg:A=1000、B=1000转账
开启事务
A-200
B+200
提交
结果: A 800 B 1200
即使事务提交以后再发生异常,A和B的数据依然不会变。A就是800,B就是1200。
3、原子性(Atomicity)
- 原子性是指事务包装的一组sql(一组业务逻辑)是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
4、一致性(Consistency)
-
一个事务在执行之前和执行之后 数据库都必须处于一致性状态。如果事务成功的完成,那么数据库的所有变化将生效。如果事务执行出现错误,那么数据库的所有变化将会被回滚(撤销),返回到原始状态。
-
事务的成功与失败,最终数据库的数据都是符合实际生活的业务逻辑。一致性绝大多数依赖业务逻辑和原子性。
eg:A=1000、B=1000转账
开启事务
A-200
B+200
提交
结果: A 800 B 1200
如果A转账失败了,那么B也得失败。不能转账之前一共是2000块钱,A扣钱,B不变,结果是1800。或者A不扣钱,B加钱,结果是2200。
**事务前后数据的完整性必须保持一致**
事务并发问题
1.并发引起的三个问题
事务在操作时的理想状态:多个事务之间互不影响,如果隔离级别设置不当就可能引发并发访问问题。
当数据库上有多个事务同时执行的时候,就可能出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题,为了解决这些问题,之后会介绍解决方案“隔离级别”。
并发访问的问题 | 含义 |
---|---|
脏读 | 一个事务读取到了另一个事务中尚未提交的数据。最严重,杜绝发生。 |
不可重复读 | 一个事务中两次读取的数据内容不一致,要求的是一个事务中多次读取时数据是不一致的,这是事务update时引发的问题 |
幻读(虚读) | 一个事务内读取到了别的事务插入或者删除的数据,导致前后读取记录行数不同。这是insert或delete时引发的问题 |
1.脏读:指一个事务读取了另外一个事务未提交的数据。(非常危险)
脏读具体解释如下图:注意 脏读的前提是没有事务的隔离性。
- 说明:事务a首先执行转账操作,然后事务a还没有提交数据的情况下,事务b读取了数据库的数据。紧接着事务a执行回滚操作,导致事务b读取的结果和数据库的实际数据是不一样的。
- 一个事务读取了另一个事务未提交的数据叫做脏读。
2.不可重复读:在一个事务内多次读取表中的数据,多次读取的结果不同。
-
说明:事务b首先读取数据库的数据,然后事务a对数据修改并提交。之后事务b对数据库再次进行读取。这时发现在事务b中2次读取的结果不一致。
-
一个事务内读取了另一个事务提交的数据。这个叫做不可重复读。
- 例如: 银行想查询A账户的余额,第一次查询的结果是200元,A向账户中又存了100元。此时,银行再次查询的结果变成了300元。两次查询的结果不一致,银行就会很困惑,以哪次为准。
-
和脏读不同的是:脏读读取的是前一事务未提交的数据,不可重复度 读取的是前一事务已提交的事务。
3.幻读(虚读):一个事务内读取到了别的事务插入或者删除的数据,导致前后读取记录行数不同
-
说明:事务b首先读取数据的数量,然后事务a添加了一条数据,并且提交了。接着事务b再次读取了数据的数量。2次读取不一致。
-
同一个事务内,2次读取的数据的数量不一致,叫做幻读或者虚读。
-
虚读(幻读)和不可重复读的区别:
- 不可重复读:强调的是数据内容的不一致。另一个事务是update操作。
- 虚读(幻读):强调的数据的数量(记录数)的不一致。另一个事务是insert或者delete操作。
2.事务隔离级别
通过以上面描述,我们发现如果不考虑事务的隔离性,会遇到脏读、不可重复读和虚读等问题。所以在数据库中我们要对上述三种问题进行解决。MySQL数据库规范规定了4种隔离级别,分别用于描述两个事务并发的所有情况。
上面的级别最低,下面的级别最高。“是”表示会出现这种问题,“否”表示不会出现这种问题。
级别 | 名字 | 隔离级别 | 脏读 | 不可重复读 | 幻读 | 数据库默认隔离级别 |
---|---|---|---|---|---|---|
1 | 读未提交 | read uncommitted | 是 | 是 | 是 | |
2 | 读已提交 | read committed | 否 | 是 | 是 | Oracle和SQL Server |
3 | 可重复读 | repeatable read | 否 | 否 | 是 | MySQL |
4 | 串行化 | serializable | 否 | 否 | 否 |
-
安全和性能对比
-
安全性:serializable > repeatable read > read committed > read uncommitted
-
性能 : serializable < repeatable read < read committed < read uncommitted
-
-
注:其实三个问题,开发中最严重的问题就是脏读,这个问题一定要避免,而关于不可重复读和虚读其实只是感官上的错误,并不是逻辑上的错误。就是数据的时效性,所以这种问题并不属于很严重的错误。如果对于数据的时效性要求不是很高的情况下,我们是可以接受不可重复读和虚读的情况发生的。
-
查询全局事务隔离级别
-
show variables like '%isolation%'; -- 或 select @@tx_isolation;
-
-
设置事务隔离级别
-
set global transaction isolation level 隔离级别; -- 如: set global transaction isolation level read uncommitted; set global transaction isolation level repeatable read;
-
注:需要退出MySQL再进入MYSQL才能看到隔离级别的变化
-