资源来自:《MySQL技术内幕-InnoDB存储引擎》
首先来了解一下数据库引入事务的原因
事务(transaction)是数据库区别于文件系统的重要特性之一。在文件系统中,如果正在写文件,此时操作系统崩溃了的话,该文件系统很可能被破坏。有些机制可以将文件恢复到某一个时间点的文件状态。但是如果需要将两个文件同步,文件系统就无能为力了。例如:在需要更新两个文件时,更新完第一个文件后,在更新第二个文件的时候,系统重启了,就会有两个不同步的文件(同一个文件的两个版本)
数据库系统引入事务的主要目的是:事务会把数据库从一种一致状态转换为另一种一致状态。同事可以保证数据库在提交事务时,可以保证要么所有修改都已经保存了,要么所有修改都不修改。
认识事务
事务可以由一条SQL组成,也可以由多条SQL组成,事务是访问并更新数据库中各种数据项的一个程序执行单元。
InnoDB中的事务完全符合ACID特性
原子性:atomicity
一个事务中的操作要么都做,要么都不做。只有事务中所有的数据库操作都执行成功,才算整个事务成功。一个事务中任何一个SQL执行失败,已经执行成功的SQL语句也必须撤销,数据库状态应该退回到执行事务前的状态。
一致性:consistency
事务将数据库从一种一致状态转变为下一种一致状态。从事务开始前到结束后,数据库的完整性约束没有被破坏。
隔离性:isolation
隔离性要求每个读写的事务对象对其他事务对象相互分离,即该事务提交前对其他事务是不可见的。
持久性:durability
事务一旦提交,其结果就是永久性的。即使发生宕机等事故,数据库也能将数据恢复。
事务分类
扁平事务
扁平事务是事务类型总最简单的一种。在实际生产环境中,是用最频繁的事务。在扁平事务中,所有操作都处于同一层次,由BEGIN WORK开始,由COMMIT WORK或ROLLBACK WORK结束,其间的操作是原子性的,要么都执行,要么都回滚。因此,扁平事务是应用程序成为原子操作的基本组成模块。
带有保存点的扁平事务
除了支持扁平事务支持的操作外,允许在事务执行过程中会滚到同一事务的较早的一种状态。这是因为某些事务中的操作执行失败并不会导致其他所有操作到无效,放弃整个事务不符合要求,开销太大。保存点用来通知系统应该记住事务当前的状态,一遍当之后发生错误时,事务能够回到保存点当时的状态。
链事务
嵌套式事务
分布式事务
REDO
基本概念
重做日志实现事务的持久性,即ACID中的D,由两部分组成:一是内存中的重做日志缓冲;二是:重做日志文件
InnoDB是事务的存储引擎,通过Force log at Commit(在提交时强行记录)机制实现事务的持久化性,即当事务提交时,必须先将事务的所有操作日志写入重做日志文件中进行持久化,待事务的COMMIT操作完成才算完成。
为了确保每次日志都写入重做日志文件,在每次将重做日志写入重做日志文件后,InnoDB存储引擎都需要调用一次fsync操作。因为重做日志文件打开并没有使用O_DIRECT(direct直接地)选项,因此重做日志缓冲先写入文件系统缓冲。为了确保日志写入磁盘,必须进行一次fsync操作(fsync函数同步内存中所有已修改的文件数据到存储设备),由于fsync的效率取决于磁盘的性能,因此磁盘的性能决定了事务提交的性能,也就是数据库的性能。
InnoDB支持用户设置非持久化的情况发生。简单来说,就是在事务提交之后可以不立即持久化到磁盘中等待一段时间之后再执行fsync,这样可以提高数据库的性能,但是如果在第5次执行fsync之前数据库发生宕机,那么第四次和第五次之间的commit操作日志将会丢失。
可以通过innodb_flush_log_at_trx_commit参数来设置重做日志刷新到磁盘的策略。该参数默认为1,表示事务提交时必须调用一次fsync操作。0表示事务提交时不进行写入重做日志操作,该操作将在master thread中完成,master线程中每1秒回进行一次重做日志文件的fsync操作。2表示事务提交时将日志写入日志文件,但仅仅是写入文件系统的缓冲中,不进行fsync,再该设置下,只要操作系统不宕机,即便数据库宕机了,数据库重启后不会丢失日志数据。
UNDO
基本概念
redo重做日志记录了事物的行为,用于会数据库出现问题后的数据恢复。用户可以通过ROLLBACK WORK请求回滚,就可以利用undo信息将数据回滚到修改之前的样子。
redo存储在重做日志文件中,而undo存放在数据库内部的一个特殊端中,这个段成为undo段(undo segment)。undo段位于共享空间中。可以通过py_innodb_page_info.py工具查看共享空间中的undo数量。例如:
python pu_innodb_page_info.py .usr/local/mysql/data/ibdatal
需要注意的是;undo并不是将数据库物理地恢复到执行语句或事物开始之前的样子。undo是逻辑日志,只是将数据库逻辑的恢复到原来的样子。如果某个事物被回滚,事物中的操作全部别取消了,但是回滚之后数据结构和页本身可能大不相同。这是因为,事物A执行的同时,还有其他并发事务,数据结构和页的变化,并发事务的操作结果。数据库的=主要任务就是协调对数据记录的并发访问。例如:一个事务在修改当前一个页中某条记录,同时还有别的事务在对同一页中的另外几条记录进行修改。因此,不能将一个页回滚到事务开始的样子,因为这样会影响到其他事务正在进行的工作。
undo逻辑:当InnoDB执行回滚时,实际上做的是与之前相反的操作,对INSERT执行DELECT,对DELECT执行INSERT,对UPDATE执行相反的UPDATE。
undo还有另一个作用就是MVCC,当用户读取一行记录时,若该记录已经被其他事务占用,当前事务可以读取执行行版本信息,以此实现非锁定读。
存储管理
InnoDB对undo的管理采用段的方式,该段为rollback segment(回滚段),每个回滚段中可以记录1024个undo log segment,每个undo log segment段中进行undo页的申请。InnoDB1.1之前,只有一个rollback segment ,此时支持1024个事务同时在线,1.1开始支持最大128个rollback segment,故支持同时在线的事务提高到128*1024。
InnoDB1.2开始,支持rollback segment存储在共享文件之外的路径中。可以通过参数innodb_undo_directory设置rollback segment文件所在路径。通过参数innodb_undo_logs设置rollback segment的数量。
日志回收
事务被提交之后,其使用的undolog可能不再需要,因此需要PurgeThread来回收已经使用并分配的undo页。在InnoDB1.1之前,purgeThread仅仅在master线程中完成。1.1开始。purge操作可以独立到单独的线程中进行,来减轻master线程的工作压力,提高CPU的利用率和存储引擎的性能,用户可以MySQL的配置文件中添加命令来启用独立的purgeThread,命令如下:
innodb_purge_threads = 1
在1.1版本中,即使innodb_purge_threads大于1,InnoDB中启动时也会将其设置为,并且报错。
REDO与UNDO
redo log用来保证事务的持久性,undo log用来帮助事务回滚和MVCC功能,redo log基本都是顺序写的,在数据库运行时不需要对redo log的文件进行读取操作。而undo log是需要进行随机读写的。redo log存储在重做日志文件(文件系统中),undo log存储在数据库中的共享空间中。