1. 事务定义
transaction
在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务
事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行
事务用来管理 insert,update,delete 语句
2. 添加订单帮助理解事务
检查数据库中是否存在相应的客户(从customers表查询),如果 不存在,添加他/她。
检索客户的ID。
添加一行到orders表,把它与客户ID关联。
检索orders表中赋予的新订单ID。
对于订购的每个物品在orderitems表中添加一行,通过检索 出来的ID把它与orders表关联(以及通过产品ID与products表关联)。
3. 事务控制语句
BEGIN / START TRANSACTION:显示开始一个事务
SAVEPOINT delete1:保存点
COMMIT:提交事务,写入数据库
ROLLBACK:回滚,撤销事务
RELEASE SAVEPOINT:释放保存点,或者事务处理完毕自动释放
ROLLBACK TO delete1:回滚到保存点
SET TRANSACTION 用来设置事务的隔离级别。InnoDB 存储引擎提供事务的隔离级别有READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ 和 SERIALIZABLE
4. MySQL事务处理有两种方法
BEGIN / START TRANSACTION
SET AUTOCOMMIT = 0; 关闭自动提交
SET AUTOCOMMIT = 1; 开启自动提交
5. 事务四个特性
事务本身不包含这四个特性,通过一些操作,使事务尽可能满足这四个特性
5.1 原子性(Automicity)
原子性指事务包含的所有操作要么全部成功,要么全部失败回滚
虽然可以通过SAVEPOINT实现部分回滚,实现灵活选择回退点
原子性通过undo log保障,事务操作首先于内存
序号
动作
1
开始事务
2
记录数据行数据快照到undo log
3
更新数据
4
将undo log 写到磁盘
5
将数据写到磁盘
6
提交事务
1)为了保证数据的持久性,数据需要在事务提交之前提交
2)保证能够回滚数据,undo log需要先于数据写入磁盘,保证系统崩溃时,回滚;如果undo log还没有记录,则数据必然未写入磁盘,因此无需回滚
3)undo log是基于行的逻辑日志,update 则记录相反的update语句,delete则insert
5.2 一致性(Consistency)
事务必须使数据库从一个一致性状态转移到另一个一致性状态。例如两个账户转账,之前总和1000元,不管转账几次,之后总和还是1000
一致性由原子性、隔离性、持久性综合保障
5.3 隔离性(Isolation)
数据库为用户开启的事务,在执行过程中,不应该收到其他并发事务的影响,需要隔离开来
5.4 持久性(Durability)
持久性保障事务提交之后的修改,即为永久的修改,通过redo log日志保障
redo log是末尾追加的,记录数据库的操作,
6. 隔离级别
6.1 并发情况下事务引发的问题
脏读(Dirty Reads):A事务读取B事务尚未提交的数据并在此基础上操作,而B事务执行回滚,那么A读取到的数据就是脏数据。 (破坏隔离性)
时间
转账事务A
取款事务B
T1
开始事务
T2
开始事务
T3
查询账户余额1000
T4
取500余额修改为500
T5
查询账户余额为500(脏读)
T6
撤销事务,余额恢复到1000
T7
汇入100,余额修改为600
T8
提交事务
解决办法:一个事务提交前,其他事务不可读取他修改过的数据
不可重复读(Non-repeatable Reads): 一个事务对一个数据进行第一次读取和第二次读取,数据不一致。不可重复读(破坏一致性,delete, update)
时间
转账事务A
取款事务B
T1
开始事务
T2
开始事务
T3
查询账户余额为1000元
T4
查询账户余额为1000元
T5
取款100元,修改余额为900元
T6
提交事务
T7
查询账户余额为900元(不可重复读)
解决办法:只有修改事务完成提交之后,才可读取数据
幻读:对自己未操作的数据,进行多次读取,第一次读取时,记录不存在;第二次读取时,记录出现了(破坏一致性,insert)
更新丢失(Update Lost):如果多个线程操作,基于同一个查询结构对表中的记录进行修改,那么后修改的记录将会覆盖前面修改的记录,前面的修改就丢失掉了,这就叫做更新丢失。这是因为系统没有执行任何的锁操作,因此并发事务并没有被隔离开来。
第1类丢失更新:事务A撤销时,把已经提交的事务B的更新数据覆盖了。
时间
转账事务A
取款事务B
T1
开始事务
T2
开始事务
T3
查询余额为1000元
T4
查询余额为1000元
T5
汇入100元,修改余额为1100元
T6
提交事务
T7
取出100元,余额为900元
T8
撤销事务
T9
余额变为1000元(丢失更新)
第二类丢失更新:事务A提交,覆盖事务B的更新
时间
转账事务A
取款事务B
T1
开始事务
T2
开始事务
T3
查询余额为1000元
T4
查询余额为1000元
T5
汇入100元,修改余额为1100元
T6
提交事务
T7
取出100元,余额为900元
T8
提交事务
T9
余额变为900元(丢失更新)
解决方法:对行加锁,仅允许并发一个更新事务
6.2 事务隔离级别
Read uncommitted(未授权读取、读未提交): 最低一级,只保证持久性 未提交写禁止其他写
如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。这样就避免了更新丢失,却可能出现脏读。也就是说事务B读取到了事务A未提交的数据。
Read committed(授权读取、读提交): 语句级别 未提交写禁止读
读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。该隔离级别避免了脏读,但是却可能出现不可重复读。事务A事先读取了数据,事务B紧接了更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变。
Repeatable read(可重复读取): 事务级别 读事务禁止写,未提交写禁止其他事务
可重复读是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,即使第二个事务对数据进行修改,第一个事务两次读到的的数据是一样的。这样就发生了在一个事务内两次读到的数据是一样的,因此称为是可重复读。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。这样避免了不可重复读取和脏读,但是有时可能出现幻象读。(读取数据的事务)这可以通过“共享读锁”和“排他写锁”实现。
Serializable(序列化): 最高级别,事务串行化了
提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。序列化是最高的事务隔离级别,同时代价也花费最高,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻像读。
注意:这四个只是标准,各个数据库并不完全按照实现
6.3 实现(InnoDB)
锁机制:阻止其他事务对数据进行操作,各个隔离级别主要体现在加的锁与释放锁的时机
RU(Read Uncommitted):事务读取时,不加锁
RC(Read Committed):事务读取时加行级共享锁(读到才加锁),一旦读完立即释放(不是事务提交)
RR(Repeatable Read):事务读取时加行级共享锁,事务结束,释放锁
SE(Serializable):事务读取时加表级共享锁,事务结束,释放锁
MVCC机制:生成数据快照,快照提供一定级别的一致性读取,也称为多版本控制
实际就是CAS版本控制和读写分离思想
主要用于RC与RR级别