MySQL 事务机制
事务机制:
事务语法:
-- 开始事务
begin;
-- 或
start transaction;
-- 提交
commit;
-- 回滚
rollback;
-- 保存点
savepoint;
事务特性:
默认事务:
MySQL的事务 默认自动提交:在自动提交的状态下 每一条SQL就是一个事务 会被直接执行
手动开启事务后:则所有的SQL语句都在一个事务中 直到执行了commit或rollback
事务原则:
-
原子性(Atomicity)
定义:
- 原子性是指事务包括的所有操作要么成功 要么全部失败回滚
实现:
- InnoDB实现回滚 靠的是undo log(回滚日志)
- 实现原子性的关键 是当事务回滚时能够撤销所有已经成功执行的sql语句
-
一致性(Consistency)
定义:
-
事务执行结束后 数据库的完整性约束没有被破坏 事务执行的前后都是合法的数据状态
如果回滚数据 回滚的数据需要跟原来保持一致数据库的完整性约束包括但不限于:
实体完整性(如行的主键存在且唯一)、列完整性(如字段的类型、大小、长度要符合要求)、外键约束、用户自定义完整性(如转账前后,两个账户余额的和应该不变)
实现:
- 保证原子性、持久性和隔离性 如果这些特性无法保证 事务的一致性也无法保证
- 数据库本身提供保障,例如不允许向整形列插入字符串值、字符串长度不能超过列的限制等
- 应用层面进行保障,例如如果转账操作只扣除转账者的余额,而没有增加接收者的余额,无论数据库实现的多么完美,也无法保证状态的一致
- 一致性原则的维护相对于其他三个 更偏向
数据的规范
-
-
隔离性(Isolation)
定义:
- 多个并发事务之间要相互隔离 对应的是MySQL的可串行化级别 但是在实际中 因为性能的原因很少使用可串行化级别
实现:
- (一个事务)写操作对(另一个事务)写操作的影响:锁机制保证隔离性
- (一个事务)写操作对(另一个事务)读操作的影响:MVCC保证隔离性
-
持久性(Durability)
定义:
- 事务一旦被提交了 对数据库中的数据是永久性的 此时即使系统崩溃修改的数据也不会丢失
实现:
- 问题:InnoDB提供了缓冲池 虽然在性能方面带来了质的飞跃 但是当MySQL系统宕机、断电的时候可能会丢数据
如果这个时候事务提交了 数据还未持久化到磁盘 那么数据也就会丢失
因为我们的数据已经提交了 但此时是在缓冲池里头 还没来得及在磁盘持久化
所以我们急需一种机制需要存一下已提交事务的数据 为恢复数据使用 - 解决:redo log 重做日志
虽然redo log和刷脏都是IO操作 但刷脏是随机IO 而redo log是追加操作属于顺序IO
另外刷脏是以数据页为单位 一点小改动都需要整页写入 而redo log只包含真正需要写入的部分
事务状态:
- Active:事务的初始状态,表示事务正在执行;
- Partially Commited:在最后一条语句执行之后;
- Failed:发现事务无法正常执行之后;
- Aborted:事务被回滚并且数据库恢复到了事务进行之前的状态之后;
- Commited:成功执行整个事务;
隔离级别:
- 读未提交:
- 概念:事务A可以读取到事务B修改过但未提交的数据 (
没提交也能获取到数据
) - 问题:可能发生脏读、不可重复读和幻读问题
一般很少使用此隔离级别
- 概念:事务A可以读取到事务B修改过但未提交的数据 (
- 读已提交
- 概念:事务A只能在事务B提交后才能读取到事务B修改的数据 (
提交后才能获取到
) - 问题:
解决了脏读的问题
但可能发生不可重复读和幻读问题一般很少使用此隔离级别
- 加锁:数据的读取都是不加锁的 但是数据的写入、修改和删除是需要加锁的
- 概念:事务A只能在事务B提交后才能读取到事务B修改的数据 (
- 可重复读 --MySQL的默认隔离级别
- 概念:事务A在提交后 事务B也不能读取到事务A提交的修改 (
开启后就不能获取到
) - 问题:
解决了脏读和不可重复读的问题
可能发生幻读问题实际开发级别
- 加锁:数据的读写都是加锁的 不过是行锁 所以可以阻塞其他事务对于本事务内读数据的修改和删除 实现可重复读
但是行锁只能控制修改和删除 而不能作用于新增 所以还是会出现幻读的现象 (应该是行锁 也可能是乐观锁 待确认)
- 概念:事务A在提交后 事务B也不能读取到事务A提交的修改 (
- 可串行化
- 概念:事务A提交前 事务B读写操作都会被阻塞 (
事务只会一个一个的执行
只有都是读操作不会被阻塞) - 问题:
各种问题(脏读、不可重复读、幻读)都不会发生
通过加锁实现(读锁和写锁)出于性能考虑很少使用
- 概念:事务A提交前 事务B读写操作都会被阻塞 (
事务并发:
并发问题:
脏读:
事务A读到了事务B未提交的数据 (如果最终事务B没有提交 那么这个数据就是不存在的)
读取到不存在的数据 就是脏读不可重复读:
在事务A执行过程中 事务B修改了事务A中读取的数据 导致事务A在前后读取的数据不一致
(如果是两个同样条件的修改语句 出现不可重复读现象 会导致两条数据修改的结果不一致)
重复读取数据不一致 就是不可重复读幻读:
在事务A执行过程中 事务B添加了数据 添加的数据在事务A的读取范围内 导致事务A在前后读取到的数据不一致
(同 不可重复读现象一样 会导致修改的数据结果不一致)
读取到新增的数据 就是幻读
注意点:幻读和不可重复读的区别?
- 幻读和不可重复读看起来很像 都是在事务执行中 被别的事务修改了读取的数据 导致前后数据不一致
两者的区别在于- 不可重复读侧重于数据的修改
- 幻读则是侧重于数据的新增
- 原因:
- 不可重复读没有对读操作的数据加锁 所以别的事务也可以修改 本事务读取的数据 最终造成不可重复读现象
- 幻读对读数据加了锁 不过幻读的锁是行锁 所以只是不能修改删除而已 还是能添加 所以会出现幻读现象
(应该是行锁 也可能是乐观锁 待确认)
并发控制:
锁机制:
参看 ↓ 数据库锁
MVCC:
- MVCC可以使用
乐观(optimistic)锁
和悲观(pessimistic)锁
来实现 - 各数据库中都有对MVCC的实现 且实现方式都不相同
- 应对高并发事务 MVCC比
单纯的加锁
更高效 - InnoDB支持MVCC InnoDB存储引擎在数据库每行数据的后面添加了
三个字段
- MVCC只在
读已提交 READ COMMITTED
和可重复读 REPEATABLE READ
两个隔离级别下工作
隐藏字段: