欢迎访问原文地址来阅读最新版本
转载请注明出处:https://kang.fun/innodb-transaction
读未提交,顾名思义,就是一个事务可以读取另一个未提交事务的数据。
但是不能避免脏读的问题,即A事务中读取到了B事务中未提交的数据,但是B事务进行了回滚,A事务中这时候就持有了并不存在的脏数据。
读提交,或者叫读已提交,就是一个事务要等另一个事务提交后才能读取数据。
这就是“不可重复读”问题。也就是说,一次事务中两次读取相同的数据却发现数据不一致了。
可重复读,就是在开始读取数据(事务开启)时,不再允许修改操作,但允许写操作。
但是仍旧无法避免幻读问题。
什么是幻读?
假设数据库中订单表数据如下:
id | 订单号 | 金额(元) |
---|---|---|
3 | NO613895797361304528 | 10.0 |
5 | NO614946192397637269 | 20.0 |
6 | NO615478237645483920 | 17.0 |
现在开启事务A,把id大于等于3且小于等于5的订单数据的支付金额进行求和,即30元。
但事务A尚未提交时,数据库中插入了一条id为4的数据:
id | 订单号 | 金额(元) |
---|---|---|
4 | NO614164929374873483 | 5.0 |
那么当事务A提交后,发现id在[3, 5]的范围内,订单金额总额并非30元而是35元,我们称这个现象为幻读。
Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。
为保证事务(transaction)是正确可靠的,数据库引擎必须具备的四个特性:原子性(atomicity,或称不可分割性)、一致性(consistency)、隔离性(isolation,又称独立性)、持久性(durability)。
InnoDB分别使用如下方式来保证了这四个特性:
binlog是Mysql Server层级的逻辑变更日志,它记录了数据库的所有变更,以二进制存储在磁盘上。
主要作用在两个方面:
增量备份
主从复制
redo log是InnoDB存储引擎层的日志,记录的是新数据的备份,在事务提交前将redo log持久化。
数据写入时,实际上是写入了内存中的Buffer Pool,Buffer Pool中的数据根据策略定期写入磁盘。那么就带来一个问题,如果在写入磁盘前系统崩溃了,数据一致性难以保证,所以在事务完成前会写入redo log,系统崩溃重启后,通过redo log来恢复未持久化到页中的数据。
这样还能使写入变为一个异步操作,那么在写入磁盘前,数据库引擎还能对多次同一个键的修改进行合并,降低磁盘写入内容和次数。
事务中的每一次修改,innodb 都会先记录对应的 undo log 记录。与 redo log 用于数据的灾后重新提交不同,undo log 主要用于数据修改的回滚。
与 redo log 记录的是物理页的修改不同,undo log 记录的是逻辑日志。
在事务中每次更新记录后,都会将旧值放到一条undo日志中,就算是该记录的一个旧版本,随着更新次数的增多,所有的版本都会被数据的roll_pointer属性连接成一个链表,我们把这个链表称之为版本链,版本链的头节点就是当前记录最新的值。
当事务回滚时,通过undo log对数据反向更新。
多版本的意思就是一条数据在数据库中同时存在多个版本,在某个事务对其进行操作的时候,需要查看这一条记录的隐藏列事务版本id,比对事务id并根据事务隔离级别去判断读取哪个版本的数据。
隐藏属性 | 是否必须 | 描述 |
---|---|---|
row_id | 否 | 行ID,唯一标识一条记录(如果定义主键,则没有) |
transaction_id | 是 | 事务ID |
roll_pointer | 是 | DB_ROLL_PTR是一个回滚指针,用于配合undo日志,指向上一个旧版本,形成版本链 |
是否允许一个uncommitted的事务将修改更新到磁盘
那么此时磁盘上就可能包含uncommitted的数据,因此系统需要记录undo log,以防事务abort时进行回滚(roll-back)
就表示磁盘上不会存在uncommitted数据,因此无需回滚操作,也就无需记录undo log
事务在committed之后是否将所有更新立刻持久化到磁盘
在committed之后必须将所有更新立刻持久化到磁盘
在committed之后可以不立即持久化到磁盘,而是缓存更新批量持久化到磁盘
no-force的优点是提升顺序写,缺点是有可能在crash后导致事务数据丢失,所以需要记录redo log,系统重启后进行前滚(roll-forward)操作
现在DBMS常用的是steal/no-force策略,因此一般都需要记录redo log和undo log。这样可以获得较快的运行时性能,代价就是在数据库恢复(recovery)的时候需要做很多的事情,增大了系统重启的时间。