数据库ACID特性
- 原子性(Atomicity)
- 事务被视为不可分割的最小单元,而原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚。(回滚通过回滚日志实现,日志中记录着事物所执行的修改操作,在回滚时,反向进行修改操作即可)
- 一致性(Consistency)
- 数据库在事务执行的前后都保持着一致性的状态,所有事务对一个数据的读取结果相同。
- 隔离性(Isolation)
- 一个事务所做的修改在最终提交前,对其他的事务是不可见的。
- 持久性(Durability)
- 一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
四大隔离级别及其实现原理
- 三种导致产生不一致性的原因
- 脏读:A,B两事务,A事务会读取到B事务未提交的数据,然后B因为某些原因回滚数据,所以A就读取了B没有提交的数据,也称脏数据。
- 不可重复读:在A事务中对同一数据两次查询不一致,可能原因是在A事务提交之前B事务对该数据进行了操作
- 幻读:类似于不可重复读,都是在一个事务周期内读的数据不一致,区别在于幻读是侧重于插入操作带来的影响,而不可重复读是编辑或者删除带来的影响
- 丢失修改:一个事物的更新操作会被另一个事务的更新操作所覆盖。(需要实现串行化的隔离级别)。
- 四种隔离级别
- 四种隔离级别的实现(当前读,快照读,MVCC,悲观锁,乐观锁)
- 当前读
- 当前读,所读取的是最新记录的数据,为保证其他并发事务不能修改当前记录,对读取的记录会枷锁。一般在执行select lock in share mode(共享锁), select for update ; update, insert,delete(排他锁)操作时使用(悲观锁)。也可采用乐观锁(控制版本号)进行手动实现。
- 不可重复读只需采用行级锁就可防止该记录数据被更改或删除,而幻读则必须添加表级锁,来防止这个表中新增一条数据。
- 哪些情况会加锁
- 有索引并且使用了该索引当条件的时候就是行锁,没有索引的时候就是表锁。insert一般都是行锁, update ,delete和select差不多,能确定且走索引的是行锁,否则表锁。如果加了S锁,不影响查询,但会影响增加和修改,加了X锁,那么其他的事务涉及到这个数据就会陷入等待。
- 也可手动执行命令来添加表锁,X锁和S锁。
- 快照读
- 不加锁的非阻塞读(即不加锁的select操作)是快照读。这基于MVCC进行实现。
- 当前读与快照读都可实现四种隔离级别,但是在快照读中,串行化中会使用到间隙锁,进而退化成为当前读。
- MVCC用于实现读提交读和可重复读这两种隔离级别,而再加上间隙锁则可实现串行化的隔离级别。
- MVCC可以解决读写冲突上的四种隔离级别,而悲观锁和乐观锁进行写写冲突上的四种隔离级别,他们经常互相搭配使用。
- 当前读
MVCC
- 多版本并发控制(MVCC)是MySQL的InnoDB存储引擎实现隔离级别的一种具体的方式,用来解决读写冲突。他的实现依赖于3个隐式字段,undo日志和Read View。
- 隐式字段
- DB_TRX_ID
- 6byte,最近修改(修改/插入)事务ID:记录创建这条记录/最后一次修改该记录的事务ID
- DB_ROLL_PTR
- 7byte,回滚指针,指向这条记录的上一个版本(存储于rollback segment里)
- DB_ROW_ID
- 6byte,隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引
- DB_TRX_ID
- undo log
- insert undo log
- 代表事务在insert新记录时产生的undo log, 只在事务回滚时需要,并且在事务提交后可以被立即丢弃。
- update undo log
- 事务在进行update或delete时产生的undo log; 不仅在事务回滚时需要,在快照读时也需要;所以不能随便删除,只有在快速读或事务回滚不涉及该日志时,对应的日志才会被purge线程统一清除。
- 每次不同事务对同一份内容的修改(加上排他锁),都会复制一份到undo log中,按照事务发生的顺序,对应的事务id不同(由一递增),最新修改的记录的指针会指向上一份记录。
- insert undo log
- Read View
- Read View就是事务进行快照读操作的时候生产的读视图。其中记录了活跃的事务id(即未提交的事务)。Read View遵从当前事务id如果比最小的活跃事务id还小,则可见;如果比已出现的事务id还大,不可见;如果在活跃事务中,则代表未提交,不可见,否则可见。
- RC,RR级别下的InnoDB快照读
- 在RC级别下,事务中每一次的快照读,都会更新Read View,而RR级别下只会有第一个Read View,以后的快照读不再生成,而是基于第一个Read View。
- 串行化级别下的快照读,则是在间隙锁的前提下进行的,使之不能插入数据。