概念
事务是数据库区别于文件系统的重要特性之一。
引入数据库的目的:实物会把数据库从一种一直状态转换为另一种一致状态。
事务可以由非常简单的SQL语句组成,也可以由一组复杂的SQL语句组成。
ACID特性
原子性
原子性是指数据库事务时不可分割的工作单位。只有使实物中所有的数据库操作都执行成功,才算整个事务成功。事务中任何一个SQL语句执行失败,已经执行成功的SQL语句也必须撤销,退回到事务前的状态。
一致性
事务将数据库从一种状态转变为下一种一致的状态。在事务开始之前和事务结束之后,数据库的完整性约束没有被破坏。
隔离性
每个读写事务的对象对其他事务的操作对象相互分离,即该事务提交前对其他事务都不可见,通常使用锁来实现。
持久性
事务一旦提交,其结果就是永久性的。及时发送宕机等故障,数据库也能将数据恢复。
事务的分类
扁平事务
它使用最频繁,最简单的。
begin work
operation1
operation2
operation3
commit work/rollback work
当出现错误,可以rollback进行回滚,全部回滚,这样很麻烦,因此就有了带有保存点的扁平事务。
带有保存点的扁平事务
某些事务可能在执行过程中出现的错误并不会导致所有的操作都无效,放弃整个事务不合乎要求,开销太大。保存点用来通知系统应该记住事务当前的状态,以便当之后发生错误时,事务能回到保存点当时的状态。
begin work(隐含save work 1)
action1
action2
save work 2
action3
save work 3
action4
roolback work:2
事务并不会完全回滚,只是回滚到了保存点2而已。
链事务
可视为保存点模式的一种变种。带有保存点的扁平事务,当发生系统崩溃时,所有的保存点都将消失,因为保存点时易失的,而非持久的。
链事务的思想:在提交一个事务时,释放不需要的数据对象,将必要的处理上下文隐式地传给下一个要开始的事务。注意,提交事务操作和开始下一个事务操作合并为一个原子操作。这意味着下一个事务将看到上一个事务的结果,就好像在事务中进行一样。
第一个事务的提交出发第二个事务的开始。只能恢复最近一个的保存点。
嵌套事务
嵌套事务是一个层次结构框架。由一个层次事务控制着各个层次的事务。层次事务之下嵌套的事务被称为子事务,其控制每一个局部的变换。
相当于是一颗树。子树可以时嵌套也可以是扁平事务。
叶子节点的事务是扁平事务。
实际的工作交给叶子节点完成,即只有叶子节点的事务才能访问数据库、发送消息、获取其他类型的资源。高层事务仅负责逻辑控制,决定何时调用相关的子事务。
嵌套查询中,事务p1持有x,y的锁,开始调用子事务p11,那么父事务p1可以传递或不传递锁,如果p11中还持有y锁,那么p1会持有x,y,z锁。
分布式事务
通常是在一个分布式环境下运行的扁平事务,因此需要根据数据所在位置访问网络中的不同节点。
事务的实现
原子性、一致性、持久性通过数据库的redo log和undo log来完成。隔离性通过锁来实现。
redo和undo的作用都可以视为时一种恢复操作,redo恢复提交事务修改的页操作,而undo回滚行记录到某个特定的版本。因此两者记录的内容不同,redo通常是物理日志,记录的是页的物理修改操作。undo是逻辑日志,根据每行记录进行记录。
redo
基本概念:
重做日志用来实现事务的持久性。由两部分组成:一是内存中的重做日志缓冲(redo log buffer),其是易失的;二是重做日志文件(redo log file),其时持久的。
Innodb当事务提交时,必须先将该事务的所有日志写入到重做日志文件进行持久化,待事务的commit操作完成才算完成。
innodb中重做日志由redo log和undo log组成。redo log用来保证事务的持久性,undo log用来帮助事务回滚及MVCC的功能。 redo log基本上时顺序写的,在数据库运行时不需要对redo log的文件进行读取操作。而undo log是需要进行随机读写的。
log block
innodb重做日志以512字节进行存储,大小和磁盘扇区大小一样,重做日志缓存、重做日志文件都是以块的方式进行保存的,称之为重做日志块(redo log block),每块的大小为512字节。
log buffer是由log block组成,在内部log buffer好似一个数组。
log group
重做日志组,其中有多个重做日志文件。它是一个逻辑上的概念。
log buffer根据一定的规则将内存中的log block刷新到磁盘。
1.事务提交时
2.当log buffer中有一般的内存空间被使用
3.log checkpoint时
LSN
代表日志序列号,占8字节,单调递增。
恢复
Innodb在启动时不管上次数据库运行时是否正常关闭,都会尝试进行恢复操作。重做日志时物理日志,因此恢复速度快。
例如对于INSERT操作,其记录的时每个页上的变化。对于下面的表:
CREATE TABLE t (a int, b int, primary key(a), key(b));
若执行SQL语句:
INSERT INTO t SELECT 1,2;
由于需要对聚集索引页和辅助索引页进行操作,其记录的重做日志大致为:
page(2,3),offset 32, value 1,2#聚集索引
page(2,4),offset 64,value 2#辅助索引
记录的是页的物理修改操作,若插入设计B+树的split,可能会有更多的页需要记录日志。
undo
基本概念
重做日志记录了事务的行为,可以很好地通过其对页进行“重做”操作。但是事务有时还需要进行回滚操作,这时就需要undo。
在对数据库进行修改时,innodb不但会产生redo,还会产生一定量的undo。如果用户执行事务或语句由于某种原因失败了,又或者用户用一条ROLLBACK语句请求回滚,就可以利用这些undo信息将数据回滚到修改之前的样子。
用户执行insert 10w条记录,事务会导致分配一个新的段,即表空间会增大,但rollback,事务回滚,表空间大小不会改变。
当innodb回滚时,实际执行先前相反的工作。
INSERT操作,会执行delete。delete操作,会执行INSERT。update操作,会执行相反的update操作。
undo 存储管理
innodb对undo同样采用段的方式。innodb有rollback segment,每个回滚段记录了1024个undo log segment。
当事务提交时,innodb会做两件事情:
1.将undo log放入列表中,以供之后的purge操作
2.判断undo log所在的页是否可以重用,若可以分配给下个事务使用。