目录
事务简介
事务是一组操作的集合,它是一个不可分割的工作单元,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败
一个完整的业务需要批量的DML(insert、update、delete)语句共同联合完成
事务只和DML语句有关,或者说DML语句才有事务。这个和业务逻辑有关,业务逻辑不同,DML语句的个数不同
就比如:用户1需要向用户2转账100元
actno balance
1 500
2 100
进行转账操作:
update t_act set balance=400 where actno=1;
update t_act set balance=200 where actno=2;
- 以上两台DML语句必须同时成功或者同时失败
- 最小单元不可再分,当第一条DML语句执行成功后, 并不能将底层数据库中的第一个账户的数据修改,只是将操作记录了一下
- 这个记录是在内存中完成的,当第二条DML语句执行成功后,和底层数据库文件中的数据完成同步
- 若第二条DML语句执行失败,则清空所有的历史操作记录,要完成以上的功能必须借助事务
注意:默认MySQL的事务是自动提交的,也就是说,当执行完一条DML语句时,MySQL会立即隐式的提交事务
事务操作
1.查看/设置事务提交方式
SELECT @@autocommit ;
SET @@autocommit = 0 ;
2.开启事务
START TRANSACTION 或 BEGIN ;
3.提交事务
COMMIT;
4.回滚事务
ROLLBACK;
案例:
(1)提交操作---无事务成功
update t_act set balance=400 where actno=1;
update t_act set balance=200 where actno=2;
(2)提交操作---无事务失败
update t_act set balance=400 where actno=1;
update t_act set balance=200 where actno=2w;
(3)提交操作---事务成功
start transaction;#手动开启事务
update t_act set balance=400 where actno=1;
update t_act set balance=200 where actno=2;
commit;#commit之后即可改变底层数据库数据
(4)提交操作---事务失败
start transaction;#手动开启事务
update t_act set balance=400 where actno=1;
update t_act set balance=200 where actno=2w; #这个地方故意让第二个语句出错
commit;#commit之后即可改变底层数据库数据
(5)回滚操作---事务失败
start transaction; #开启事务
update act set money = 0 where name = '张三';
update act set money = 200 where name = '李四';
-- 假设这里出现了错误,可以回滚事务
ROLLBACK;
-- 如果没有错误,可以提交事务
COMMIT;
事务四大特性ACID
1.原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败
当事务发现有些语句不能执行时,需要将数据恢复到事务执行前,通过undo log实现
undo log是mysql中比较重要的事务日志之一,顾名思义,undo log是一种用于撤销回退的日志, 在事务没提交之前,MySQL会先记录更新前的数据到 undo log日志文件里面, 当事务回滚时或者数据库崩溃时,可以利用 undo log来进行回退
2.一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态
原子性、持久性和隔离性,都是为了保证数据库状态的一致性
3.隔离性(Isolation):事务内部的操作与其他事务是隔离的,并发执行的各个事务之间不能互相干扰
与原子性、持久性侧重于研究事务本身不同,隔离性研究的是不同事务之间的相互影响
4.持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的
持久性问题的产生:
背景:Mysql为了保证存储效率,每次读写文件都是先对缓存池(Buffer Pool)操作, 缓冲池再定期刷新到磁盘中(这一过程称为刷脏)
产生:由于数据不是直接写到磁盘,那么如果主机断电,就会有一部分数据丢失
解决:通过重做日志(redo log)恢复数据。在每次修改数据之前, 都会将相应的语句写到redo log中,如果主机断电,那么再次启动时可通过redo log恢复
拓展:redo log也需要在事务提交时将日志写入磁盘,它比缓冲池写入快的原因有两点:redo log是追加文件写,属于顺序IO,缓冲池是属于随机IO,且刷脏是以页为单位, 有一点修改就要整页写入
并发事务问题
1.脏读
一个事务读到另外一个事务修改但还没有提交的数据
2.不可重复读
一个事务先后读取同一条记录,但两次读取的数据不同
3.幻读
在一个事务中,多次查询同一个范围的数据,却发现有新增或者减少的行。这是因为在这个事务进行的过程中,另一个事务插入或者删除了符合查询条件的数据,导致前后两次查询结果不一致
事务隔离级别
为了解决并发所产生的问题,在数据库中引入了事务的隔离级别,隔离级别越高,数据越安全,但是性能越低,解决并发产生的问题越多
1.读未提交Read uncommitted
- 事物A和事物B,事物A未提交的数据,事物B可以读取到
- 这种隔离级别最低,这种级别一般是在理论上存在,数据库隔离级别一般都高于该级别
- 三种并发问题都没解决
演示:
MySQL8中隔离级别的变量跟之前的版本不一样,之前是tx_isolation,MySQL8改成了transaction_isolation
#创建表:
create table t_user(id int primary key auto_increment,username varchar(255));
#设置读未提交的隔离级别
set global transaction isolation level read uncommitted;
#查看当前隔离级别
select @@global.tx_isolation,@@tx_isolation;
2.读已提交Read committed
- 事物A和事物B,事物A提交的数据,事物B才能读取到
- 这种隔离级别高于读未提交
- 换句话说,只有对方事务提交之后的数据,当前事务才能读取到
- 这种级别可以避免“脏数据”
- 这种隔离级别会导致“不可重复读取”
- Oracle默认隔离级别
#设置读已提交的隔离级别
set global transaction isolation level read committed;
#查看当前隔离级别
select @@global.tx_isolation,@@tx_isolation;
插入但没有提交是读不出来的,提交之后才能读出来
不可重复读和脏读的区别:脏读读取到的是一个未提交的数据,而不可重复读读取到的是前一个事务已提交的数据。 而不可重复读在一些情况也并不影响数据的正确性,比如需要多次查询的数据也是要以最后一次查询到的数据为主
3.可重复读Repeatable Read(默认)
- 事务A和事务B,事务A提交之后的数据,事务B读取不到,事务B是可重复读取数据
- 这种隔离级别高于读已提交
- 换句话说,对方提交之后的数据,还是读取不到
- 这种隔离级别可以避免“不可重复读取”,达到可重复读取,比如1点和2点读到数据是同一个
- MySQL默认级别
- 解决了 “不可重复读” 和 “幻读”
#设置可重复读的隔离级别
set global transaction isolation level repeatable read;
#查看当前隔离级别
select @@global.tx_isolation,@@tx_isolation;
无论是删除还是添加,commit后都是成功的,但是另一边却还是读出原来的数据,这就是可重复读,读取的是备份数据,不是真正的数据
4.串行化Serializable
- 事务A和事务B,事务A在操作数据库时,事务B只能排队等待
- 这种隔离级别很少使用,吞吐量太低,用户体验差
- 这种级别可以避免“幻读”,每一次读取的都是数据库中真实存在数据,事务A与事务B串行, 而不并发
#设置串行化的隔离级别
set global transaction isolation level serializable;
#查看当前隔离级别
select @@global.tx_isolation,@@tx_isolation;