ACID特性
- 原子性(Atomicity):事务是一个原子操作单元,其对数据的修改,要么全部执行,要么全部不执行
- 一致性(Consistency):可以理解未数据的完整性,用过原子性、隔离性、持久性来保证
- 隔离性(Isolation):一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对其他的并发事务是隔离的,隔离级别从低到高如下:
- 读未提交
- 读提交
- 可重复度
- 串行化
- 隔离性(Isolation):一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对其他的并发事务是隔离的,隔离级别从低到高如下:
- 持久性(Durability):事务一旦提交,数据库中数据的改变就应该是永久性的,后续的操作或故障不应该对其有任何影响
事务隔离级别
- 1. 脏读:事务读到了其他事务未提交的数据;如事务A读取了事务B更新的数据,然后事务B执行了回滚操作,此时A读到的数据为脏数据
- 2. 不可重复读:事务读到了其他事务已提交的数据;如事务A多次读取同一数据,期间事务B在A读取过程中执行了数据修改并提交的操作导致事务A多次读取的数据不一致
- 3. 幻读:当用户读取某一个范围的数据行时,另一个事务又在该范围插入了新行,当用户在读取该范围的数据行时会读取到新的插入行 | 隔离级别 | 脏读 | 不可重复读 | 幻读 | | —- | —- | —- | —- | | 读未提交 | 是 | 是 | 是 | | 不可重复度 | 否 | 是 | 是 | | 可重复读 | 否 | 否 | 是 | | 串行化 | 否 | 否 | 否 |
可重复读实现原理
InnoDB的行数据有多个版本,每个数据版本有自己的row trx_id,每个事务或者语句有自己的一致性视图。普通查询语句是一致性读,一致性读会根据row trx_id和一致性视图确定数据版本的可见性。
- 对于可重复读,查询只承认在事务启动前就已经提交完成的数据;
- 对于读提交,查询只承认在语句启动前就已经提交完成的数据;
对当前读,总是读取已经提交完成的最新版本。
可重复度的核心为一致性读(基于事务开始时创建的一致性视图);而事务更新数据的时候,只能用当前读,如果当前的记录的行锁被其他事务占用的化,就需要进入锁等待。
锁机制
锁分类
按锁的粒度可分为:
- 表级锁
- 页级锁
- 行级锁
按操作类型可分为:
- 读锁(S锁):共享锁,针对同一份数据,多个读操作可以同时进行而不会互相影响
- 写锁(X锁):排他锁,当前写操作没有完成前,它会阻断其他写锁和读锁
按操作性能可分为:
- 乐观锁:一般的实现方式是对记录数据版本进行比对,在数据更新提交的时候才会进行冲突检测,如果发现冲突了,则提示错误信息
悲观锁:在对一条数据修改的时候,为了避免同时被其他人修改,在修改数据之前先锁定,再修改的控制方式。共享锁和排他锁都是悲观锁的不同实现,都属于悲观锁范畴
InnoDB行锁原理
InnoDB行锁是通过对索引数据页上的记录加锁实现的,主要实现算法如下:
RecordLock:记录锁,锁定单个行记录的锁。(RC、RR隔离级别都支持)
- GapLock:间隙锁(范围锁),锁定索引记录间隙,确保索引记录的间隙不变(RR级别支持)
- Next-keyLock锁:记录锁和间隙锁组合,同时锁住数据,并且锁住数据前后范围,每个Next-Key Lock是前开后闭区间。(RR级别支持)
在RR隔离级别,InnoDB对于记录加锁行为都是先采用Next-Key Lock,但是当SQL操作含有唯一索引时,InnoDB会对Next-Key Lock进行优化,降级为RecordLock,仅锁住索引本身而非范围。
- select…from语句:InnoDB引擎采用MVCC机制实现非阻塞读,即对于普通的select语句,InnoDB不加锁
- select…from lock in share mode语句:追加了共享锁,InnoDB会使用Next-Key Lock锁进行处理,如果扫描发现唯一索引,锁降级为RecordLock
- select…from for update语句:追加了排他锁,InnoDB会使用Next-Key Lock锁进行处理,如果扫描发现唯一索引,锁降级为RecordLock
- update/delete…where语句:InnoDB会使用Next-Key Lock锁进行处理,如果扫描发现唯一索引,锁降级为RecordLock
- insert语句:InnoDB会在将要插入的哪一行设置一个排他的RecordLock锁
两个“原则”、两个“优化”和一个“bug”
原则 1:加锁的基本单位是 next-key lock。
原则 2:查找过程中访问到的对象才会加锁。
优化 1:索引上的等值查询,给唯一索引加锁的时候,next-key lock 退化为行锁。
优化 2:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock 退化为间隙锁。
一个 bug:唯一索引上的范围查询会访问到不满足条件的第一个值为止。