目录
一、事务简介
二、事务操作方式
1、设置事务提交方式
1.1、命令
1.2、示例
2、开启事务
2.1、命令
2.2、示例
三、事务四大特性
四、并发事务问题
五、事务隔离级别
5.1、命令
5.2、示例
5.2.1、脏读
5.2.2、不可重复读
5.2.3、幻读
一、事务简介
- 事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。
- 例如,张三要从银行向李四转1000元,银行需要正确操作3个步骤才能完成转账,如果其中某个步骤出错,则会出现异常,那么就可以把这3个步骤当作是一个事务。
- 在操作前,开启事务,正确完成所有操作后,才提交事务,否则回滚事务。
- 回滚事务:把之前临时修改的数据,全部恢复。
二、事务操作方式
- 新建一个用户数据表。
-
create table account( id int auto_increment primary key comment '主键ID', name varchar(10) comment '姓名', money int comment '余额' )comment '账户表';
-
- 向用户数据表中插入两条用户数据。
-
insert into account(id, name, money) VALUES (null,'张三',2000),(null,'李四',2000);
-
1、设置事务提交方式
1.1、命令
- MySQL的事务提交方式默认是自动的,可以设置为手动提交方式。
-
# 查看事务的提交方式,1为自动,0为手动。 SELECT @@autocommit ; # 设置事务提交方式为手动0 SET @@autocommit = 0;
-
- 提交事务
-
COMMIT;
-
- 回滚事务
-
ROLLBACK;
-
1.2、示例
- 查看事务提交的方式。
- 此时为自动提交,将其修改为手动提交。
- 转账操作总共为三步。
-
# 1、查询张三账户余额 select * from account where name = '张三'; # 2、将张三账户余额-1000 update account set money = money - 1000 where name = '张三'; # 3、将李四账户余额+1000 update account set money = money + 1000 where name = '李四';
-
- 依次输入上面三条命令执行。
- 刷新用户表,发现张三和李四的余额并没有任何变化,因为此时事务提交方式已经改为了手动。需要另外执行指令才能完成操作。
- 提交事务,转账成功。
- 假设一个操作出错的情况。
- 在执行第三条操作的时候,报错了,此时就不能将这个事务进行提交,不然会出错。
- 回滚事务,张三和李四的余额并没有任何变化。
2、开启事务
2.1、命令
- 开启事务
-
START TRANSACTION; 或 BEGIN;
-
- 提交事务
-
COMMIT;
-
- 回滚事务
-
ROLLBACK;
-
2.2、示例
- 先把事务提交方式修改回自动提交。
- 直接测试异常情况,开启事务,执行下列指令。
-
select * from account where name = '张三'; update account set money = money - 1000 where name = '张三'; 操作出错。。。 update account set money = money + 1000 where name = '李四';
- 此时发生异常,但表中的数据并未发生变化。执行回滚即可恢复。
-
三、事务四大特性
- 原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败。
- 一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态。
- 隔离性(lsolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。
- 持久性(Durabilitv):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。
四、并发事务问题
- 两个事务同时操作数据库时所引发的一些问题。
- 脏读:一个事务读到另外一个事务还没有提交的数据。
- 不可重复读:一个事务先后读取同一条记录,但两次读取的数据不同,称之为不可重复读。
- 幻读:一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在,好像出现了“幻影”。
五、事务隔离级别
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read uncommitted | √ | √ | √ |
Read committed | × | √ | √ |
Repeatable Read(默认) | × | × | √ |
Serializable | × | × | × |
- 【注】事务隔离级别越高,数据越安全,但是性能越低
5.1、命令
- 查看事务隔离级别
-
SELECT @@TRANSACTION_ISOLATION;
-
- 设置事务隔离级别
-
SET [SESSION|GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE} # session 只针对当前对话端有效 # global 针对所有对话端有效
-
5.2、示例
- 打开两个终端,模拟两个事务同时对数据库进行操作。
5.2.1、脏读
- 将其中一个终端的事务隔离级别设置为Read uncommitted。
- 模拟读取脏数据的情况。
- 此时,左侧第二次读取的数据为脏数据,因为右侧终端的事务并未提交。
- Read committed可以解决脏读的问题。先将两个终端都rollback。
- 将其中一个终端的事务隔离级别设置为Read committed。
- 模拟读取脏数据的情况。
- 此时,左侧第二次读取的数据并未发生改变。
- 将右侧终端的事务提交,左侧读取的数据才发生改变。
5.2.2、不可重复读
-
此时事务隔离级别为Read committed,继续使用两个终端进行下列操作(之前的事务开启了记得关闭)。
- 左侧两次读取的数据不相同,这就是不可重复读问题。
- Repeatable Read可以解决不可重复读问题,先将左侧终端commit。
- 将左侧终端的事务隔离级别设置为Repeatable Read。
- 重新模拟不可重复读的情况。
- 此时左侧两次读取的数据相同。
- 将左侧事务提交后,再次查询才看到数据被修改了。
5.2.3、幻读
- 此时事务隔离级别为Repeatable Read,继续使用两个终端进行下列操作。
- 左侧先查询id=3的用户,发现该用户不存在;右侧此时插入id=3的用户,并且提交了事务;左侧此时因为没有查询到id=3的用户而要插入id=3的用户,但发现id=3的用户已经存在。再次查询发现,id=3的用户依旧不存在。此时就出现了幻读的问题。
- Serializable(串行化)可以解决幻读问题,先将左侧终端commit。
- 将左侧终端的事务隔离级别设置为Serializable。
- 重新模拟幻读的情况。
- 左侧查询发现没有id=4的用户后,右侧想要执行插入操作却并未成功,而是需要等待左侧事务提交后才能进行操作。