前言

  这段时间自己会把之前学的东西都总结一遍,希望对自己以后的工作中有帮助。其实现在每天的状态都是很累的,但是我要坚持!

  进入我们今天的正题:

  为什么MySQL要 有事务呢?事务到底是用来干什么的?我们通过一个例子来说明:

  事务广泛的运用于订单系统、银行系统等多种场景。如果有以下一个场景:A用户和B用户是银行的储户。现在A要给B转账500元。那么需要做以下几件事:

    1)检查A的账户余额>500元;
    2)A账户扣除500元;
    3)账户增加500元;

  正常的流程走下来,A账户扣了500,B账户加了500,皆大欢喜。那如果A账户扣了钱之后,系统出故障了呢?A白白损失了500,而B也没有收到本该属于他的500。

  以上的案例中,隐藏着一个前提条件:A扣钱和B加钱,要么同时成功,要么同时失败。事务的需求就在于此!

一、MySQL事务概述

1.1、MySQL事务简介

访问并更新数据库中各种数据项的一个程序执行单元。在事务中的操作,要么都执行修改,要么都不执行,这就是事务的目的,也是事务模型区别于文件系统的重要特征之一。

用于处理操作量大,复杂度高的数据。比如说,在员工管理系统中,删除一个员工,既需要删除员工的基本资料,也要删除和该员工相关的其他信息。这些数据库操作语句就构成一个事务。

InnoDB数据库引擎的数据库或者表才支持事务。

用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行。

  事务用来管理 insert,update,delete 语句

1.2、事务的四大特性(ACID特性)

事务必须同时满足4个特性,即通常所说事务的ACID特性。虽然理论上定义了严格的事务要求,但是数据库厂商出于各种目的并没有严格满足事务的ACID标准。

  例如,对于MYSQL的NDB Cluster引擎,虽然支持事务,但是不满足D的要求,即持久性的要求。对于Oracle数据库来说,其默认的事务隔离级别为READ COMMITTED,不满足I的要求,即隔离性的要求。

  对于InnoDB存储引擎而言,默认的事务隔离级别是READ REPRATABLE,完全遵循和满足事务的ACID特性。

事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。

事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。

    (例如:拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。)

当多个用户并发访问数据库操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。

一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

关系数据库管理系统(RDBMS,数据库系统)来实现的。数据库管理系统采用日志来保证事务的原子性、一致性和持久性。日志记录了事务对数据库所做的更新,

     如果某个事务在执行过程中发生错误,就可以根据日志,撤销事务对数据库已做的更新,使数据库退回到执行事务前的初始状态。数据库管理系统采用锁机制来实现事务的隔离性。

     当多个事务同时更新数据库中相同的数据时,只允许持有锁的事务能更新该数据,其他事务必须等待,直到前一个事务释放了锁,其他事务才有机会更新该数据。

1.3、事务分类

  扁平事务:最简单,使用最频繁的事务。在扁平事务中,所有的操作都处于一个层次,其有BEGIN WORK开始,有COMMIT WORK或ROLLBACK WORK结束。处于之间的操作是原子的,要么全部执行,要么全部回滚。

  带有保存点的扁平事务:除了扁平事务支持的操作外,允许在事务执行过程中回滚到同一事务中较早的一个状态,这是因为可能有些事务在执行过程中出现的错误并不会对有的操作都无效,

            放弃整个事务不合乎要求,开销也太大。保存点用来通知系统应该记住事务当前的状态,以便以后发生错误时,事务能回到该状态。

  链事务:可视为保存点模式的一个变种。

  嵌套事务:一个层次结构框架。

  分布式事务

二、事务控制语句

  在MYSQL命令行的默认设置下,事务都是自动提交的,即执行SQL语句后就会马上执行COMMIT操作。

BEGIN和START TRANSACTION,或者执行命令SET AUTOCOMMIT = 0,以禁用当前会话的自动提交。

  1)START TRANSACTION | BEGIN

    显示的开启一个事务。在存储过程中,MYSQL数据库的分析器会自动将BEGIN识别为BEGIN...END,因此在存储过程中只能使用START TRANSACTION语句来开启一个事务。

  2)COMMIT

只需发出COMMIT。COMMIT会提交事务,并使已对数据库进行的所有修改成为永久性的。COMMIT和COMMIT WORK语句基本上是一致的,都是用来提交事务。

    不同的是COMMIT WORK用来控制事务结束后的行为是CHAIN还是RELEASE的。如果是CHAIN方式,那么事务就变成了链事务。用户可以通过参数completion_type来进行控制,默认该参数是0,表示没有任何操作。

    在这种设置下,COMMIT和COMMIT WORK是完全等价的。当参数值为1时,COMMIT WORK等价于COMMIT AND CHAIN,表示马上自动开启一个相同隔离级别的事务。

    当参数值为1时,COMMIT WORK等价于COMMIT AND RELEASE。当提交事务后会自动断开与服务器连接。

  3)ROLLBACK

    回滚会结束用户的事务,并撤销正在进行的所有未提交的修改。

  4)SAVEPOINT identifiter

创建一个保存点,一个事务可以有很多个保存点。

  5)RELEASE SAVEPOINT identifier

    删除一个事务的保存点,当没有一个保存点执行这语句时,会抛出一个异常。

  6)ROLLBACK to [SAVEPOINT] identifier

。可以把事务回滚到标记点,而不回滚到此标记点之前的任何工作。

    注意:虽然有ROLLBACK,但是它并没有真正的结束一个事务,因此即使执行了ROLLBACK TO SAVEPOINT,之后也需要显示的运行COMMIT或ROLLBACK命令。

  7)SET TRANSACTION

用来设置事务的隔离级别。InnoDB存储引擎提供的事务隔离级别有READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ和SERIALIZABLE。

三、MySQL事务处理的两种方法

1)用 BEGIN,ROLLBACK,COMMIT来实现

  BEGIN 开始一个事务

  ROLLBACK 事务回滚

  COMMIT 事务确认

2)直接用SET来改变MySQL的自动提交模式

  SET AUTOCOMMIT=0 禁止自动提交

  SET AUTOCOMMIT=1 开启自动提交

四、MySQL中事务的隔离级别

4.1、在MySQL中如果不考虑事务的隔离性,会发生的几个问题

  1)脏读

  指在一个事务处理过程里读取了另一个未提交的事务中的数据。

  当一个事务正在多次修改某个数据,而在这个事务中这多次的修改都还未提交,这时一个并发的事务来访问该数据,就会造成两个事务得到的数据不一致。

  举例:

    用户A向用户B转账100元,对应SQL命令如下:

    update account set money=money+100 where name=’B’;(此时A通知B)

    update account set money=money-100 where name=’A’;

    当只执行第一条SQL时,A通知B查看账户,B发现确实钱已到账(此时即发生了脏读),而之后无论第二条SQL是否执行,只要该事务不提交,则所有操作都将回滚,那么当B以后再次查看账户时就会发现钱其实并没有转。 

  2)不可重复读

  指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询的时候,被另一个事务修改并提交了。

  举例:

    事务T1在读取某一数据,而事务T2立马修改了这个数据并且提交事务给数据库,事务T1再次读取该数据就得到了不同的结果,发送了不可重复读。

  不可重复读和脏读的区别是,脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了前一事务提交的数据。

  在某些情况下,不可重复读并不是问题,比如我们多次查询某个数据当然以最后查询得到的结果为主。但在另一些情况下就有可能发生问题,例如对于同一个数据A和B依次查询就可能不同,A和B就可能打起来了。 

  3)虚读(幻读)

  幻读是事务非独立执行时发生的一种现象。

  举例:

    事务T1对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作,这时事务T2又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。

    而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务T2中添加的,就好像产生幻觉一样,这就是发生了幻读。

  幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。

4.2、MySQL数据库的四种隔离界别

READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ和SERIALIZABLE。       

  1)Serializable (串行化):可避免脏读、不可重复读、幻读的发生。

    它通过强制事务串行执行,避免了前面说的幻读的问题。
  2)Repeatable read (可重复读):可避免脏读、不可重复读的发生。

    解决了脏读,也保证了在同一个事务中多次读取同样记录的结果是一致的。但是理论上,可重读读隔离级别还是无法解决另外一个幻读的问题,

    指的是当某个事务在读取某个范围内的记录时,另外一个事务也在该范围内插入了新的记录,当之前的事务再次读取该范围内的记录时,会产生幻行。
  3)Read committed (读已提交):可避免脏读的发生。

    一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。

    这个级别有时候也叫做不可重复读,因为两次执行相同的查询,可能会得到不一样的结果。因为在这2次读之间可能有其他事务更改这个数据,每次读到的数据都是已经提交的。
  4)Read uncommitted (读未提交):最低级别,任何情况都无法保证

    事务中的修改,即使没有提交,在其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读。

  分析:

    1)以上四种隔离级别最高的是Serializable级别,最低的是Read uncommitted级别,当然级别越高,执行效率就越低。像Serializable这样的级别,

     就是以锁表的方式(类似于Java多线程中的锁)使得其他的线程只能在锁外等待,所以平时选用何种隔离级别应该根据实际情况。在MySQL数据库中默认的隔离级别为Repeatable read (可重复读)。

    2)在MySQL数据库中,支持上面四种隔离级别,默认的为Repeatable read (可重复读)。

     而在Oracle数据库中,只支持Serializable (串行化)级别和Read committed (读已提交)这两种级别,其中默认的为Read committed级别。

    3)Repeatable read是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。

     不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,

     当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。

MySQL数据库中查看当前事务的隔离级别:select @@tx_isolation;

设置事务的隔离级别:

      set [glogal|session] transaction isolation level 隔离级别名称;
        set tx_isolation=’隔离级别名称’;

     如果想启动时就设置事务的默认隔离级别,修改MYSQL的配置文件,在[mysqld]中添加如下行:

[mysqld]

transaction-isolation = READ-COMMITTED

     注意:设置数据库的隔离级别一定要是在开启事务之前!