MySQL事务概念
概念
什么是事务
数据库事务(简称:事务)是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。 当事务被提交给了DBMS(数据库管理系统),则DBMS需要确保该事务中的所有操作都成功完成且其结果被永久保存在数据库中,如果事务中有的操作没有成功完成,则事务中的所有操作都需要被回滚,回到事务执行前的状态;同时,该事务对数据库或者其他事务的执行无影响,所有的事务都好像在独立的运行。
为什么需要事务
为数据库操作序列提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法。
当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰。
事务的特性
事务的特性可以概括为ACID,具体如下。
原子性(Atomicity)
一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作。
一致性(Consistency)
事务应确保数据库的状态从一个一致状态转变为另一个一致状态。
一致状态的含义是数据库中的数据应满足完整性约束。
隔离性(Isolation)
多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
一个事务所做的修改在最终提交以前,对其他事务是不可见的。
持久性(Durability)
已被提交的事务对数据库的修改应该永久保存在数据库中。
事务类型
扁平事务 扁平事务是最简单的一种,也是实际开发中使用的最多的一种事务。在这种事务中,所有操作都处于同一层次。扁平事务的主要缺点是不能提交或回滚事务的某一部分,或者分几个独立的步骤去提交。
带有保存点的扁平事务 这种事务除了支持扁平事务支持的操作外,允许在事务执行过程中回滚到同一事务中较早的一个状态,这是因为可能某些事务在执行过程中出现的错误并不会对所有的操作都无效,放弃整个事务不合乎要求,开销也太大。保存点用来通知系统应该记住事务当前的状态,以便以后发生错误时,事务能回到该状态。
链事务 链事务,就是指回滚时,只能恢复到最近一个保存点;而带有保存点的扁平事务则可以回滚到任意正确的保存点。
嵌套事务 在事务中再嵌套事务,位于根节点的事务称为顶层事务。事务的前驱称为父事务,其它事务称为子事务。事务的前驱称为父事务,事务的下一层称为子事务。 子事务既可以提交也可以回滚,但是它的提交操作并不马上生效,除非由其父事务提交。因此就可以确定,任何子事务都在顶层事务提交后才真正的被提交了。同理,任意一个事务的回滚都会引起它的所有子事务一同回滚。
分布式事务 分布式事务通常是指在一个分布式环境下运行的扁平事务,因此需要根据数据所在位置访问网络中的不同节点。
使用事务
事务设置 在MySQL命令行的默认设置下,事务都是自动提交的,即执行SQL语句后就会马上执行COMMIT操作。
因此要显示地开启一个事务须使用命令BEGIN
或START TRANSACTION
,或者执行命令SET AUTOCOMMIT=0
,用来禁止使用当前会话的自动提交。
另外,对于常见的两种存储引擎,InnoDB支持事务,MyISAM不支持。
事务控制语句
BEGIN
或START TRANSACTION
:显示地开启一个事务;COMMIT
或COMMIT WORK
:提交事务,并使已对数据库进行的所有修改成为永久性的;ROLLBACK
或ROLLBACK WORK
:回滚,结束用户的事务,并撤销正在进行的所有未提交的修改;SAVEPOINT identifier
:在事务中创建一个保存点,一个事务中可以有多个保存点;RELEASE SAVEPOINT identifier
:删除一个事务的保存点,当没有指定的保存点时,执行该语句会抛出一个异常;ROLLBACK TO identifier
:把事务回滚到标记点;SET TRANSACTION
:设置事务的隔离级别。InnoDB存储引擎提供事务的隔离级别有READ UNCOMMITTED
、READ COMMITTED
、REPEATABLE READ
和SERIALIZABLE
。
事务隔离级别
什么是事务的隔离性
多个用户的并发事务访问同一个数据库时,一个用户的事务不应该被其他用户的事务干扰,多个并发事务之间要相互隔离。
假设多个事务不隔离,就会产生各种问题:假设有InnoDB表t(id PK, name),表中有三条记录:(1, zhaoliu)、(2, zhangsan)、(3, lisi)
case 1
事务A,先执行,处于未提交的状态:insert into t values(4, wangwu);
事务B,后执行,也未提交:select * from t;
如果事务B能够读取到(4, wangwu)这条记录,事务A就对事务B产生了影响,导致事务B读到了未提交事务操作的记录,即脏读。
case 2
事务A,先执行:select * from t where id=1,结果集为:(1, zhaoliu)
事务B,后执行,并且提交:update t set name=xxoo where id=1; commit;
事务A,再次执行相同的查询:select * from t where id=1,结果集为:(1, xxoo)
已提交事务B导致事务A的两次相同的查询,得到了不同的结果(相同的行数,但数据不一致),即“不可重复读”。
case 3
事务A,先执行:select * from t where id>3,结果集为:NULL
事务B,后执行,并且提交:insert into t values(4, wangwu); commit;
事务A,再次执行相同的查询:select * from t where id>3, 结果集为:(4, wangwu)
已提交事务B导致事务A两次查询时,数据集的行数发生了变化,即“幻读”。
可以看到,并发的事务可能导致其他事务发生如下情况:
脏读(Drity Read)
某个事务已更新一份数据(但还未提交),另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的,脏的。
不可重复读(Non-repeatable read)
在一个事务的两次查询之中数据不一致,即同一事务中,同样的条件 , 读取过的数据 , 再次读取出来发现值不一样了。这可能是两次查询过程中间插入了一个事务更新的原有的数据。
幻读(Phantom Read)
在一个事务的两次查询中数据不一致。重点在于新增或者删除:同样的条件 , 第 1 次和第 2 次读出来的记录数不一样。例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。
为避免出现上述情况,InnoDB实现了4中级别的隔离,如下:
隔离级别 | 脏读(Dirty Read) | 不可重复读(NonRepeatable Read) | 幻读(Phantom Read) |
未提交读:Read uncommitted | yes | yes | yes |
已提交读:Read committed | no | yes | yes |
可重复读:Repeatable read | no | no | yes |
可串行化:Serializable | no | no | no |
注:yes只是指有可能,并不一定会发生;no是指绝对不会发生。
未提交读:Read uncommitted,RU 在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。
已提交读:Read committed,RC 这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。
可重复读:Repeatable read,RR 这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。
可串行化:Serializable, 这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。一致性最好的,但并发性最差。
参考
Last updated