什么是事务?
事务是逻辑上的一组操作,要么都执行,要么都不执行。
事务的特性?
ACID:
原子性: 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用。
一致性: 执行事务前后,数据保持一致。例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的。
隔离性: 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立。
持久性: 一个事务被提交之后,它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
并发事务带来的问题?
脏读(Dirty read): 事务A在修改事务,且还没有提交,事务B读取到了这个事务。
幻读(Phantom read): 事务A读取了一个范围数据,事务B在这个范围内插入(删除)了数据,事务A再次读读这个范围数据,发现多了(少了)一些数据。
不可重复读(Unrepeatableread): 事务A读取了一条数据,事务B对这条数据进行了修改,事务A再次读取这个数据,发现读取到的数据不一样了。
丢失修改(Lost to modify): 事务A对一条数据进行修改,同时事务B对这条数据也进行修改,导致事务A对这条数据的修改丢失。例如a = 20,并发进行a = a - 1,最终结果是19,说明事务A的修改丢失了。
不可重复度和幻读区别?
不可重复读的重点是修改,幻读的重点在于新增或者删除。
事务隔离级别?
READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
MySQL InnoDB默认支持的隔离级别?
MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)。
为什么大部分数据库的默认隔离级别都是读已提交?
因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是 READ-COMMITTED(读取提交内容) ,但是 InnoDB 存储引擎默认使用 REPEATABLE-READ(可重读) 并不会有任何性能损失。
一致性非锁定读和锁定读?
一致性非锁定读(Consistent Nonlocking Reads):
就是不加锁读,通常实现是使用版本号或者时间戳,在更新数据时,对版本号+1或者更新当前时间戳。查询时将当前可见版本号与对应记录的版本号进行对比,如果记录版本小于版本号小于可见版本号,表示该记录可见。
InnoDB 存储引擎的多版本控制 (multi versioning)是对非锁定读的实现,如果读取的行正在执行 DELETE 或 UPDATE 操作,这时读取操作不会去等待行上锁的释放。相反地,InnoDB 存储引擎会去读取行的一个快照数据,对于这种读取历史数据的方式,我们叫它快照读 (snapshot read)。
在 Repeatable Read 和 Read Committed 两个隔离级别下,如果是执行普通的 select 语句会使用 一致性非锁定读(MVCC:多版本并发控制)。并且在 Repeatable Read 下 MVCC 实现了可重复读和防止部分幻读(一致性非锁定读的时候,第一次读取生成快照,以后每次都读快照,阻止了这部分幻读)。
锁定读(Locking Reads):
读取的是数据的最新版本,这种读也被称为 当前读(current read)。
select ... lock in share mode:对记录加 S 锁,其它事务也可以加S锁,如果加 x 锁则会被阻塞。
select ... for update、insert、update、delete:对记录加 X 锁,且其它事务不能加任何锁。
不管是S锁还是X锁,都不影响一致性非锁定读,所以MVCC可以实现Repeatable Read在一致性非锁定读的情况下的幻读。但是如果是当前读(current read)又叫锁定读,则会通过对读取的记录使用 Next-key Lock ,来防止其它事务在间隙间插入数据,来防止幻读。
InnoDB 对 MVCC 的实现?
MVCC 的实现依赖于隐藏字段、Read View、undo log。首先通过隐藏字段和Read View来判断数据的可见性。如不可见,则通过隐藏字段来找到undo log 中的历史版本中,最后提交事务的数据。同一个事务中,用户只能看到该事务创建 Read View 之前已经提交的修改和该事务本身做的修改。
undo log 主要有两个作用?
- 当事务回滚时用于将数据恢复到修改前的样子。
- 另一个作用是 MVCC ,当读取记录时,若该记录被其他事务占用或当前版本对该事务不可见,则可以通过 undo log 读取之前的版本数据,以此实现非锁定读。
在 InnoDB 存储引擎中 undo log 分为两种?
insert undo log :指在 insert 操作中产生的 undo log。因为 insert 操作的记录只对事务本身可见,对其他事务不可见,故该 undo log 可以在事务提交后直接删除。不需要进行 purge 操作。
update undo log :update 或 delete 操作中产生的 undo log。该 undo log可能需要提供 MVCC 机制,因此不能在事务提交时就进行删除。提交时放入 undo log 链表,等待 purge线程进行最后的删除。
RC 和 RR 隔离级别下 MVCC 的差异?
在事务隔离级别 RC 和 RR (InnoDB 存储引擎的默认事务隔离级别)下,InnoDB 存储引擎使用 MVCC(非锁定一致性读),但它们生成 Read View 的时机却不同:
- 在 RC 隔离级别下的 每次select 查询前都生成一个Read View (m_ids 列表)。
- 在 RR 隔离级别下只在事务开始后 第一次select 数据前生成一个Read View(m_ids 列表)。
MySQL支持的锁介绍?
行锁(Record Lock):锁直接加在索引记录上面,锁住的是key。
间隙锁(Gap Lock):锁定索引记录间隙,确保索引记录的间隙不变。间隙锁是针对事务隔离级别为可重复读或以上级别而已的。
Next-Key Lock :行锁和间隙锁组合起来就叫Next-Key Lock。
Next-Key Lock 工作原理?
- 默认情况下,InnoDB工作在可重复读隔离级别下,并且会以Next-Key Lock的方式对数据行进行加锁,这样可以有效防止幻读的发生。Next-Key Lock是行锁和间隙锁的组合,当InnoDB扫描索引记录的时候,会首先对索引记录加上行锁(Record Lock),再对索引记录两边的间隙加上间隙锁(Gap Lock)。加上间隙锁之后,其他事务就不能在这个间隙修改或者插入记录。
- Gap Lock在InnoDB的唯一作用就是防止其他事务的插入删除操作,以此防止幻读的发生。
MySQL在InnoDB存储引擎在 RR 级别下解决幻读的方式?
- 执行普通 select,此时会以 MVCC 快照读的方式读取数据。
- 执行 select...for update/lock in share mode、insert、update、delete 等当前读时,使用Next-key-Lock 防止幻读。