数据库的事务(Transaction)处理技术是很重要的概念,下面结合MySQL讲讲自己对这类概念的理解。
所谓事务是用户定义的、不可分割的一组操作序列,这些操作只能全做或全都不做,不能存在中间状态。涉及到用户定义,MySQL为我们提供了三种定义事务的语句:
start transaction | begin # 开始一个新事务 commit # 提交当前事务,并将修改持久化到数据库 rollback # 回滚当前事务,取消所有操作
而MySQL初始设置会将autocommit(自动提交)设置为1,表示用户对数据库的每个操作都作为一个事务自动提交,仅当使用 start transaction 或 begin 开始一个新事务后,autocommit才会临时失效,直到出现事务结束语句(commit | rollback)。(在事务未结束之前开启另一个新事务会自动commit)
注意:DDL语句是不能回滚的,包括create、drop、alter等。
事务具有4个特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),简称ACID特性。
原子性:事务作为数据库的逻辑工作单位,是不可分割的,事务内的操作要么全做,要么全不做。
一致性:事务的执行结果必须使数据库从一个一致性状态转换到另一个一致性状态。一致性是和原子性密切相关的特性,一致性状态的转换必然要经过原子性的操作。若数据库在完成事务时突然发生故障而中断,原子性不能保证,那么数据库内的数据就会出错,处于不一致的状态。
隔离性:事务的执行不能互相干扰,即一个事务内的操作和使用的数据对其他并发事务是隔离的,不可见的。事务将数据库由一个一致性状态转换为另一个一致性状态时,不能出现非本事务内的操作或数据,即不能被其他事务干扰。
持久性:一个事务一旦提交,它对数据库的修改应该是永久性的,不会因接下来的操作或故障影响已提交的事务。
事务是恢复和并发控制的基本单位,要保证事务的正常就一定要保证事务的ACID特性。事务的ACID特性被破坏可能有以下的原因:
第2个因素会导致事务的原子性遭到破坏,进而数据库处于不一致的状态,避免这类问题是数据库恢复机制的责任,这里暂时不提。第1个因素则会破坏事务的原子性、一致性和隔离性,避免这类问题也就是接下来要说的并发控制的责任。
考虑一个超市的两台收银机A、B的活动序列:
不考虑ACID,最终的营业额是X+b,A的修改丢失,导致事务A处于不一致的状态,这种不一致的状态是由并发操作引起的。
并发带来的问题可以分为三种:1. 丢失修改;2.不可重复读;3.读“脏”数据。丢失修改也叫脏写,MySQL的所有隔离级别都不允许脏写。下面看其余两种问题。
也称脏读。在解释脏读前,建议先看一下关于InnoDB的脏页刷新机制:MySQL中InnoDB脏页刷新机制Checkpoint
简单来说,写回磁盘的数据可能是事务提交后的数据,也可能是事务进行中未提交的数据。每次写回磁盘都会建立一个检查点CheckPoint,内存中未写回磁盘的为脏页,事务未提交的数据是脏数据。
下面模拟一下MySQL的脏读。打开两个命令行窗口,并连接到MySQL,运行show processlist发现两个进程分别对应两个窗口的session,我们用这两个窗口模拟并发执行事务的情况。
首先设置窗口的隔离级别为READ UNCOMMITTED
(读未提交)
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
新建一张表my_table,包含两个int类型的列id、cost,id作为主键,插入一条数据,然后按以下顺序执行:
MySQL解决这个问题的方法是设置隔离级别为READ COMMITTED(读已提交)。
不可重复读就是A事务在读取某个数据后,事务B对其进行的修改会让事务A在事务未结束之前读出来,导致了A事务两次读取同一字段时数据不同,数据库状态不一致。
SQL中对数据的修改分为更新、插入和删除,分别对应三种不可重复读的问题,后两种问题也被称为“幻读”。下面我们在MySQL复现这些问题。
首先设置MySQL的隔离级别为READ COMMITTED(读已提交)
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
然后按以下顺序执行:
MySQL解决这个问题的方法是设置隔离级别为REPEATABLE READ(可重复读)。
幻读也是一种不可重复读的问题,只是侧重于记录的新增和删除(第一类侧重于修改)。
首先设置MySQL的事务隔离级别为READ COMMITTED(读已提交)
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
然后按以下顺序执行复现(注意id为自增主键):
但是用REPEATABLE READ是不是就完全解决了幻读问题呢?并不是,可重复读级别只能解决一部分幻读问题,还有另一种问题会出现。
设置MySQL的事务隔离级别为REPEATABLE READ(可重复读)
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
按以下顺序复现可重复读的幻读问题:
删除记录的复现也类似,表现为A已删除一条记录并提交事务,但是B还是可以查到已删除的记录,而且B不能删掉,像是出现幻觉一样。
(左侧为A事务,右侧为B事务)
这类幻读问题的解决是对查询的数据加锁(select * from my_table where id > 2 for update),这样B可以锁住id大于2的记录的插入,A只有等待B事务提交后才可成功插入记录。
MySQL完全解决幻读问题的方法是设置隔离级别为SERIALIZABLE
(串行化)。但是串行化对数据库的并发性能影响较大,一般不采用这个级别的隔离。
MySQL提供了4种隔离级别,分别为
原文:https://www.cnblogs.com/fengg123/p/11030220.html