一、事务属性与架构概述
1、基本概念:
事务(Transaction)是访问和更新数据库的程序执行单元;事务中可能包含一个或多个sql语句,这些语句要么都执行,要么都不执行。作为一个关系型数据库,MySQL支持事务,本文介绍基于MySQL5.6。
ACID四个属性,即:
1、原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。如果事务在执行过程中发生错误,事务会被回滚(Rollback)到事务开始前的状态。实现主要基于undo log日志。
2、持久性(Durability):保证事务提交后不会因为宕机等原因导致数据丢失。实现主要基于redo log日志。
3、隔离性(Isolation):保证事务执行尽可能不受其他事务影响。InnoDB默认的隔离级别是RR,RR的实现主要基于锁机制、数据的隐藏列、undo log和类next-key lock机制。
4、一致性(Consistency):事务追求的最终目标,一致性的实现既需要数据库层面的保障,也需要应用层面的保障。
2. MySql的逻辑架构和存储引擎
如上图所示,MySQL服务器逻辑架构从上往下可以分为三层:
(1)第一层:处理客户端连接、授权认证等。
(2)第二层:服务器层,负责查询语句的解析、优化、缓存以及内置函数的实现、存储过程等。
(3)第三层:存储引擎,负责MySQL中数据的存储和提取。MySQL中服务器层不管理事务,事务是由存储引擎实现的。MySQL支持事务的存储引擎有InnoDB、NDB Cluster等,其中InnoDB的使用最为广泛;其他存储引擎不支持事务,如MyIsam、Memory等。
如无特殊说明,后文中描述的内容都是基于InnoDB。
3、提交和回滚
典型的MySQL事务是如下操作的:
start transaction;
…… #一条或多条sql语句
commit;
其中start transaction标识事务开始,commit提交事务,将执行结果写入到数据库。如果sql语句执行出现问题,会调用rollback,回滚所有已经执行成功的sql语句。当然,也可以在事务中直接使用rollback语句进行回滚。
自动提交
MySQL中默认采用的是自动提交(autocommit)模式,如下所示:
在自动模式提交的情况下,如果没有start transaction显示地开启一个事务,那么每个sql语句都将自动开启事务执行提交操作。
通过如下的命令可以关闭事务的自动提交, 需要注意的是,autocommit参数是针对连接的,在一个连接中修改了参数,不会对其它连接产生影响。
如果关闭了autocommit,那么所有的sql语句都在一个事务中,直到执行了commit或者rollback,改事务结束,同时开始了另一个事务。
特殊操作:
- 在MySQL中,存在一些特殊的操作,如果在事务中执行了他们,会马上执行commit提交事务:如DDL语句(create table / drop table / alter table / lock tables)等语句。
- 不过,常用的select、Insert 、update和delete命令,都不会强制提交事务。
一致性是事务的最终目的,原子性、隔离性、持久性都是为了实现一致性!!事务的执行流程如下图所示:下文会具体解释相关日志和该执行流程
二、事务属性详述
1、什么是原子性
事务的原子性就如原子操作一般,表示事务不可再分,其中的操作要么都做,要么都不做。如果事务中一个SQL语句执行失败,则已执行的语句也必须回滚,数据库退回到事务前的状态。
事务的原子性表明事务就是一个整体,当事务无法成功执行的时候,需要将事务中已经执行过的语句全部回滚,使得数据库回归到最初未开始事务的状态。
事务的原子性就是通过undo log日志进行实现的。当事务需要进行回滚时,InnoDB引擎就会调用undo log日志进行SQL语句的撤销,实现数据的回滚。那么undo log是什么呢?
回滚日志(undo log)是InnoDB引擎提供的日志,顾名思义,回滚日志的作用就是对数据进行回滚。当事务对数据库进行修改,InnoDB引擎不仅会记录redo log(后面会讲),还会生成对应的undo log日志。如果事务执行失败或调用了rollback,导致事务需要回滚,就可以利用undo log中的信息将数据回滚到修改之前的样子。
但是undo log与redo log不一样,它属于逻辑日志。它对SQL语句执行相关的信息进行记录。当发生回滚时,InnoDB引擎会根据undo log日志中的记录做与之前相反的工作。
比如对于每个数据插入操作(insert),回滚时会执行数据删除操作(delete);
对于每个数据删除操作(delete),回滚时会执行数据插入操作(insert);
对于每个数据更新操作(update),回滚时会执行一个相反的数据更新操作(update),把数据改回去。
undo log有两个作用,一是提供回滚,二是实现MVCC功能。
二、什么是持久性
事务的持久性是指当事务提交之后,数据库的改变就应该是永久性的,而不是暂时的。这也就是说,当事务提交之后,任何其它操作甚至是系统的宕机故障都不会对原来事务的执行结果产生影响。事务的持久性是通过InnoDB存储引擎中的redo log日志来实现的。
重做日志(redo log)是InnoDB引擎层的日志,用来记录事务操作引起数据的变化,记录的是数据页的物理修改。InnoDB引擎对数据的更新,是先将更新记录写入redo log日志,然后会在系统空闲的时候或者是按照设定的更新策略再将日志中的内容更新到磁盘之中。这就是所谓的预写式技术(Write Ahead logging)。这种技术可以大大减少IO操作的频率,提升数据刷新的效率。
redo log有一些细节需要我们注意,redo log日志的大小是固定的,为了能够持续不断的对更新记录进行写入,在redo log日志中设置了两个标志位置,checkpoint和write_pos,分别表示记录擦除的位置和记录写入的位置。这种结构很像一个循环队列:
三、什么是隔离性
原子性和持久性是单个事务本身层面的性质,而隔离性是指事务之间应该保持的关系。隔离性要求不同事务之间的影响是互不干扰的,一个事务的操作与其它事务是相互隔离的。由于事务可能并不只包含一条SQL语句,所以在事务的执行期间很有可能会有其它事务开始执行,因此多事务的并发性就要求事务之间的操作是相互隔离的。
事务之间的隔离,是通过锁机制实现的。当一个事务需要对数据库中的某行数据进行修改时,需要先给数据加锁。加了锁的数据,其它事务是不运行操作的,只能等待当前事务提交或回滚将锁释放。锁机制并不是一个陌生的概念,在许多场景中都会利用到不同实现的锁对数据进行保护和同步。而在MySQL中,根据不同的划分标准,还可将锁分为不同的种类。
- 按照粒度划分:行锁、表锁、页锁
- 按照使用方式划分:共享锁、排它锁
- 按照思想划分:悲观锁、乐观锁
四、什么是一致性
一致性是指事务执行结束后,数据库的完整性约束没有被破坏,事务执行的前后都是合法的数据状态。一致性是事务追求的最终目标,原子性、持久性和隔离性,实际上都是为了保证数据库状态的一致性而存在的。
换句话说,ACID里的AID都是数据库的特征,也就是依赖数据库的具体实现。而唯独这个C,实际上它依赖于应用层,也就是依赖于开发者。这里的一致性是指系统从一个正确的状态,迁移到另一个正确的状态。什么叫正确的状态呢?就是当前的状态满足预定的约束就叫做正确的状态。而事务具备ACID里C的特性是说通过事务的AID来保证我们的一致性。