1. 介绍
数据库事务是指逻辑上的一组操作,肯定是两个及两个以上操作,作用是保证这一组操作全部成功或失败。
加入A转账1000给B,这个其实就是一组操作:
- A的账户减去1000
- B的账户加1000
有了事务控制之后,这个转账过程就有两个操作结果,而且将以上两个操作作为一组:
- 转账成功:A的账户要减去1000,B的账户加1000
- 转账失败:A,B的账户都不会发生变动
如果没有事务控制,那么可能会发生以下状况:
A 的账户减去1000,但由于后续的执行中发生了异常,这样B的账户无法加1000,从而导致出现数据一致性的微调,出现了脏数据。
- 测试数据准备
create database if not exists finance;
use finance;
-- 创建账户表
create table finance_account(
id int primary key auto_increment,
name varchar(64),
balance decimal(10,2)
);
-- 清空表数据
truncate table finance_account;
-- 增加测试账号
insert into finance_account values(null,'tony',200000.00),(null,'jcak',0.00);
-- 查看表
select * from finance_account;
2. MySQL数据库事务管理
MySQL数据库事务管理默认情况下是自动事务管理,也是默认开启事务,自动提交事务。
每一条SQL在执行的时候都会自动开始和提交事务。
- 查看和修改MySQL的事务管理
show variables like '%autocommit%';
-- 修改事务状态关闭自动提交,0-关闭,1-开启
set autocommit =0;
由于MySQL数据库的事务管理是自动开启,自动提交。当一组操作的某个操作执行发生异常的时候,这样会出现数据的不一致问题。
因此需要手动开启和提交事务。
语法:
手动开启事务:
start transaction;
手动提交事务:
commit;
手动回滚事务:执行过程中发生了异常需要回滚事务,即回退到开启事务之前的状态
rollback;
只有在开启事务/提交事务和开启事务/回滚事务之后,事务才会结束。
手动事务的使用:1.正常状态
start transaction ;
update finance_account set balance=balance-100000 where name='tony';
update finance_account set balance=balance+100000 where name='jack';
commit ;
手动事务的使用:2.异常状态
start transaction ;
update finance_account set balance=balance-100000 where name='tony';
update finance_account set balance=balance+100000 where name='jack';
rollback ;
3. 回滚点
3.1 回滚点的概念
在一组操作中,某些操作成功后,后续的操作可能成功或失败,但是不管后面的成功或失败,前面操作已经成,可以在当前成功的位置设置一个回滚点,这样可以供后续失败的操作返回到该回滚点的位置,而不是返回操作之前的状态。
3.2 回滚点的设置和回滚
- 设置回滚点 savepoint 回滚点名称;
- 回滚回滚点 rollback to 回滚点名称;,此时事务没有结束,需要手动提交
3.3 回滚点的使用
1.不适用回滚点–展示
-- 不使用回滚点
start transaction ;
update finance_account set balance=balance-10000 where name='tony';
update finance_account set balance=balance-10000 where name='tony';
update finance_account set balance=balance-10000 where name='tony';
update finance_account set balance=balance-10000 where name='tony';
update finance_account set balance=balance-10000 where name='tony';
update finance_account set balance=balance-10000 where name='tony';
update finance_account set balance=balance-10000 where name='tony';
update finance_account set balance=balance-10000 where name='tony';
update finance_account set balance=balance-10000 where name='tony';
update finance_account set balance=balance-10000 where name='tony';
update finance_account set balance=balance-10000 where name='tony';
update finance_account set balance=balance+100000 where name='jack';
-- rollback 会回到处理之前
rollback ;
- 一定要写rollback,这样手动事务才会结束。再次强调:
只有在开启事务/提交事务和开启事务/回滚事务之后,事务才会结束。即start transaction /commit 或 start transaction / rollback
运行前后的结果一样:
2. 使用回滚点
start transaction ;
-- 假设这5步成功
update finance_account set balance=balance-10000 where name='tony';
update finance_account set balance=balance-10000 where name='tony';
update finance_account set balance=balance-10000 where name='tony';
update finance_account set balance=balance-10000 where name='tony';
update finance_account set balance=balance-10000 where name='tony';
-- 因此可以设置回滚点
savepoint sign;
update finance_account set balance=balance-10000 where name='tony';
update finance_account set balance=balance-10000 where name='tony';
update finance_account set balance=balance-10000 where name='tony';
update finance_account set balance=balance-10000 where name='tony';
update finance_account set balance=balance-10000 where name='tony';
update finance_account set balance=balance+100000 where name='jack';
-- 假设以上6步发生异常,需要回滚,但是设置了回滚点,因此不需要回退到原状态,只需要回滚到回滚点
rollback to sign;
update finance_account set balance=balance-10000 where name='tony';
update finance_account set balance=balance-10000 where name='tony';
update finance_account set balance=balance-10000 where name='tony';
update finance_account set balance=balance-10000 where name='tony';
update finance_account set balance=balance-10000 where name='tony';
update finance_account set balance=balance+100000 where name='jack';
commit ;
3.4 回滚点的使用场景
再插入大量(百万)数量的时候可以使用,假设插入500万条数据,此时将这个操作为整体,使用事务控制,在插入500万条数据是,前200万成功,此时可以设置一个回滚点,但是后面的300万插入时遇到了异常,此时就可以回退到200万数据设置的回滚点,这样就可以少插入200万条数据。
4. 数据库事务的特性(重要)
- 原子性(Atomicity):指的是数据库事务作为一个不能分割的整体,事务中的操作要么发生,要么都不发生
- 一致性(Consistency):值的是数据库事务前后的数据必须保持一致
- 隔离性(Isolation):数据库事务的隔离性是指多个用户并发操作数据库,一个用户的事务不能被其他用户的事务干扰,多个并发事务之间需要隔离,简单来说,事务之间互不干扰,前提条件是:事务的隔离级别设置得当
- 持久性(Durability):事务一旦提交,他对数据库中数据的变更是永久性的,即使接下来数据库发生了故障,也不会有任何影响,因为事务的提交会将数据写入数据库,无法撤回。
5. 数据库事务的隔离级别
隔离级别设置不当会引发那些问题
多个事务在操作的时候理想状态下,所有事务相互隔离,互不影响。但是由于并发操作,由于数据库事务的隔离级别设置不当,当多个用户访问同一个数据时候,会引发一些并发访问的问题。例如脏读,不可重复读,幻读。
5.1 脏读
指的是:一个事务读到了另一个事务中没有提交的数据
5.2 不可重复读
指的是:一个事务读到了另外一个事务中已经提交的数据。一个事务中两次读取的数据内容不一致,但是要求一个事务中多次读取的内容必须要一致。不可重复读的原因是update导致引发的问题
5.3 幻读
指的是:一个事务中两次读取的数据的数量不一致,要求一个事务中多次读取数据的数量必须一致,这种幻读问题是由于 insert或delete导致的。
5.4 数据库事务的隔离级别
可以通过设置事务的隔离级别来解决脏读、不可重复读和幻读。
级别 | 隔离级别中文名字 | 隔离级别英文名字 | 脏读 | 不可重复读 | 幻读 | 数据库的默认隔离级别 |
---|---|---|---|---|---|---|
1 | 读未提交 | read uncommitted | 是 | 是 | 是 | |
2 | 读未提交 | read commited | 否(没有脏读问题) | 是 | 是 | Oracle |
3 | 可重复读 | repeatable read | 否 | 否 | 是 | MySQL |
4 | 串行化 | serializable | 否 | 否 | 否 |
串行化:指的是一个事务在操作数据,另一个事务只能等待这个事务操作完成之后再进行其他的操作。
设置数据库事务的隔离级别是read commited 就可以解决脏读问题;设置数据库事务的隔离级别是 repeatable read 就可以解决不可重复读问题;设置数据库事务的隔离级别是 serializable 就可以解决幻读问题; 隔离级别越高,安全性越高,但是安全性越差,一般使用数据库的默认隔离级别即可。
5.5 数据库事务隔离级别的查看和设置
查看数据库隔离级别的设置
MySQL 8.0:
select @@transaction_isolation;
设置语法:
set session transaction isolation level [X];
--X:read uncommitted,read commited ,repeatable read,serializable