事务会把数据库从一种一致状态转换为另一种一致状态。在数据库提交工作时,可以确保要么所有修改都已经保存了,要么所有修改都不保存。InnoDB 存储引擎中的事务完全符合ACID 的特性。ACID 是以下4 个词的缩写:
A (Atomicity), 原子性。指整个数据库事务是不可分割的工作单位。只有使事务中所有的数据库操作都执行成功,才算整个事务成功。
C (consistency), 一致性。一致性指事务将数据库从一种状态转变为下一种一致的状态。在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。
I (isolation) , 隔离性。隔离性还有其他的称呼,如并发控制(concurrency control) 、可串行化(serializability) 、锁(locking) 等。事务的隔离性要求每个读写事务的对象对其他事务的操作对象能相互分离,即该事务提交前对其他事务都不可见,通常这使用锁来实现。
D (durability), 持久性。事务一旦提交,其结果就是永久性的。即使发生右机等故
障,数据库也能将数据恢复
从事务理论的角度来说,可以把事务分为以下几种类型:
扁平事务(Flat Transaction) 是事务类型中最简单的一种,但在实际生产环境中,这可能是使用最为频繁的事务。在扁平事务中,所有操作都处于同一层次,其由BEGIN WORK 开始, 由COMMIT WORK 或ROLLBACK WORK 结束,其间的操作是原子的,要么都执行,要么都回滚。
带有保存点的扁平事务(Flat Transactions with Savepoint), 除了支持扁平事务支持的操作外,允许在事务执行过程中回滚到同一事务中较早的一个状态。这是因为某些事务可能在执行过程中出现的错误并不会导致所有的操作都无效,放弃整个事务不合乎要求,开销也太大。保存点(Savepoint) 用来通知系统应该记住事务当前的状态,以便当之后发生错误时,事务能回到保存点当时的状态
链事务(Chained Transaction) 可视为保存点模式的一种变种。带有保存点的扁平事务,当发生系统崩溃时,所有的保存点都将消失,因为其保存点是易失的(volatile),而非持久的(persistent) 。这意味着当进行恢复时,事务需要从开始处重新执行,而不能从最近的一个保存点继续执行。
链事务的思想是:在提交一个事务时,释放不需要的数据对象,将必要的处理上下文隐式地传给下一个要开始的事务。注意,提交事务操作和开始下一个事务操作将合并为一个原子操作。这意味着下一个事务将看到上一个事务的结果,就好像在一个事务中进行的一样。
链事务与带有保存点的扁平事务不同的是,带有保存点的扁平事务能回滚到任意正确的保存点。而链事务中的回滚仅限于当前事务,即只能恢复到最近一个的保存点。对于锁的处理,两者也不相同。链事务在执行COMMIT 后即释放了当前事务所持有的锁,而带有保存点的扁平事务不影响迄今为止所持有的锁。
嵌套事务(Nested Transaction) 是一个层次结构框架。由一个顶层事务(toplevel transaction) 控制着各个层次的事务。顶层事务之下嵌套的事务被称为子事务(subtransaction), 其控制每一个局部的变换。
分布式事务(Distributed Transactions) 通常是一个在分布式环境下运行的扁平事务, 因此需要根据数据所在位置访问网络中的不同节点。
1. 基本概念
重做日志用来实现事务的持久性,即事务ACID 中的D 。其由两部分组成: 一是内存中的重做日志缓冲(redo log buffer), 其是易失的;二是重做日志文件(redo log file),其是持久的。
InnoDB 是事务的存储引擎,其通过Force Log at Commit 机制实现事务的持久性,即当事务提交(COMMIT) 时,必须先将该事务的所有日志写入到重做日志文件进行持久化,待事务的COMMIT 操作完成才算完成。这里的日志是指重做日志,在InnoDB 存储引擎中,由两部分组成,即redo log 和undo log 。redo log 用来保证事务的持久性, undo log 用来帮助事务回滚及MVCC 的功能。redo log 基本上都是顺序写的,在数据库运行时不需要对redo log 的文件进行读取操作。而undo log 是需要进行随机读写的。
1. 基本概念
重做日志记录了事务的行为,可以很好地通过其对页进行“重做”操作。但是事务有时还需要进行回滚操作,这时就需要undo 。因此在对数据库进行修改时, InnoDB 存储引擎不但会产生redo, 还会产生一定世的undo。这样如果用户执行的事务或语句由于某种原因失败了,又或者用户用一条ROLLBACK 语句请求回滚,就可以利用这些undo信息将数据回滚到修改之前的样子。
redo 存放在重做日志文件中,与redo 不同, undo 存放在数据库内部的一个特殊段
(segment) 中,这个段称为undo 段(undo segment) 。undo 段位千共享表空间内。
delete 和update 操作可能并不直接删除原有的数据。
undo log 的介绍已经知道仅是将主键列等于1 的记录delete flag 设置为1. 记录并没有被删除,即记录还是存在于B+ 树中。其次,对辅助索引上a 等于1 , b 等于l 的记录同样没有做任何处理,甚至没有产生undo log。而真正删除这行记录的操作其实被"延时"
了,最终在purge 操作中完成。
purge 用于最终完成delete 和update 操作。这样设计是因为InnoDB 存储引擎支持MVCC ,所以记录不能在事务提交时立即进行处理。这时其他事物可能正在引用这行,故InnoDB 存储引擎需要保存记录之前的版本。而是否可以删除该条记录通过purge来进行判断。若该行记录已不被任何其他事务引用,那么就可以进行真正的delete 操作。可见, purge操作是清理之前的delete 和update 操作,将上述操作"最终"完成。而实际执行的操作为delete 操作, 清理之前行记录的版本。
若事务为非只读事务,则每次事务提交时需要进行一次fsync 操作,以此保证重做日志都已经写人磁盘。当数据库发生者机时,可以通过重做日志进行恢复。虽然固态硬盘的出现提高了磁盘的性能,然而磁盘的fsync 性能是有限的。为了提高磁盘fsync 的效率,当前数据库都提供了group commit 的功能,即一次fsync 可以刷新确保多个事务日志被写人文件。对于InnoDB 存储引擎来说,事务提交时会进行两个阶段的操作:
1 )修改内存中事务对应的信息,并且将日志写人重做日志绥冲。
2) 调用fsync 将确保日志都从重做日志缓冲写人磁盘。
步骤2) 相对步骤1 )是一个较慢的过程,这是因为存储引擎需要与磁盘打交道。但当有事务进行这个过程时,其他事务可以进行步骤1 )的操作,正在提交的事物完成提交操作后,再次进行步骤2) 时,可以将多个事务的重做日志通过一次fsync 刷新到磁盘,这样就大大地减少了磁盘的压力,从而提高了数据库的整体性能。对于写人或更新较为频繁的操作, group commit 的效果尤为明显。
在MySQL 命令行的默认设置下,事务都是自动提交( auto commit ) 的,即执行SQL 语句后就会马上执行COMMIT 操作。凶此要显式地开启一个事务锵使用命令BEGIN 、START TRANSACTION ,或者执行命令SET AUTOCOMMIT啡, 禁用当前会话的自动提交。每个数据库厂商自动提交的设置都不相同,每个DBA 或开发人员需要非常明白这一点, 这对之后的SQL 编程会有非凡的意义,因此用户不能以之前的经验来判断MySQL 数据库的运行方式。在具体介绍其含义之前, 先来看看用户可以使用哪些事务控制语句。
以下这些SQL 语句会产生一个隐式的提交操作,即执行完这些语句后,会有一个隐式的COMMIT 操作。
由于lnnoDB 存储引擎是支持事务的,因此InnoDB 存储引擎的应用需要在考虑每秒请求数( Question Per Second, Q PS ) 的同时,应该关注每秒事务处理的能力( Transaction Per Second, TPS ) 。
计算TPS 的方法是(com commit+com rollback) /time. 但是利用这种方法进行计算的前提是:所有的事务必须都是显式提交的,如果存在隐式地提交和回滚(默认autocomrnit= 1 ) ,不会计算到com comrnit 和com rollback 变量中。
ISO 和ANIS SQL 标准制定了四种事务隔离级别的标准。
SQL 标准定义的四个隔离级别为:
7.7.1 MySQL 敢据库分布式事务
InnoDB 存储引擎提供了对XA 事务的支持,并通过XA 事务来支持分布式事务的实现。分布式事务指的是允许多个独立的事务资源(transactional resources) 参与到一个全局的事务中。事务资源通常是关系型数据库系统,但也可以是其他类型的资源。全局事务要求在其中的所有参与的事务要么都提交,要么都回滚,这对于事务原有的ACID 要求又有了提高。另外,在使用分布式事务时, InnoDB 存储引擎的事务隔离级别必须设置为SERIALIZABLE 。
XA 事务允许不同数据库之间的分布式事务,如一台服务器是MySQL 数据库的,另一台是Oracle 数据库的,又可能还有一台服务器是SQL Server 数据库的,只要参与在全局事务中的每个节点都支持XA 事务。分布式事务可能在银行系统的转账中比较常见。
XA 事务由一个或多个资源管理器(Resource Managers)、一个事务管理器(Transaction Manager) 以及一个应用程序(Application Program) 组成。
分布式事务使用两段式提交(two-phase commit) 的方式。在第一阶段,所有参与全局事务的节点都开始准备(PREPARE) ,告诉事务管理器它们准备好提交了。在第二阶段,事务管理器告诉资源管理器执行ROLLBACK 还是COMMIT。如果任何一个节点显示不能提交,则所有的节点都被告知需要回滚。可见与本地事务不同的是,分布式事务需要多一次的PREPARE 操作,待收到所有节点的同意信息后,再进行COMMIT 或是ROLLBACK 操作。
在MySQL 数据库中还存在另外一种分布式事务,其在存储引擎与插件之间,又或者在存储引擎与存储引擎之间,称之为内部XA 事务。
最为常见的内部XA 事务存在于binlog 与lnnoDB 存储引擎之间。由于复制的需要,因此目前绝大多数的数据库都开启了binlog 功能。在事务提交时,先写二进制日志,再写lnnoDB 存储引擎的重做日志。对上述两个操作的要求也是原子的,即二进制日志和重做日志必须同时写入。若二进制日志先写了,而在写人lnnoDB 存储引擎时发生了宕机,那么slave 可能会接收到 master 传过去的二进制日志并执行,最终导致了主从不一致的情况。
1、在循环中提交
2、使用自动提交
3、使用自动回滚
通过批量处理小事务来完成大事务的逻辑。每完成一个小事务,将完成品的结果存放在batchcontext 表中, 表示已完成批量事务的最大账号ID 。若事务在运行过程中产生问题,需要重做事务,可以从这个已完成的最大事务ID 继续进行批量的小事务,这样重新开启事务的代价就显得比较低,也更容易让用户接受。batchcontext 表的另外一个好处是,在长事务的执行过程中,用户可以知道现在大概已经执行到了哪个阶段。比如一共有1亿条的记录,现在表batchcontext 中最大的账号ID 为4000 万,也就是说这个大事务大
概完成了40% 的工作。
这里还有一个小地方需要注意,在从表account 中取得max account no 时,人为地加上了一个共享锁,以保证在事务的处理过程中,没有其他的事务可以来更新表中的数据,这是有意义的,并且也是非常有必要的操作。