文章目录
- 第十三章 TCL
- 13.1 事务的介绍
- 13.2 事务的特性
- 13.3 MySQL的事务
- 13.3.1 隐式事务
- 13.3.2 显式事务
- 13.4 并发事务
- 13.4.1 并发事务出现的问题
- 13.4.2 事务的隔离级别
第十三章 TCL
13.1 事务的介绍
我们先来看一个场景:
假如把每一张银行卡的信息存入数据库的表中进行存储,每一张表中存储有银行卡的卡号、余额信息。小明需要给小红转账1000元钱,那么在数据库中需要进行的操作是什么?- 将小明的银行卡余额,减1000- 将小红的银行卡余额,加1000那么,如果在上述的操作中,如果第一步成功了,小明的余额已经减过了。但是在给小红的银行卡余额增1000的时候出现了问题,导致本次操作失败了。那么本次转账整体失败,小明的余额也需要回滚到减1000之前的状态。
当一个业务需求涉及到多个DML操作时,这个业务(或者多个DML操作)当成一个整体来处理。在处理的过程中,如果有失败或异常,我们要回到业务开始时。如果成功处理,我们再将数据持久化到磁盘中。这样一个过程我们称之为一个事务。事务具有原子性。不可切割。
总结: 事务指逻辑上的一组操作,组成这组操作的各个单元,要么全成功,要么全不成功。
13.2 事务的特性
-
原子性(Atomicity)
指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
-
一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。转账前和转账后的总金额不变。
-
隔离性(Isolation)
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
-
持久性(Durability)
指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
13.3 MySQL的事务
13.3.1 隐式事务
隐式事务,或者称为自动事务。不需要进行额外的控制,每一条DML语句都是一个事务,可以自动提交。
- 事务开始于
- 连接到数据库上,并执行一条DML语句insert、update或delete。
- 前一个事务结束后,又输入了另一条DML语句。
- 事务结束于
- 执行commit或rollback语句。
- 执行一条DDL语句,例如create table语句,在这种情况下,会自动执行commit语句。
- 执行一条DCL语句,例如grant语句,在这种情况下,会自动执行commit。
- 断开与数据库的连接。
- 执行了一条DML语句,该语句却失败了,在这种情况中,会为这个无效的DML语句执行rollback语句。
13.3.2 显式事务
-- 禁用自动提交的功能
set autocommit = 0;-- 开启一个事务(可以省略不写)
start transaction;-- 执行一组DML操作
...-- 设置回滚点
savepoint spName;-- 结束事务
commit; -- 提交事务
rollback; -- 回滚到事务的起点
rollback to spName; -- 回滚到指定的回滚点
13.4 并发事务
13.4.1 并发事务出现的问题
如果有多个事务,同时操作同一个数据库中的同一个数据的时候,会出现如下的问题:
-
脏读: 事务A读取了事务B刚刚更新的数据,但是事务B回滚了,这样就导致事务A读取的为脏数据,我们称之为脏读。
如公司某财务人员更新公司入账报表时,在DML语句中的数字后少添加了一个0,但是未提交。然后吃饭,吃饭回来,发现错误,然后更正后做了提交。而在吃饭期间,老板要求秘书查看一下报表,秘书看到的是少个0的数据。这就是脏读。
-
不可重复读: 事务A读取同一条记录两次,但是在两次之间事务B对该条记录进行了修改并提交,导致事务A两次读取的数据不一致。
它和脏读的区别是: 脏读是事务A读取了另一个事务B未提交的脏数据,而不可重复读则是事务A读取了事务B提交的数据。多数情况下,不可重复读并不是问题,因为我们多次查询某个数据时,当然要以最后查询得到的结果为主。但在另一些情况下就有可能发生问题,比如,老板让B和C分别核对事务A操作的数据,结果可能不同,老板是怀疑B呢,还是C呢?
-
幻读: 事务A在修改全表的数据,比如将字段age全部修改为0岁,在未提交时,事务B向表中插入或删除数据,如插入一条age为25
岁的数据。这样导致事务A读取的数据与需要修改的数据不一致,就和幻觉一样。幻读和不可重复读的相同点: 都是针对于另外一个已经提交的事务而言。不同点: 不可重复读是针对于同一条记录来说的 (delete或update同一条记录, 而幻读是针对于一批数据来说的 (insert)。
13.4.2 事务的隔离级别
我们知道了多个事务同时对相同的数据进行处理的时候,会出现上述的问题,那么应该怎么去解决这个问题呢?
可以通过设置事务的隔离级别
-
未提交读(read uncommitted)
就是不做隔离控制,可以读到 “脏数据“,可能发生不可重复读,也可能出现幻读。
-
提交读(read committed)
提交读就是不允许读取事务没有提交的数据。显然这种级别可以避免了脏读问题。但是可能发生不可重复读,幻读。这个隔离级别是大多数数据库的默认隔离级别。(除了MySQL)
-
可重复读(repeatable read)
为了避免提交读级别不可重复读的问题,在事务中对符合条件的记录上"排他锁”,这样其他事务不能对该事务操作的数据进行修改,可避免不可重复读的问题产生。由于只对操作数据进行上锁的操作,所以当其他事务插入或删除数据时,会出现幻读的问题,此种隔离级别为Mysql默认的隔离级别。
-
序列化(Serializable)
在事务中对表上锁,这样在事务结束前,其他事务都不能够对表数据进行操作(包括新增,删除和修改)。这样避免了脏读,不可重复读和幻读,是最安全的隔离级别。但是由于该操作是堵塞的,因此会严重影响性能。
避免脏读 | 避免不可重复读 | 避免幻读 | |
---|---|---|---|
read uncommitted | × | × | × |
read committed | √ | × | × |
repeatable read | √ | √ | × |
Serializable | √ | √ | √ |
-- 查看当前隔离级别
select @@transaction_isolation;
-- 设置当前MysqL连接的级别
set transaction isolation level read committed;
-- 设置数据库系统的全局隔离级别
set global transaction isolation level read committed;
-- 一般需要重启生效