目录
1.什么是事务
事务(Transaction)是将⼀组操作封装成⼀个执行单元(封装到⼀起),这⼀个执⾏单元要么⼀起执行成功,要么⼀起失败,不会出现执行“⼀半”的情况。
2.为什么用事务
以银⾏转账为例,A 转账 B 转账,那么它的执⾏流程是这样的:
- A 账户 -500
- B 账户 +500
试想⼀下,如果执⾏了⼀半,断点了或者程序崩溃了,那么 A 账号的钱就永久消失了?那怎么办?⽤事 务就可以解决,封装成⼀个执⾏单元,要么⼀起成功,要么⼀起失败。
使⽤数据库演示⼀下,准备测试表:
drop table if exists accout;
create table account(
id int primary key auto_increment,
name varchar(20) comment '账户名称',
money decimal(11,2) comment '⾦额'
);
insert into account(name, money) values
('张三', 5000),
('李四', 1000);
张三给李四转 500 元,操作如下:
-- 张三账户减少2000
update account set money=money-500 where name = '张三';
-- 李四账户增加2000
update account set money=money+500 where name = '李四';
假如在执⾏以上第⼀句 SQL 时,出现⽹络错误,或是数据库挂掉了,张三的账户会减少 500,但是李四的账户上就没有了增加的⾦额。
解决方案:使用事务来控制,保证以上两句SQL要么全部执行成功,要么全部执行失败。
3.事务怎么用
事务的使⽤步骤有三个:
- 开启事务(start transaction)
- 执⾏多条 SQL
- 提交/回滚事务(commit/rollback)
比如上⾯的张三给李四转账,咱们的事务执⾏ SQL 如下:
-- 开启事务
start transaction;
-- 张三账户减少2000
update account set money=money-2000 where name = '张三';
-- 李四账户增加2000
update account set money=money+2000 where name = '李四';
-- 提交事务
commit;
4.事务的四大特性
事务有4 ⼤特性(ACID),原⼦性、持久性、⼀致性 和 隔离性,具体概念如下:
4.1 原子性 (Atomicity)
⼀个事务中的所有操作,要么全部执行成功,要么全部执行失败。原⼦性是事务最重要的特性,全部执⾏失败并不是不执⾏,⽽是通过逆操作 rollback(回滚)数据。
实现原理:回滚是逆 SQL 操作:⽐如 A 账户 -500 元的逆操作就是 A 账户 +500 元。
4.2 持久性 (Durability)
事务执⾏完成之后,它所做的所有修改都是永久的(不会丢失)。
4.3 ⼀致性 (Consistency)
⼀个事务在执⾏前后数据必须保持⼀种合法的状态,事务总是从⼀个⼀致状态到另⼀个⼀致状态。
4.4 隔离性 (Isolation)
多个事务并发访问时,事务之间是相互隔离的,⼀个事务不应该被其他事务⼲扰,多个并发事务之间要相互隔离。
4.4.1 举例说明:
比如宠物店,如果宠物店来了⼀个狗狗,那么是可以正常的处理它的诉求的,⽐如打个疫 苗啊、美个容啊都是可以很顺利的完成的,但是如果⼀个 20 平⽅的宠物商店来了 20 只狗那它们就得 打起来了,这个时候怎么办?把它们装到笼⼦⾥⾯“隔离”起来啊,这和咱们的事务的隔离性是⼀样
的,都是解决多个事务同时请求的问题的。
4.4.2 MySQL 事务隔离级别有 4 种:
- 读未提交(READ UNCOMMITTED):也叫未提交读,该隔离级别的事务可以看到其他事务中未提交的数据。该隔离级别因为可以读取到其他事务中未提交的数据,而未提交的数据可能会发⽣回滚, 因此我们把该级别读取到的数据称之为脏数据,把这个问题称之为脏读。
- 读已提交(READ COMMITTED):也叫提交读,该隔离级别的事务能读取到已经提交事务的数据, 因此它不会有脏读问题。但由于在事务的执⾏中可以读取到其他事务提交的结果,所以在不同时间 的相同 SQL 查询中,可能会得到不同的结果,这种现象叫做不可重复读。
- 可重复读(REPEATABLE READ):是 MySQL 的默认事务隔离级别,它能确保同⼀事务多次查询的 结果⼀致。但也会有新的问题,⽐如级别的事务正在执⾏时,另⼀个事务成功的插⼊了某条数 据,但因为它每次查询的结果都是⼀样的,所以会导致查询不到这条数据,⾃⼰重复插⼊时⼜失败 因为唯⼀约束的原因)。明明在事务中查询不到这条信息,但⾃⼰就是插⼊不进去,这就叫幻读 (Phantom Read)。
- 序列化(SERIALIZABLE):事务最高隔离级别,它会强制事务排序,使之不会发⽣冲突,从而解决了脏读、不可重复读和幻读问题,但因为执行效率低,所以真正使⽤的场景并不多。
注意:
- MySQL是支持给每个连接客户端单独设置事务的隔离级别的。
- 查询事务的隔离级别:
select @@global.tx_isolation, @@tx_isolation;
-
设置当前事务的隔离级别:
set session transaction isolation level 隔离级别
脏读:⼀个事务读取到了另⼀个事务修改的数据之后,后⼀个事务⼜进⾏了回滚操作,从而导致 第⼀个事务读取的数据是错误的。
演示:开启另一个客户端2
不可重复读:在⼀个事务中,两次查询同⼀条数据得到了不同的结果就是不可重复读。在⼀个事 务两次查询中间,另⼀个事务把这条数据修改了。
演示:
幻读:当同⼀查询在不同时间产⽣不同的结果,就是事务中的幻读问题。例如,⼀个 SELECT 被 执⾏了两次,但是第⼆次返回了第⼀次没有返回的⼀⾏,那么这⼀⾏就是⼀个“幻像”⾏。
演示: