数据库事务ACID
四大特性介绍
1.什么是事务?
数据库的事务: 事务,简单理解就是一件完整的事情,有始有终!那么数据库事务,其实也就是对数据库的一系列操作组成的事件集合,这些事件要么同时成功,要么同时失败。比如:银行转账,A用户转给B用户,那么A用户金额减少,相应的B用户增加相应的金额,那么如果在A转出金额后,出现故障或者意外,那么B没收到钱,A的金额还减少了,这样就会造成部分金额丢失,为防止这种情况,引入了事务,事务的ACID四大特性:原子性、一致性、隔离性、持久性,便保障了这类事情的发生,保证数据的一致和持久。
下面以事务 Transaction(InnoDB引擎)为例!
2.原子性要么同时成功同时失败
原子性,就是操作的最小的粒度,也是多次相同操作所产生的影响或者结果一样,做到幂等性原则。
3.一致性事务操作前后,数据总量不变
一致性:是保证数据的前后访问一致。
一致性的实现依赖undo log!
undo log
【机制】
-
磁盘->段->区->页
- undo log存放在数据库内部的一个特殊段(segment)中,这个段称为undo段(undosegment);
- undo log是逻辑日志,因此只是将数据库逻辑恢复到原来的样子;
- undo log的另一个作用是MVCC,即在InnoDB存储引擎中MVCC的实现是通过undo来完成的;
- undo log的产生会伴随着redo log的产生,这是因为undo log也需要持久性的保护。
【与redo log区别】
- redo log通常是物理日志,记录的是页的物理修改操作
- undo log是逻辑日志,根据每行记录进行记录
3. 隔离性(Isolation) 多个事务之间。相互独立。
lock
【粒度】
-
表锁、行锁
-
【类型】
- S共享锁、X排他锁、IS意向共享、IX意向排他(排他锁x与一切不兼容,IX意向排他锁与共享、排他不兼容)
-
【机制】
- 无锁:MVCC:对于正在更新的数据,InnoDB会去该行的一个快照数据(undo log)(历史版本数据,提交事务会存历史版本在log中)
- 加锁:X锁:SELECT … FOR UPDATE
S锁:SELECT …LOCK IN SHARE MODE
-
【算法】
- Record Lock:单个行记录上的锁,加在聚簇索引(mysql主键索引就是聚簇索引)
- Gap Lock:间隙锁,锁定一个范围,但不包含记录本身
- Next-Key Lock:Record+Gap,锁定一个范围,并锁本身
-
【问题】
- 读取
- 脏读:某一个事务,读取了另一个事务未提交的数据
- 不可重复度(虚读):某一事务,对同一数据前后读取结果不一致。
- 幻读:某一事务,对同一个表前后查询到的行数不一致。
-
- 更新(任何隔离级别都不会发生这类问题,因为对于DML操作需要先加IX锁,它会阻止事务2的行为)
- 第一类丢失更新:某一事务的回滚,导致另一事务已更新数据丢失
- 第二类丢失更新:某一事务的提交,导致另一事务已更新数据丢失
【死锁】
- 场景:
- - 事务1:update ... where id=1;update ... where id=2;
- 事务2:update ... where id=2;update ... where id=1;
- 解决:
- 超时回滚:`被动`innodb_lock_wait_timeout,当一个等待时间超过设置的某一阈值时,其中一个事务进行回滚,也就是释放锁(innodb是谁的范围大谁先释放)
- 死锁检测:`主动`wait-for graph,采用等待图的方式来进行检测,这是更主动的死锁检测
【升级】
- InnoDB存储引擎不存在锁升级问题( `因为其不是根据每个记录来产生行锁的,相反,其根据每个事务访问的每个页对锁进行管理的,采用的是位图的方式。因此不管一个事务锁住页中一个记录还是多个记录,其开销通常都是一致的`。 )
-
隔离级别
-
- Read Uncommitted读未提交
-
- 未解决脏读、不可重复读、幻读问题
- Read Committed读已提交
-
- 采用Record Lock算法,解决脏读问题
- 采用MVCC,总是读取被锁定行的最新一份快照数据。
- Repeatable Read可重复读 (MySQL默认)
-
- 默认的隔离级别
- 采用Next-Key Lock算法,解决脏读、不可重复读、幻读问题
- 采用MVCC,总是读取事务开始时的行历史版本数据
- Serializable串行化
-
- 解决了脏读、不可重复读、幻读问题(select … LOCK IN SHARE MODE)
4. 持久性 (Durability) 当事务提交或回滚后,数据库会持久化的保存数据。
-
redo log
-
【机制】
- 当事务提交时,必须先将该事务的所有日志写入到Redo log进行持久化,待事务的COMMIT操作完成才算完成;
- redo log是顺序写的,在数据库运行时不需要对该文件进行读取操作;
- 每次写入redo log文件后,InnoDB引擎都需要调用一次fsnc(刷盘)操作;
- redo log通常是物理日志,记录的是页的物理修改操作。
【与bin log区别】
- redo log是在存储引擎层产生,bin log是在数据库的上层产生,并且bin log不仅仅针对InnoDB存储引擎,MySQL中任何存储引擎对于数据库的更改都会产生二进制日志;
- bin log 是一种逻辑日志,其记录的是对应的SQL语句,而InnoDB引擎层面的redo log是物理格式日志,其记录的是对于每个页的修改;
- bin log只在事务提交完成后进行一次写入,而redo log在事务进行中不断被写入,这表现为日志并不是随事务提交的顺序进行写入的。