引言
事务是数据库区别于文件系统的重要特性之一。数据库系统引入事务的主要目的:事务会把数据库从一种一致状态转换为另一种一致状态。
在数据库提交工作时,可以确保其要么所有修改都已经保存了,要么所有修改都不保存。
InnoDB存储引擎中的事务完全符合ACID的特性。
- 原子性
- 一致性
- 隔离性
- 持久性
事务概述
事务是数据库区别于文件系统的重要特性之一。事务用来保证数据库的完整性——要么都做修改,要么都不做。同时,事务有严格的定义,必须同时满足四个特性。
- 原子性:原子性指数据库事务是不可分割的工作单位。只有使事务中所有的数据库操作执行都成功,才算整个事务成功。如果事务中任何一个SQL语句执行失败,那么已经执行成功的SQL语句必须撤销。数据库状态应该退回到执行事务前的状态。
- 一致性:一致性指事务将数据库从一种状态转变为下一种一致的状态。在事务开始之前和结束以后,数据库的完整性约束没有被破坏。
- 隔离性:一个事务的影响在该事务提交前对其他事务不可见。
- 持久性:事务一旦提交,结果是永久性的。
事务的实现
隔离性由锁来实现,原子性、一致性、持久性通过数据库的redo和undo来完成。
redo
在InnoDB存储引擎中,事务日志通过重做日志文件和InnoDB存储引擎的日志缓冲来实现。当开始一个事务时,会记录该事务的一个LSN(Log Sequence Number,日志序列号);当事务执行时,会向InnoDB存储引擎的日志缓冲里插入事务日志;当事务提交时,必须将InnoDB存储引擎的日志缓冲写入磁盘(默认的实现,即innodb_flush_log_at_trx_commit =1)。也就是 在写数据前,需要先写日志。这种方式称为预写日志方式(WAL)
。
InnoDB存储引擎通过预写日志的方式来保证事务的完整性。意味着在磁盘上存储的数据页和内存缓冲池中的页是不同步的。
undo
重做日志记录了事务的行为,可以很好的通过其进行“重做”,但是事务有时还需要撤销,这时就需要undo。undo和redo正好相反,对于数据库进行修改时,数据库不但会产生redo。还会产生一定量的undo,假如执行的事务或者语句由于某种原因失败了,或者使用一条rollback语句进行回滚。就可以利用undo信息将数据回滚到修改前的样子。
与redo不同的是,redo存放在重做日志文件中,undo存放在数据库内部的一个特殊段中,称为undo段,undo段位于共享表空间内。
undo操作是将数据库逻辑的恢复到原来的样子,所有修改被逻辑的取消。在多用户并发系统中,可能会有数十、数百甚至数千个并发事务。数据库的主要任务是协调对于数据记录的并发访问。如一个事务在修改当前一个页中某几条记录,但同时还有别的事务在对同一个页中另几条记录进行修改。因此,不能将一个页回滚到事务开始的样子,因为这样会影响其他事务正常进行工作。
补充,InnoDB的binlog、redo log、undo log
InnoDB存储引擎使用几种不同的日志来确保事务的ACID特性以及数据恢复能力。
1. redo Log(重做日志)
:
- 作用:重做日志主要负责记录事务对数据库所做的修改操作。他的目的是在系统崩溃时能够恢复到最新的状态。当一个事务提交时,相关的更改首先记录到redo log中,这样即使在数据还未写入到磁盘上的数据文件之前系统发生故障,也能从redo log中重做这些操作,保证事务的持久性。
- 写入时机: 在事务执行过程中,更改数据前先写入redo log缓存,然后随着事务的进行逐步刷新到磁盘上的redo log文件。
2. Undo Log(回滚日志)
- 作用:回滚事务用于实现事务的原子性和一致性,他记录了事务对数据库所做的修改的反向操作。如果事务需要回滚或者在事务隔离级别下解决并发问题,undo log可以用来撤销已经执行的操作,恢复到事务开始前的状态。
- 写入时机:在事务开始修改之前,会先写入undo log,这样在事务失败需要回滚时,根据undo log内容撤销所有改动。
3. binlog
- 作用:binlog是MySQL服务器层的日志,与存储引擎无关,主要用于数据的复制和恢复。他记录了对数据库执行的所有更改数据的SQL语句或者数据的逻辑变更。BinLog对于主从复制至关重要,因为她允许从服务器根据主服务器执行的更改来更新自己的数据。
- 写入时机:binlog是在事务提交之后写入的,意味着事务所有的更改都已经成功提交到数据库后,才会记录到binlog中,这和redo log的写入时机有所不同。
事务控制语句
-
start transaction | begin : 显示的开启一个事务
-
commit:提交事务,并使得已对数据库做的修改成为永久性的。
-
rollback:回滚事务,撤销正在进行的所有未提交的修改。
-
savepoint identifier: 在事务中创建一个保存点,一个事务中可以有多个savepoint。
-
release savepoint identifier : 删除一个事务的保存点,当没有一个保存点执行这句语句时,会抛出一个异常。
-
rollback to [savepoint] identifier : 这个语句和savepoint命令一起使用。可以把事务回滚到标记点,而不回滚此标记点之前的任何工作。例如可以发出两条update语句,后面跟一个savepoint,然后又是两条delete语句,如果执行delete语句期间出现了某种异常情况,而且捕获到了这个异常,可以发出rollback to savepoint命令,事务就会回滚到指定的savepoint,撤销delete完成的所有工作。rollback to savepoint命令并不真正的结束事务,需要显示的执行commit或者rollback命令才能结束一个事务。
-
set transaction: 这个语句用来设置事务的隔离级别。InnoDB存储引擎提供的事务隔离级别有:read uncommitted、read committed、repeatable read、serializable。
start transaction、begin语句都可以在mysql命令行下显示的开启一个事务。但是在存储过程中,mysql分析会自动将begin识别为begin…end。因此在存储过程中,只能使用start transaction语句来开启一个事务。
commit和commit work语句基本上是一致的,都用来提交事务。不同之处在于,commit work用来控制事务结束后的行为,是chain还是release。可以通过参数completion_type来进行控制,默认参数为0,表示没有任何操作。在这种设置下,commit和commit work是完全等价的。当参数completion_type为1时,commit work等价于commit and chain,表示马上自动开启一个相同隔离级别的事务。completion_type为2时,commit work 等同于commit and release 表示事务提交后自动断开和服务器的连接。
InnoDB存储引擎中的事务都是原子的,这说明下述两种情况:或者构成事务的每条语句都会提交(成为永久),或者所有语句都会回滚。
事务的隔离级别
SQL标准定义的四个隔离级别为
- read uncommitted
- read committed
- repeatable read
- serializable。
InnoDB存储引擎默认的支持隔离级别是repeatable read ,但是与标准SQL不同的是,InnoDB存储引擎在repeatable read事务隔离级别下,使用Next-Key Lock锁的算法,因此避免幻读的产生。因此,InnoDB存储引擎在默认隔离级别下已经能完全保证事务的隔离性要求,即达到SQL标准的serializable隔离级别。
隔离级别越低,事务请求的锁越少,或者保持锁的时间越短。在InnoDB存储引擎中,可以使用如下命令设置当前会话或者全局的事务隔离级别
set [global|session] transaction isolation level [read uncommitted| read committed|repeatable read | serializable]
如果想在MySQL库启动时就设置事务的默认隔离级别,需要修改MySQL配置文件如下
[mysqld]
transaction-isolation = read-committed
在serializable的事务隔离级别,InnoDB存储引擎会对每个select语句后自动加上lock in share mode,即给每个读取操作加一个共享锁。因此在这个事务隔离级别下,读占用锁了,一致性的非锁定读不在予以支持。serializable的事务隔离级别主要用于InnoDB存储引擎的分布式事务。
在read committed的事务隔离级别下,除了唯一性的约束检查以及外键约束检查需要Gap Lock,InnoDB存储引擎不会使用Gap Lock的锁算法。
分布式事务
InnoDB存储引擎支持XA事务,通过XA事务可以支持分布式事务的实现。分布式事务指的是允许多个独立的事务资源参与一个全局的事务中。事务资源通常是关系型数据库系统,全局事务要求在其中所有参与的事务要么都提交,要么都回滚。另外,在使用分布式事务时,InnoDB存储引擎事务隔离级别必须设置为serializable。
分布式事务可能在银行的转账系统中比较常见,如一个用户需要从上海转10000元到北京的一个用户上。
这种情况一定要使用分布式事务,如果不能都提交或者都回滚,在任何一个节点出现问题都会导致严重的结果。
分布式事务由一个或者多个资源管理器、一个事务管理器、一个应用程序组成。
- 资源管理器:提供访问事务资源的方法。通常一个数据库就是一个资源管理器。
- 事务管理器:协调参与全局事务中的每个事务,需要和参与全局事务中的所有资源管理器进行通信。
- 应用程序:定义事务的边界,指定全局事务中的操作。
在MySQL分布式事务中,资源管理器就是MySQL数据库,事务管理器为连接到MySQL服务器的客户端。
分布式事务采用两阶段提交的方式,在 第一个阶段,所有参与全局事务的节点都开始准备,告诉事务管理器他们准备好提交了,在第二个阶段,事务管理器告诉资源管理器执行rollback还是commit。如果任何一个节点显示不能提交,则所有节点被告知需要回滚。