事务的概念

比如在一个人员管理系统中,你删除一个人员,就需要删除人员的基本资料、人员相关信息(信箱文档等等),这些数据库操作就构成一个事务。

事务是一个最小的不可再分的工作单元。

一个完整的业务需要批量的 DML(insert、update、delete)语句共同联合完成,事务只和 DML 语句有关,或者说 DML 语句才有事务。这个和业务逻辑有关,业务逻辑不同,DML 语句的个数不同。

所以,在 Mysql 中事务主要用来处理操作量大、复杂度高的数据。

  • mysql中只有 Innodb 数据库引擎的数据库或表才支持事务;
  • 事务处理可以用来维护数据库的完整性。要么全部执行,要么全部不执行;
  • 事务是用来管理 update\ delete\ insert 的操作;

一般来说,事务必须满足4个条件:

  • 原子性:一组事务,要么成功,要么撤回;
  • 稳定性:有非法数据(外键约束之类),事务撤回;
  • 隔离性:事务独立运行。一个事务处理后的结果,影响了其他事务,那么其他事务会撤回。事务的100%隔离,需要牺牲速度;
  • 可靠性:软、硬件崩溃后,InnoDB 数据表驱动会利用日志文件重构修改。可靠性和高速度不可兼得,innodb_flush_log_at_trx_commit 选项决定什么时候把事务保存到日志里。

事务的隔离级别:

  • 读未提交:read uncommitted
  • 读已提交:read committed
  • 可重复读:repeatable read
  • 串行化:serializable

:::info 读未提交:事务A和事务B,事务A未提交的数据,事务B可以读取到;这里读取到的数据叫“脏数据”,这种隔离级别最低,一般是理论上存在,数据库隔离级别一般都高于该级别;
读已提交:事务A和事务B,事务A提交的数据,事务B才能读取到;这种级别可以避免“脏数据”,但这种隔离级别会导致“不可重复读取”;
可重复读:事务A提交之后的数据,事务B读取不到,事务B可以重复读取数据,这种隔离级别高于读已提交;这种级别可以避免“不可重复读取”,达到可重复读取,但是会导致“幻象读”,mysql默认级别;
串行化:事务A和事务B,事务A在操作数据库时,事务B只能排队等待,这种隔离级别很少使用,吞吐量太低,用户体验差;这种级别可以避免“幻象读”,每一次读取都是数据中真实存在数据,事务A和事务B串行,而不并发。 :::

事务会用到 TCL (Transaction Controll Language)语句。

  • commit:提交
  • rollback:回滚

事务提交与回滚实现。

  1. -- 事务提交,一次性提交所有修改
  2. start transaction
  3. DML语句
  4. commit
  5. -- 事务回滚,回滚所有修改
  6. start transaction
  7. DML语句
  8. rollback

mysql中操作事务

通过 begin 开启事务后,执行的数据都只是暂时的插在内存里,还没刷到硬盘上;这时如果你发现有错,就可以通过 rollback 将这些操作撤回;如果没有问题就通过 commit 刷到硬盘中;

commit 之后再 rollback 就没作用了。

/*
 * 事务回滚与提交 
 */

-- 创建人员表
create table if not exists t_Account(
    pid int primary key auto_increment,
    pname varchar(20),
    account decimal(5)
);

-- -- 初始化,张三500,李四100
insert into t_Account(pname, account) values("张三", 500);
insert into t_Account(pname, account) values("李四", 100);

-- 查询数据

select * from t_Account;

image.png

-- 实现转账: 张三转账100给李四,张三余额400,李四余额200
update t_Account set account = account - 100 where pid = 1;
update t_Account set account = account + 100 where pid = 2;

image.png

commit提交事务

为了测试,这里要开两个控制台测试。

-- 控制台A开启事务: 张三再次给李四转账100,张三余额300,李四余额300
start transaction;
update t_Account set account = account - 100 where pid = 1;
update t_Account set account = account + 100 where pid = 2;

-- commit提交事务
commit;



-- 控制台B分别查询查看数据变化
select * from t_Account;

image.png
image.png
image.png

rollback回滚事务

-- 控制台A开启事务: 张三继续给李四转账100,张三余额200,李四余额400
start transaction;
update t_Account set account = account - 100 where pid = 1;
update t_Account set account = account + 100 where pid = 2;

-- commit提交事务
rollback;



-- 控制台B分别查询查看数据变化
select * from t_Account;

image.png
image.png

【注意】如果事务已经 commit 之后,再 rollback 回滚,那么 rollback 无效。

to be continue…