事务(Transaction) 是并发控制的基本单位。所谓的事务,它是一个操作序列,这些操作要么都 执行,要么都不执行,它是一个不可分割的工作单位。事务是数据库维护数据一致性的单位,在每 个事务结束时,都能保持数据一致性。
同时,事务有着严格的地定义,必须满足四个特性,也就是我们一直说的ACID,但是,并不是说各种数据库就一定会满足四个特性,对于不同的数据库的实现来说,在不同程度上是不一定完全满足要求的,比如,Oracle数据库来说,默认的事务隔离级别是READ COMMITTED
,是不满足隔离性的要求的。
下面我们趁热打铁,介绍一下事务的必知必会的四大特性,这几个特性也是在面试中,面试官面试MySQL的相关知识的时候,问的比较多的问题,所以,这几个特性务必需要理解并且透彻的记在心里,开个玩笑,被火车撞了,也不应该忘记这四个特性!
事务的四大特性简称为:ACID
,分别是原子性、一致性、隔离性和持久性。
下面我们一一来介绍一下。
原子性指的是整个数据库的事务是一个不可分割的工作单位,每一个都应该是一个原子操作。
当我们执行一个事务的时候,如果一系列的操作中,有一个操作失败了,那么,需要将这一个事务中的所有操作恢复到执行事务之前的状态,这就是事务的原子性。
下面举个简单的例子。
i++;
上面这个最简单不过的代码经常也会被问到,这是一个原子操作吗?那肯定不是,如果我们把这个代码放到一个事务中来说,当i+1
出现问题的时候,回滚的就是整个代码i++(i = i + 1)了,所以回滚之后,i的值也是不会改变的。
以上就是原子性的概念。
一致性是指事务将数据库从一种状态转变为下一种一致性的状态,也就是说在事务执行前后,这两种状态应该是一样的,也就是数据库的完整性约束不会被破坏。
另外,需要注意的是一致性是不关注中间状态的,比如银行转账的过程,你转账给别人,至于中间的状态,你少了500 ,他多了500,这些中间状态不关注,如果分多次转账中间状态也是不可见的,只有最后的成功或者失败的状态是可见的。
如果到分布式的一致性问题,又可以分为强一致性、弱一致性和最终一致性,关于这些概念,可以自己查查,还是很有意思的。
事务我们是可以开启很多的,MySQL数据库中可以同时启动很多的事务,但是,事务和事务之间他们是相互分离的,也就是互不影响的,这就是事务的隔离性。
事务的持久性是指事务一旦提交,就是永久的了,就是发生问题,数据库也是可以恢复的。因此,持久性保证事务的高可靠性。
事务可以分为很多中类型,一般分为:扁平事务、带有保存点的扁平事务、链事务、嵌套事务、分布式事务。
扁平事务是最简单的一种,在实际开发中也是使用的最多的一种事务。在这种事务中,所有操作都处于同一层次,最常见的方式如下:
BEGIN WORK
Operation
Operation
Operation
...
Operation N
COMMIT WORK
举个例子
begin work;
select * from user;
update user set name = 'sihai' where id = ;
commit work;
扁平事务的主要缺点是不能提交或回滚事务的某一部分,或者分几个独立的步骤去提交。
这种事务除了支持扁平事务支持的操作外,这种事务跟扁平事务最大的区别就是允许在事务执行过程中回滚到同一事务中较早的一个状态,这是因为可能某些事务在执行过程中出现的错误并不会对所有的操作都无效,放弃整个事务不合乎要求,开销也太大。保存点用来通知系统应该记住事务当前的状态,以便以后发生错误时,事务能回到该状态。
举个例子
begin work;
select * from user;
savepoint t1;
update user set name = 'sihai' where id = ;
savepoint t2;
commit work;
通过上面的方式我们就建立了两个保存点t1、t2,通过ROLLBACK TO SAVEPOINT t1
,我们就可以返回到保存点t1
。
链事务:在提交一个事务时,释放不需要的数据对象,将必要的处理上下文隐式的传给下一个要开始的事务。需要注意,提交事务操作和下一个事务操作将合并为一个原子操作,就是下一个事务可以看到上一个事务的结果。
链事务,就是指回滚时,只能恢复到最近一个保存点;而带有保存点的扁平事务则可以回滚到任意正确的保存点。
举个例子
begin work;
select * from user;
savepoint t1;
update user set name = 'sihai' where id = ;
savepoint t2;
commit work;
还是这个例子,但是对于链事务来说,是不能直接rollback到保存点t1的,最能恢复到最近的一个保存点t2;另外我们需要注意,链事务在执行commit后就会释放当前事务所持有的所有锁,而带有保存点的扁平事务不会影响所持有的锁。
在事务中再嵌套事务,这种结构有点像一颗横着的树的结构,位于根节点的事务称为顶层事务。事务的前驱称为父事务,其它事务称为子事务。事务的前驱称为父事务,事务的下一层称为子事务。
子事务既可以提交也可以回滚,但是它的提交操作并不马上生效,除非由其父事务提交。因此就可以确定,任何子事务都在顶层事务提交后才真正的被提交了。同理,任意一个事务的回滚都会引起它的所有子事务一同回滚。
BEGIN WORK
SubTransaction1:
BEGIN WORK
SubOperationX
COMMIT WORK
SubTransaction2:
BEGIN WORK
SubOperationY
COMMIT WORK
...
SubTransactionN:
BEGIN WORK
SubOperationN
COMMIT WORK
COMMIT WORK
分布式事务通常是指在一个分布式环境下运行的扁平事务,因此需要根据数据所在位置访问网络中的不同节点。
在不同的物理地址,通过网络访问,执行不同的事务,这就是分布式事务。
首先这一部分我们还是先介绍一下这些事务的语句,也不是很多,使用也不复杂,下面用一个表格做一个整理。
注意:
COMMIT
和COMMIT WORK
语句不同之处在于COMMIT WORK用来控制事务结束后的行为是CHAIN
还是RELEASE
,如果是CHAIN,那么事务就是链事务。
用户可以通过参数completion_type
控制,如下:
执行下面的操作;
SET @@completion_type = ;
BEGIN WORK;
INSERT INTO lock_test SELECT ;
COMMIT WORK;
接着我们再执行下面的操作;
INSERT INTO lock_test SELECT ;
ROLLBACK;
SELECT * FROM lock_test;
我们先插入一条数据115,然后再回滚,我们知道如果不是在一个事务的时候,115应该是会插入成功的,就算我们回滚了,但是,这里我们回滚之后,查询结果如下:
这个时候并没有115这条记录,也就是回滚生效了,说明在COMMIT WORK
之后,又是一个新的事务,所以才会出现这样的结果。
我们先进行下面的操作;
SET @@completion_type = ;
BEGIN WORK;
INSERT INTO lock_test SELECT ;
COMMIT WORK;
上面我们已经提交事务了,当我们使用下面的语句查询lock_test的数据的时候,就会出现断开连接。
SELECT * FROM lock_test;
事务的隔离级别有四种分别是:
对于这几种隔离级别会带来的问题及总结,可以查看这篇文章:MySQL 面试必备:又一神器“锁”,不会的在面试都挂了
这篇文章从下面几个内容介绍了一下MySQL数据库事务的内容,更详细的其他内容在后面的文章中再讲解。