MySql 技术内幕 (第8章 事务编程)

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 小结  

 

 

原文地址:https://www.cnblogs.com/coloz/p/13589992.html