事务的基本要素:ACID事务:
- 原子性:事务是一个不可分割的单位,事务中的操作要么都发生,要么都不发生
- 一致性:事务前后数据的完整性必须保持一致,方便回滚
- 隔离性:事务与事务之间的操作不能相互干扰
- 持久性:一个事务一旦被提交,它对数据库中数据的改变就是永久的,即使接下来数据库发生故障,也不应该对其有任何影响
- 原子性是基础,隔离性是手段,一致性是约束条件,持久性是目的
事务的状态可以分为:
- 活动的,事务对应的数据库操作正在执行过程中
- 部分提交的:当事务中的最后一个操作执行完成,但由于操作都在内存中执行,所造成的影响并 没有刷新到磁盘时,我们就说该事务处在 部分提交的 状态。
- 失败的,可能遇到了某些错误而无法继续执行
- 中止的:如果事务执行了一部分而变为失败的状态,那么就需要把已经修改的事务中的操作还原到事务执行前的状态。也就是要撤销失败事务对当前数据库造成的影响。这个撤销的过程称之为回滚 。当回滚操作执行完毕时,也就是数据库恢复到了执行事务之前的状态。
- 提交的,当一个处在 部分提交的 状态的事务将修改过的数据都 同步到磁盘 上之后,我们就可以说该事务处 在了 提交的 状态
mysql中的事务:
- mysql默认采用自动提交,也就是隐式事务,如果不是显示的指定一个事务的开始,每次操作都会被当成一个事务执行提交操作
- 数据定义语言DDL、修改mysql数据库中的表、关于MySQL复制的一些语句都是隐式事务,都无法通过改变autocommit 系统变量的值,来变为显示事务。
- mysql可以识别4个隔离级别,innodb也支持所有的隔离级别
- 事务是由存储引擎实现的,所以在同一个事务中,使用多种存储引擎是不可靠的
- 例如在事务中混用了事务型表和非事务型表,如果回滚非事务型表也无法撤销,会导致数据的不一致,这种情况很难修复,事务的最终结果也无法确定
事务的隔离级别
-
读未提交
- 如果一个事务开始了写操作,虽然不允许其他事务同时进行写操作,但允许其他事务读取数据,有可能出现脏读,也就是读取到了其他事务未提交的数据。
-
读已提交
- 对于一行数据,如果有一个写事务将会禁止其他事务访问该行数据,避免了脏读,但是可能出现不可重复读,
- 也就是事务A事先读取了数据,事务B紧接着更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变。
-
可重复读
- 可重复读取是指在一个事务内,多次读同一个数据,在这个事务还没结束时,其他事务不能访问该数据,这样避免了不可重复读和脏读,但是有时可能会出现幻读。
- 也就是一个事务在前后两次查询同一个范围的时候,因为只是禁止事务对查询到的数据进行操作,并不能禁止其他事务插入新的数据,所以后一次查询会看到了前一次查询没有看到的行
- mysql还额外增加了间隙锁和mvcc,避免了幻读问题
-
串行化
- 要求事务序列化执行,事务只能一个接着一个地执行
- 序列化是最高的事务隔离级别,同时代价也是最高的,性能很低,一般很少使用,在该级别下,事务顺序执行,可以避免脏读、不可重复读以及幻读。
-
MySQL默认隔离级别为:可重复读 (Repeated read),Oracle默认隔离级别为:读已提交(READ COMMITTED)
事务日志
- 事务日志可以提高事务效率,使用事务日志,存储引擎在修改表的数据的时候,只需要修改内存拷贝,再把修改行为持久化到硬盘上的事务日志,
- 内存中的数据在后台可以慢慢的刷回磁盘,不用每次都将修改的数据本身持久化到磁盘,也就是修改数据其实需要写两次磁盘
- 事务日志采用的是追加的方式,是顺序I/O
事务有4种特性:原子性、一致性、隔离性和持久性。那么事务的四种特性到底是基于什么机制实现呢?
- 事务的隔离性由锁机制实现。
- 而事务的原子性、一致性和持久性由事务的 redo 日志和undo 日志来保证。
- REDO LOG 称为 重做日志 ,提供再写入操作,恢复提交事务修改的页操作,用来保证事务的持 久性。
- UNDO LOG 称为 回滚日志 ,回滚行记录到某个特定版本,用来保证事务的原子性、一致性。
redo日志
-
为什么需要REDO日志
- 缓冲池可以帮助我们消除CPU和磁盘之间的鸿沟,checkpoint机制可以保证数据的最终落盘,然而由于checkpoint 并不是每次变更的时候就触发 的,而是master线程隔一段时间去处理的。所以最坏的情况就是事务提交后,刚写完缓冲池,数据库宕机了,那么这段数据就是丢失的,无法恢复。
- 而且事务包含持久性的特性,就是说对于一个已经提交的事务,在事务提交后即使系统发生了崩溃,这个事务对数据库中所做的更改也不能丢失
- 目的是想让已经提交了的事务对数据库中数据所做的修改永久生效,即使后来系统崩溃,在重启后也能把这种修改恢复出来。所以其实没有必要在每次事务提交时就把该事务在内存中修改过的全部页面刷新到磁盘,只需要把修改了哪些东西 记录一下就好。
-
优点是降低了刷盘频率 ,而且redo日志占用的空间非常小
-
在事务执行过程中,redo log不断地按顺序写入磁盘
-
Redo log可以简单分为两个部分:
- 重做日志的缓冲 (redo log buffer) ,保存在内存中
- 重做日志文件 (redo log file) ,保存在硬盘中
-
redo的整体流程为:
- 先将原始数据从磁盘中读入内存中来,修改数据的内存拷贝
- 生成一条重做日志并写入redo log buffer,记录数据被修改后的值
- 当事务commit时,将redo log buffer中的内容,采用追加写的方式刷新到 redo log file
- 另外会定期将内存中修改的数据刷新到磁盘中
-
redo log的刷盘策略
- redo log buffer刷盘到redo log file的过程并不是真正的刷到磁盘中去,只是刷入到 文件系统缓存中,这是现代操作系统为了提高文件写入效率做的一个优化,真正的写入会交给系统自己来决定,如果系统宕机,那么数据也丢失了
- 针对这种情况,InnoDB给出 innodb_flush_log_at_trx_commit 参数,该参数控制 commit提交事务 时,如何将 redo log buffer 中的日志刷新到 redo log file 中
- 设置为0 :表示每次事务提交时不进行刷盘操作。系统默认master thread每隔1s进行一次重做日志的同步,有可能会丢失1s内的数据
- 设置为1 :表示每次事务提交时都将进行同步,刷盘操作( 默认值 )
- 设置为2 :表示每次事务提交时都只把 redo log buffer 内容写入 page cache,不进行同步。由os自己决定什么时候同步到磁盘文件。
redo log写满的情况:
-
InnoDB的redo log是固定大小的,写到末尾时候会回到开头循环写,文件可以复写,
-
write pos是当前记录的位置,一边写一边后移,写到文件末尾后就回到文件开头。checkpoint是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。
-
write pos和checkpoint之间的是空着的部分,可以用来记录新的操作。如果write pos追上checkpoint,表示满了,这时候不能再执行新的更新,得停下来先擦掉一些记录,把checkpoint推进一下。
Undo日志
-
undo log是事务原子性的保证。在事务中更新数据的前置操作其实是要先写入一个undo log 。
-
日志的作用是,回滚数据 和 MVCC
-
回滚段与undo页
- InnoDB对undo log的管理采用段的方式,也就是 回滚段(rollback segment) 。每个回滚段记录了 1024 个 undo log segment ,而在每个undo log segment段中进行 undo页 的申请。
- 在 InnoDB1.1版本之前 ,只有一个rollback segment,因此支持同时在线的事务限制为 1024 。虽然对绝大多数的应用来说都已经够用。
- 从1.1版本开始InnoDB支持最大 128个rollback segment ,故其支持同时在线的事务限制提高到 了 128*1024
-
回滚段与事务
- 每个事务只会使用一个回滚段,一个回滚段在同一时刻可能会服务于多个事务。
- 当一个事务开始的时候,会制定一个回滚段,在事务进行的过程中,当数据被修改时,原始的数据会被复制到回滚段。
- 在回滚段中,事务会不断填充盘区,直到事务结束或所有的空间被用完。如果当前的盘区不够用,事务会在段中请求扩展下一个盘区,如果所有已分配的盘区都被用完,事务会覆盖最初的盘 区或者在回滚段允许的情况下扩展新的盘区来使用。
- 回滚段存在于undo表空间中,在数据库中可以存在多个undo表空间,但同一时刻只能使用一个 undo表空间。
- 当事务提交时,InnoDB存储引擎会做以下两件事情: 将undo log放入列表中,以供之后的purge(清除)操作 判断undo log所在的页是否可以重用,若可以分配给下个事务使用
-
回滚段中的数据分类
- 未提交的回滚数据(uncommitted undo information)
- 已经提交但未过期的回滚数据(committed undo information)
- 事务已经提交并过期的数据(expired undo information)
-
在InnoDB存储引擎中,undo log分为:
- insert undo log
- update undo log
-
undo log的删除
- 针对于insert undo log
- 因为insert操作的记录,只对事务本身可见,对其他事务不可见。故该undo log可以在事务提交后直接删 除,不需要进行purge操作。
- 针对于update undo log
- 该undo log可能需要提供MVCC机制,因此不能在事务提交时就进行删除。提交时放入undo log链表,等 待purge线程进行最后的删除。
- 针对于insert undo log
-
undo log是逻辑日志,对事务回滚时,只是将数据库逻辑地恢复到原来的样子。
-
redo log是物理日志,记录的是数据页的物理变化,undo log不是redo log的逆过程