1.什么是事务?
事务(Transaction)是将⼀组操作封装成⼀个执⾏单元(封装到⼀起),这⼀个执⾏单元要么⼀起执⾏ 成功,要么⼀起失败,不会出现执⾏“⼀半”的情况。
数据库引擎:InnoDB才支持事务,MyISAM不支持事务。
2.为什么⽤事务?
以银⾏转账为例,A 转账 B 转账,那么它的执⾏流程是这样的:
A 账户 -500
B 账户 +500
试想⼀下,如果执⾏了⼀半,断点了或者程序崩溃了,那么 A 账号的钱就永久消失了?那怎么办?⽤事 务就可以解决,封装成⼀个执⾏单元,要么⼀起成功,要么⼀起失败。
使⽤数据库演示⼀下,准备测试表:
create table account(
id int primary key auto_increment,
name varchar(20) comment '账户名称',
money decimal(11,2) comment '⾦额');
insert into account(name, money) values
('张三', 1000),
('李四', 1000);
张三给李四转 500 元,操作如下:
-- 操作1:张三账户减500
1.update account set money=money-500 where name='张三';
-- 李四账户增加500
2.update account set money=money+500 where name = '李四';
假如在执⾏以上第⼀句 SQL 时,出现⽹络错误,或是数据库挂掉了,阿⾥巴巴的账户会减少 500,但是 李四的账户上就没有了增加的⾦额。
3.事务的使用?
3.1开启事务(start transaction)
MySQL8之前使用start transaction,MySQL8之后使用begin
转账业务(多条SQL)
3.2提交事务(commit)
commit
3.3回滚事务(rollback)
rollback
注意事项:
事务在开启之后,必须以提交事务或回滚事务来结束。如果没有提交事务或回滚事务来结束,那么修改是无效的
事务的四大特征(ACID)
事务有4 大特性(ACID),原子性(不可分割性):、一致性和隔离性(独立性)、持久性。
具体概念如下:
原子性(Atomicity):
一个事务中的所有操作,要么全部执行成功,要么全部执行失败。
原子性是事务最重要的特性,全部执行失败并不是不执行,而是通过逆操作 rollback(回滚)数据。
实现原理:回滚是逆 SQL 操作:比如 A 账户 -500 元的逆操作就是 A 账户 +500 元。
一致性(Consistency):
一个事务在执行前后数据必须保持一种合法的状态,事务总是从一个一致状态到另一个一致状态。
举例说明:比如转账操作,转账之前两个账号的总额是 100 元(各个 50 元),那么两个账户相互转账之后的钱总额也是 100 元,也就是事务总是从一个一致状态到另一个一致状态,不会说转账之前是100 元,相互转账之后变成 80 了,这就不符合一致性了。
持久性(Durability):
事务执行完成之后,它所做的所有修改都是永久的(不会丢失)。
数据存储到磁盘中就是持久的,不会丢失的,不会因为电脑重启而丢失。
隔离性(重要)(Isolation):
多个事务并发访问时,事务之间是相互隔离的,一个事务不应该被其他事务干扰,多个并发事务之间要相互隔离。
举例说明:比如宠物店,如果宠物店来了一个狗狗,那么是可以正常的处理它的诉求的,比如打个疫苗啊、美个容啊都是可以很顺利的完成的,但是如果一个 20 平方的宠物商店来了 20 只狗那它们就得打起来了,这个时候怎么办?把它们装到笼子里面“隔离”起来啊,这和咱们的事务的隔离性是一样的,都是解决多个事务同时请求的问题的。
MySQL 事务隔离级别有 4 种(重要)
1. READ UNCOMMITTED(读未提交)[脏读]
读未提交,也叫未提交读,该隔离级别的事务可以看到其他事务中未提交的数据。该隔离级别因为可以读取到其他事务中未提交的数据,而未提交的数据可能会发生回滚,因此我们把该级别读取到的数据称之为脏数据(幽灵数据),把这个问题称之为脏读。
脏读
一个事务读取到了另一个事务修改的数据之后,后一个事务又进行了回滚操作,从而导致
第一个事务读取的数据是错误的。
2. READ COMMITTED(读已提交)[不可重复读]
读已提交,也叫提交读,该隔离级别的事务能读取到已经提交事务的数据,因此它不会有脏读问题。但由于在事务的执行中可以读取到其他事务提交的结果,所以在不同时间的相同 SQL 查询中,可能会得到不同的结果,这种现象叫做不可重复读。
不可重复读
使用相同的SQL,得到了不同的结果,这个就叫做不可重复读。
在一个事务中,两次查询同一条数据得到了不同的结果就是不可重复读。在一个事务两次查询中间,另一个事务把这条数据修改了。
3. REPEATABLE READ(可重复读)[幻读]
可重复读,是 MySQL 的默认事务隔离级别,它能确保同一事务多次查询的结果一致。但也会有新的问题,比如此级别的事务正在执行时,另一个事务成功的插入了某条数据,但因为它每次查询的结果都是一样的,所以会导致查询不到这条数据,自己重复插入时又失败(因为唯一约束的原因)。明明在事务中查询不到这条信息,但自己就是插入不进去,这就叫幻读(Phantom Read)。
不可重复读的事务隔离可以解决不可重复读得问题,但存在幻读问题。
如果不存在幻读问题,那么无论你如何执行,都不会出现幻象行的,也就不会出现多一条数据的问题的(使用相同的SQL)
幻读
在同一事务的不同时间使用相同SQL查询时,会产生不同的结果。
当同一查询在不同时间产生不同的结果,就是事务中的幻读问题。例如,一个 SELECT 被
执行了两次,但是第二次返回了第一次没有返回的一行,那么这一行就是一个“幻像”行。
4. SERIALIZABLE(序列化)
序列化,事务最高隔离级别,它会强制事务排序,使之不会发生冲突,从而解决了脏读、不可重复读和幻读问题,但因为执行效率低,所以真正使用的场景并不多。
MySQL是支持给每个连接客户单独设置事务的隔离级别。
a)查询事务的隔离级别
语法:
select @@global.tx_isolation,@@global.tx_isolation;
b)设置当前客户端的事务隔离级别
语法:
set session transaction isolation level 隔离级别;
设置当前连接的事务隔离级别为读未提交:
结论:读未提交(read uncommitted)存在脏读的习惯
不可重复读和幻读有什么区别?
答:不可重复读和幻读描述的侧重点是不同的。
不可重复读描述的是修改操作,比如t1时间读取了一行数据,t2时间的时候读取到了这个数据,内容发生了改变,这个就叫做不可重复读。
而幻读描述的是幻象行的问题,它是指在t1时间使用SQL得到了n条数据,而在t3时间使用相同的SQL却得到了n+1条数据,那么多出来的这一行数据叫做幻象行,而这个操作就叫做幻读。
也就是说不可重复读描述的侧重点是修改操作,而幻读描述的侧重点是添加(或删除)操作。