8.1 事务概述
事务应该满足ACID。(原子性,一致性,隔离性,持久性);
-
A(atomicity) 原子性:每条语句,或者一个事务,当做一个整体,要么成功,要么失败回滚。事务是一个不可分割的单位。
-
C(consistency) 一致性:事务将数据库从一个状态转变为另外一种一致的状态,数据库的完整性约束不会被改变。 比如表中姓名不能重复,如果事务修改后回滚,表中数据姓名变得非唯一,则破坏了一致性;
-
I(isolation), 隔离性:别称并发控制,可串行化,锁。保证事务提交前其他事务看不见。
-
D(durability),持久性:凡是提交了的事务,都应该持久化到磁盘上,即使数据库崩溃,也能通过日志来恢复。
8.2 事务的分类
1) 扁平事务
实际生产环境中最频繁的一种事务,所有操作都处于同一层次,要么都执行,要么都回滚;
- 优点:使用简单,广泛使用。
- 缺点:不能提交事务的一部分,或分步骤提交。
2) 带有保存点的扁平事务
除了支持扁平事务外,允许在事务执行的过程中回滚到同一事务中的较早一个状态(保存点savepoint);
当系统发生崩溃时,所有的保存点都将消失,因为其保存点是易失的,而非持久的。
3) 链事务
提交一个事务时,释放不需要的数据对象,将必要的处理上下文隐式的传给下一个要开始的事务。
链式事务只能回滚仅限于当前事务,即只能恢复到最近一个保存点;
4) 嵌套事务
嵌套事务时一个层次结构框架,有一个顶层事务控制着各个层次的事务(子事务);
mysql不是原生的;
保存技术比嵌套查询更灵活;
当使用保存技术,无论持有多少个保存点,所有被锁住的对象都可以被得到和访问;
而嵌套查询中,不同的子事务在数据库对象上持有锁不同;
5) 分布式事务
通常是一个在分布式环境下运行的扁平事务;
8.3 事务控制语句
-
START TRANSATION / BEGIN : 显式开启一个事务;
-
COMMIT 基本= COMMIT WORK
-
ROLLBACK 基本 = ROLLBACK WORK
-
SAVEPOINT xxx : SAVEPOINT允许在事务中创建一个保存点, 一个事务中可以有多个SAVEPOINT;
-
RELEASE SAVEPOINT xxx : 删除一个事务的保存点, 当没有一个保存点执行这个语句时,会抛出异常;
-
ROLLBACK TO [SAVEPOINT] xxx : 可以把事务回滚到标记点,
例如:
update语句1 ------> update语句2 ------> savepoint ------> DELETE语句1 ------> DELETE语句2;
如果执行DELETE语句期间发生异常, 而且捕获到了这个异常,并发出ROLLBACK TO POINT 命令,事务就会回滚到指定的SAVEPOINT,撤销DELETE完成的所有工作, 而update语句完成的工作不受影响。
-
SET TRANSATION: InnoDB提供事务隔离级别有: (READ UNCOMMITTED / READ COMMITTED / REPEATABLE READ / SERIALIZABLE )
在命令行下开启事务可以: BEGIN 。。。 END
或者 START TRANSATION 。。。COMMIT
但是在存储过程中,mysql数据库的分析器会自动的把BEGIN识别为 BEGIN...END,所以在存储过程中只能使用 START TRANSATION
COMMIT 和 COMMIT WORK :
语句基本上是一致的,都是用来提交事务。
不同之处在于 COMMIT WORK 用来控制事务结束后的行为是CHAIN还是RELEASE的;
如果是CHAIN则事务变成链事务;
用户可以用completion_type来进行控制,
默认该参数为0,没有任何操作,这种设置下 COMMIT和COMMIT WORK 完全等价;
completion_type=1, COMMIT WORK 等同于 COMMIT AND CHAIN,表示马上自动开启一个相同隔离级别的事务;
completion_type=2, COMMIT WORK 等同于 COMMIT AND RELEASE, 当事务提交后悔自动断开与服务器的联接 ;
ROLLBACK和ROLLBACK WORK :
与COMMIT 和 COMMIT WORK 的工作一样;
ROLLBACK TO SAVEPOINT :
可以回滚到一个不存在的保存点,会抛出异常;
虽然有rollback 但并没有结束事务, 还需要显式地运行commit或rollback命令;
8.4 隐式提交的SQL语句
8.7 不好的事务编程习惯
8.7.1 在循环中提交
不要在循环中提交。无论是显式提交还是隐式提交。
每一次提交都要写一次redo日志;
8.7.2 使用自动提交
因为不同语言的API的自动提交设置是不一样的,容易出错。
8.7.3 使用自动回滚
最好是在程序中控制事务。
8.8 长事务
长事务: 执行时间较长的事务;
长事务最好分为批量小事务。因为长事务如果失败,回滚的代价太大了。
8.9 小结
-
SET TRANSATION: InnoDB提供事务隔离级别有: (READ UNCOMMITTED / READ COMMITTED / REPEATABLE READ / SERIALIZABLE )
在命令行下开启事务可以: BEGIN 。。。 END
或者 START TRANSATION 。。。COMMIT
但是在存储过程中,mysql数据库的分析器会自动的把BEGIN识别为 BEGIN...END,所以在存储过程中只能使用 START TRANSATION
COMMIT 和 COMMIT WORK :
语句基本上是一致的,都是用来提交事务。
不同之处在于 COMMIT WORK 用来控制事务结束后的行为是CHAIN还是RELEASE的;
如果是CHAIN则事务变成链事务;
用户可以用completion_type来进行控制,
默认该参数为0,没有任何操作,这种设置下 COMMIT和COMMIT WORK 完全等价;
completion_type=1, COMMIT WORK 等同于 COMMIT AND CHAIN,表示马上自动开启一个相同隔离级别的事务;
completion_type=2, COMMIT WORK 等同于 COMMIT AND RELEASE, 当事务提交后悔自动断开与服务器的联接 ;
ROLLBACK和ROLLBACK WORK : 与COMMIT 和 COMMIT WORK 的工作一样;
ROLLBACK TO SAVEPOINT :
可以回滚到一个不存在的保存点,会抛出异常;
虽然有rollback 但并没有结束事务, 还需要显式地运行commit或rollback命令;
8.4 隐式提交的SQL语句
8.5 事务的隔离级别
-
READ UNCOMMITTED
-
READ COMMITTED
-
REPEATABLE READ
-
SERIALIZABLE
InnoDB存储引擎默认支持的隔离级别是 REPEATABLE READ,使用Next-Key Lock的锁算法,避免了幻读的产生。在这个级别下已经可以完全保证事务的安全性。达到SERIALIZABLE的隔离级别,但是性能比其好。
隔离级别越低,事务请求的锁越少或保持锁的时间越短。
SERIALIZABLE :
InnoDB会对每一个select 语句后自动加上LOCK IN SHARE MODE,即 给每个读取操作加一个共享锁, 在这个事务隔离级别下,读占用锁了;
SERIALIZABLE主要用于InnoDB的分布式事务;
READ COMMITTED :
除了唯一性约束检查及外键约束的检查需要Gap lock,InnoDB存储引擎不会使用Gap Lock锁算法。(注意,mysql5.1以前,这个隔离级别只能工作在Replication(复制)的二进制日志为Row格式下。mysql5.1后不会了,也可以工作在STATEMENT格式下。) 最好建议是选择Row格式的二进制日志。因为记录的是行的变更,而不是SQL语句。避免出现主从不同步的现象 ;
事务的并发问题:
-
脏读 (读后数据回滚):事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
-
不可重复读 (修改导致重复读数据不一致) :事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。
-
幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
-
小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。
解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表
小结:
1、SQL规范所规定的标准,不同的数据库具体的实现可能会有些差异
2、mysql中默认事务隔离级别是可重复读时并不会锁住读取到的行
3、事务隔离级别为读提交时,写数据只会锁住相应的行
4、事务隔离级别为可重复读时,
如果有索引(包括主键索引)的时候,以索引列为条件更新数据,会存在间隙锁、行锁、下一键锁的问题,从而锁住一些行;
如果没有索引,更新数据时会锁住整张表。
5、事务隔离级别为串行化时,读写数据都会锁住整张表
6、隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大,鱼和熊掌不可兼得。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed,它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。
8.6 分布式事务编程
允许多个独立的事务资源参与到一个全局的事务中。全局事务中的事务要么全都成功,要么都回滚。在使用分布式事务时,InnoDB的隔离级别必须是SERIALIZABLE的。 InnoDB支持XA事务,并通过XA事务来支持分布式事务。异构分布式数据库可以通过XA事务实现分布式事务。
XA事务:
-
一个或多个资源管理器(提供访问事务资源的方法,一个数据库就是一个资源管理器)
-
一个事务管理器(协调参与全局事务中的各个事务)
-
一个应用程序(定义事务的边界,指定全局事务中的操作)
分布式事务使用两段提交的方式:
第一阶段,所有参与全局事务的节点都开始准备(prepare),告诉TM, 它们准备好提交了;
第二阶段,TM告诉RM's 执行ROLLBACK或者COMMIT;
如果任何一个节点显示不能提交,则所有节点都被告知需要回滚;
Mysql内部也是通过XA事务来协调两张表的操作,实现原子性的;
8.7 不好的事务编程习惯
8.7.1 在循环中提交
不要在循环中提交。无论是显式提交还是隐式提交。
每一次提交都要写一次redo日志;
8.7.2 使用自动提交
因为不同语言的API的自动提交设置是不一样的,容易出错。
8.7.3 使用自动回滚
最好是在程序中控制事务。
8.8 长事务
长事务: 执行时间较长的事务;
长事务最好分为批量小事务。因为长事务如果失败,回滚的代价太大了。
8.9 小结