事务(Transaction)管理机制


一、总    结


  1. 事务是一个最小的不可再分的工作单元。 一个事务对应一套完整的业务逻辑。
  2. 事务管理机制的作用 —— 通过保证一系列数据操作过程的完整性,来保障数据的安全性。
  3. 使用事务的前提条件    ——    数据库管理系统必须使用支持事务的存储引擎。
  4. MySQL中默认采用InnoDB存储引擎,默认采用【自动提交模式】来管理事务。
  5. 事务机制的经典使用场景    ——    处理“银行账户之间的转账操作" 。
  6. 事务只和DML语句有关,只有DML语句的出现才存在事务管理。
  7. 开启事务管理的标志 —— DML语句的执行。
  8. 结束事务管理的标志 —— TCL语句的执行
  9. 事务管理的四大特性 —— ACID : 【原子性、一致性、隔离性、持久性】
  10. 事务之间由低到高的四个隔离级别 —— 【读未提交、读已提交、可重复读、串行化读】
  11. 可以把一系列要执行的操作称为事务。而事务管理就是管理这些操作要么完全执行,要么完全不执行。反映在SQL中就是,事务只和DML语句有关。通常,一个完整的业务需要通过执行批量的DML语句之后,进行统一提交事务,才能达到效果。


二、解    析


(一)银行转账业务的分析

          银行转账过程是一个完整的业务,最小的单元,不可再分,也就是说银行转账业务是一个完整的事务。
1) t_act 账户表中的数据如下:    
            
2)需求:账户“1001”要 转账给 “1002”账户。
      该一系列“转账”操作,应当会导致数据库表中的相关数据发生修改。假设转账金额是500元。 就应该执行下列两条DML语句:    
update t_act set balance = 1000.0 where actno = ‘act-001’;
update t_act set balance = 500.0 where actno = ‘act-002’;

3)分析:
- 以上两条DML语句必须同时成功或者同时失败,因为它为最小业务单元,不可拆分;
- 当第一条DML语句执行成功之后,并不能立马将底层数据库中的第一个账户的数据修改,应该只是将操作记录了一下,这个记录是在内存中完成的。
- 当第二条DML语句执行成功之后,底层数据库文件中的数据需要完成同步。
- 若第二条DML语句执行失败,将清空所有的历史操作记录
4)技术(机制):
        要完成以上功能,那必须借助mysql数据库的“事务“机制 transaction。
在mysql中并不是所有的数据存储引擎都支持事务管理的,只有 Innodb数据存储支持事务管理。且默认采用自动提交事务方式。
(即执行DML语句时,默认自动提交事务。)
在mysql中,,常用的3种存储引擎?
        1)myISam   (不支持事务)
        2)InnoDB      (MySql数据库默认使用是InnoDB存储引擎。该存储引擎支持事务。)
        3)memory       (不支持事务)
2. )一个完整的业务需要批量的DML语句(insert、update、delete)共同完成。  
比如:银行账户转账 业务
   从A账户向B账户转账10000 , 需要执行两条update语句:   
                update t_act set balance = balance - 10000 where actno = 'act-001';     
                  update t_act set balance = balance + 10000 where actno = 'act-002';       
   以上两条DML语句必须同时成功,或者同时失败,不允许出现一条成功,一条失败。
  要想保证以上的两条DML语句同时成功或者同时失败,那么就需要使用数据库的“事务机制”。
   思考:假设所有的业务都能使用1条DML语句搞定,还需要事务机制吗?  
    该情况不需要使用到事务。
    但实际开发中,通常一事务【业务】需要多条DML语句共同联合完成。
 事务只和DML语句有关系;当执行DML语句时,其实就是开启了一个事务。
    或者说 只有DML语句(insert/ delete/ update)中才存在事务。 
    因为它们这三个语句都是和数据库表当中的“数据”相关的。
   以上所描述的批量DML语句共有多少DML语句,这个和业务逻辑有关系。
   业务逻辑不同,则DML语句的个数不同。
4. )事务可以保证多个操作的原子性。即,事务的存在就是为了保证数据的完整性,安全性。*
   事务可以保证多个操作的原子性:要么全部成功,要么全部失败。
   对于数据库来说,事务保证批量的DML要么全部成功,要么全部失败
5. )【TCL】语句是和事务相关的语句 ** 
rollback 或commit 的执行,标志着事务的结束。
 4.事务管理?
可以把一系列要执行的操作称为事务。
而事务管理就是管理这些操作要么完全执行,要么完全不执行。
  经典例子:
  A要给B转钱,首先A账户钱减少了,但由于数据库突然断电,导致无法给B账户加钱。
  然后由于丢失数据,B不承认收到A的钱;
  在这里事务就是确保加钱和减钱两个都完全执行或完全不执行,如果加钱失败,那么不会发生减钱。

0)-手动开启事务管理的命令
start transaction  
 这行命令作用是:
 【start transaction命令的作用】:
     在mysql开启了“自动提交”模式的状态下,关闭了事务的自动提交机制,对接下来要执行的DML语句进行手动开启事务管理。
     手动开启事务管理之后,从此mysql不再将DML语句执行出来的结果立即更新到表中。而是会记录到事务的日志中。只有开发者手动地进行commit提交成功之后,mysql才会将DML执行结果同步到数据库的表中。
 1)-事务开启的标志
 任何一条DML语句执行,都标志事务的开启。
 2)-事务结束的标志
 commit 或 rollback的执行,都标志着事务的结束。
1)事务提交  操作
 语法:commit    ———— 事务成功的结束。
                  会将所有的DML语句操作记录 和 底层硬盘文件中数据进行一次同步。
2)事务回滚 操作
 语法:rollback   ————  事务失败的结束。
              会将所有的DML语句操作记录全部清空。   
回滚会清掉开始事务管理之后写到事务日志中的内容,即恢复到开启事务管理之前。
注意:回退操作只是回退"写"的内容,对于普通的读表select语句不能回退。
3)-事务管理的意义
        保证数据操作的完整性,安全性。
4)-注意事项
在事务进行过程中,未结束之前,DML语句是不会修改底层数据库文件中的数据。只是将历史操作在内存中记录一下。只有在事务结束,而且是成功结束(提交)的时候,才会修改底层硬盘文件中的数据。
 只能回滚 insert、delete和update语句,不能回滚select语句(回滚select没有任何意义)。
  对于create、drop、alter这些语句也无法回滚。
当 commit 或 rollback 语句执行后,事务会自动关闭(将来的更改会隐含提交)。

(二)事务管理的四大特性 

1.原子性(Atomicity)

        事务的整个操作是一个整体,不可以分割,要么全部成功,要么全部失败。
        事务是最小的工作单元,不可再分。
        只有两种结果:成功 或 失败
2.一致性(Consistency)
        指的是事务操作的前后,数据表中的数据没有变化。
        事务必须保证多条DML语句同时成功或者同时失败。

3.隔离性(Isolation)

        事务之间的操作是相互隔离不受影响的。
        事务A与事务B之间具有隔离。一个事务不会影响其他的事务运行。

4.持久性(Durability)

        数据一旦提交,不可改变,永久的改变数据表数据
        持久性说的是最终数据必须持久化到硬盘文件中,事务才算成功的结束。
        只有事务成功提交的情况下,才有持久性。
         在事务完成后,该事务对数据库所做的更改将持久化地保存在数据库中,事务才算成功的结束。且不会 被回滚。

(三)事务管理的提交模式

        自动提交模式的状态用于决定新事务 如何启动 以及 何时启动。
        自动提交模式的开启或关闭,可以通过服务器变量AUTOCOMMIT来控制。    

1-启用 自动提交模式

如果自动提交模式被启用,则单条DML语句将缺省地开始一个新的事务。
如果该语句执行成功,事务将自动提交,并永久地保存该语句的执行结果。
如果语句执行失败,事务将自动回滚,并取消该语句的执行结果。
在自动提交模式下,仍可使用start transaction语句来显式地启动事务。这时,一个事务仍可包含多条语句,直到这些语句被统一提交或回滚。

2-禁用 自动提交模式 

如果禁用自动提交,事务可以跨越多条语句。
在这种情况下,事务可以用COMMIT和ROLLBACK语句来显式地提交或回滚。


三、MySql中的事务管理


(一)MySQL中事务采用自动提交模式

默认情况下,mysql的事务管理采用的是自动提交模式:DML语句的只要执行一条DML,就开启了事务,并提交这次事务;于是执行结果会立马同步到到数据表中。

0.查询事务管理状态

show variables like 'autocommit'; 
查询结果  —— ON值,代表自动提交。
查询结果 —— OFF值,代表 不是自动提交。 需要手动执行 commit;

1.关闭自动提交事务

(1)使用命令

在自动提交模式开启的情况下,关闭事务的自动提交,手动开启事务。
step1:
    start transaction;     // 该SQL命令含义:关闭了事务的自动提交机制,手动开启事务管理。
step2:
    执行若干条DML语句。
step3:
    commit;                   // 进行事务的提交
start transaction命令的作用:
        在mysql开启了“自动提交”模式的状态下,关闭了事务的自动提交机制,对接下来要执行的DML语句进行手动开启事务管理。
         手动开启事务管理之后,从此mysql不再将DML语句执行出来的结果立即写到表中,而是会记录到事务的日志中。只有开发者手动地进行commit提交成功之后,mysql才会将DML执行结果同步到数据库的表中。

(2)修改变量值

MySql中事务的自动提交模式。该方式只对当前会话有效 (将off值改为on,则为"开启")
set autocommit=off; 
或
set autocommit=0;
或
set session autocommit = off

关闭后自动提交事务之后,则需要commit来执行每一条语句,相当于手动开启了事务管理。

 不过注意的是set autocommit针对的是会话变量,所以这个设置只在此次会话连接中生效。

2.开启自动提交事务

set autocommit=on;
或
set autocommit=1;


(二)事务间的隔离性

1.引发的相关问题

       隔离性是事务管理的四大特性之一,事务之间存在隔离级别。
事务的隔离级别决定了事务之间可见的级别。
理论上隔离级别包括4个:  低 ——高
因事务之间的隔离性引发的相关问题
当多个客户端并发地访问同一个表时,可能出现下面的一致性问题:

(1)脏读取

        一个事务开始读取了某行数据,但是另外一个事务已经更新了此数据但没有能够及时提交,这就出现了脏读取(Dirty Read)。

(2)不可重复读

         在同一个事务中,同一个读操作对同一个数据的前后两次读取产生了不同的结果,这就是不可重复读(Non-repeatable Read)。

(3)幻像读

        幻像读(Phantom Read)是指在同一个事务中以前没有的行,由于其他事务的提交而出现的新行数据。

2.事务的隔离级别

InnoDB 存储引擎支持这四种事务的隔离级别。用以控制事务所做的修改,并将修改通告至其它并发的事务:
       1-read uncommitted   读未提交
        2- read committed       读已提交
        3- repeatable read       可重复读
        4- serializable               串行化(序列化)

(1)read uncommitted

        对方事务A还没有提交,我们当前事务B可以读取到对方未提交的数据。            这里读到的数据,叫“脏数据”或 “脏读 Dirty Read”     
        读未提交存在脏读(Dirty Read)现象:表示读到了脏的数据。
    1) 事务A和和事务B,事务A未提交的数据,事务B可以读取。(允许一个事务可以看到其他事务未提交的修改。)
    2) 这里读取到的数据可以叫做“脏数据”或“脏读 Dirty Read”
    3) 读未提交隔离级别最低,这种级别一般叧在理论上存在,数据库默认隔离级别一般都高于该隔离级别;

(2)read committed

    1) 事务A和事务B,事务A提交的数据,事务B才可读取到;(允许一个事务只能看到其他事务已经提交的修改,未提交的修改是不可见的。)
    2) 该隔离级别高于“读未提交”级别,解决了: 脏读现象。 
    3) 换句话说:对方事务提交之后的数据,当前事务才可读取到。
    4) 该隔离级别可以避免脏数据;
    5) 该隔离级别能够导致“不可重复读取”
    6) Oracle数据库管理系统默认隔离级别为“读已提交”
        (Oracle数据库支持 READ COMMITTED 和 SERIALIZABLE这两种事务隔离级别。)

(3)repeatable read

1) 事务A和事务B,事务A提交之后的数据,事务B还是读取不到。事务B只能读取到,事务B开启事务时刻表中的数据。
2) 事务B是可重复读到数据的。(确保如果在一个事务中执行两次相同的SELECT语句,都能得到相同的结果,不管其他事务是否提交这些修改。 (银行总账))
3) 这种隔离级别高于“读已提交”。
4) 换句话说,对方提交之后的数据,还是读取不到。
5) 这种隔离级别可以避免“脏读和不可重复读”,达到“重复读取”;
6) MySQL数据库管理系统默认隔离级别为:可重复读
7) 这种隔离级别解决了:不可重复读问题,达到了“重复读取”。   
  该隔离级别存在的问题是:读取到的数据是幻象。    

(4)serializable

序列化读/串行化读
 1 ) 事务A和事务B,事务A在操作数据库表中数据的时候,事务B叧能排队等待;(将一个事务与其他事务完 全地隔离。 )
    2) 这种事务隔离级别一般很少使用,吞吐量太低,用户体验不好;效率低。需要事务排队。   
    3) 事务A和事务B不再并发;避免了“幻想读”,每一次读取都是数据库表中真实的记录。    

3.事务隔离级别的作用范围

  • 会话级(session):只对当前会话有效   
  • 全局级(global) :  对所有会话有效

(1.1)查当前会话隔离级别

select @@tx_isolation;
    或
select @@session.tx_isolation;

(1.2)查看全局隔离级别

select @@global.tx_isolation;

(2.1)设置会话隔离级别

set transaction   isolation level   隔离级别类型 ;
    或
set session  transaction   isolation level   隔离级别类型 ;

(2.2)设置全局隔离级别

set global transaction isolation level  隔离级别类型;


4-设置服务器缺省隔离级别

(1)静态设置:修改配置

关闭服务器,修改my.ini配置文件
在 my.ini 文件中的[mysqld]下面添加:
transaction-isolation = 隔离级别类型

(2)动态设置:使用命令

在运行的服务器中通过命令方式,动态设置
set [global/session] transaction isolation level 隔离级别类型;


四、JDBC中的事务


 1-JDBC中的事务默认采用自动提交机制
 即 ,只要执行任意一条DML语句,JDBC则会自动提交一次事务。
    但是在实际的业务当中,通常都是使用N条DML语句共同联合才能完成的。必须保证他们这些DML语句在同一个事物中同时成功或者同时失败。
典型的案例:银行转账业务

2-代码演示 “银行转账”业务
 分析:采用JDBC中默认的自动提交事务机制,会对该业务实现过程中产生的影响。以及给出解决方案。
代码编号:**《Jdbc_Transaction/演示银行转账业务》**
 3-事务+【行级锁】的使用
 1)悲观锁/行级锁
    事务必须排队执行。被选中锁住的数据不允许被并发修改。
    使用方法:select语句后面添加for update即可。如:
使用场景:
     在实际开发中,在查询某张表数据时,为了保障数据的真实性,可以使用【行级锁】 (使用方法:在select语句之后添加for update)将需要的这些数据进行锁住。于是,别的线程开启的事务将无法对这些数据进行修改。           
select ename,job,sal from emp where job = 'manager' for update;
这行语句的意思:工作岗位是manager的这些员工数据被锁定。(行级锁)    当前事务未结束时,这些数据被锁住了。其他的事务无法对这些被锁住的数据进行修改操作。
2)乐观锁
    支持并发,事务也无需排队。但是会多一个版本号的概念。
    多线程并发时,也可以对某条数据进行修改。
 执行原理:

               假设:开启事务1时,读取到数据的版本号是1.1。同时开启事务2,读取到数据的版本号也是1.1。
              事务1先对数据进行了修改,修改之后发现版本号依旧是1.1,和它最初读取到的版本号一致(说明没有其他的事务对数据进行了修改)。于是提交事务修改数据。版本号将会变更为1.2。
               事务2再进行修改数据,修改之后准备提交时,发现数据的版本号是1.2。和它最初读取到的版本号不一致(说明有其他的事务对数据进行了修改)。于是会执行回滚操作。

| ename | job     | sal  | version |
| ----- | ------- | ---- | ------- |
| james | manager | 8000 | 1.1     |

3)代码演示

 结合Debug工具查看效果

涉及到两个程序
1-演示程序开启一个事务.java
2-该程序演示修改被锁定的记录.java

《Jdbc_Transaction/事务+行级锁的使用》



五、MyBatis中的事务




九.其他
锁机制
在事务操作一个表时,如果使用索引来取值,那么会锁定到对应行;
 如果没有使用索引来取值,那么会锁定整个表。锁定之后其他连接无法操作指定行或表。
回滚点

作用:回滚点可以指定rollback回退的位置。

> 比如:现在打了100条命令,发现第81打错了,如果回滚到打了81命令之前一点而不是回滚到开启事务之前就可以节省下很多时间。
创建回滚点:    savepoint   回滚点名;

回滚到回滚点:   rollback to 回滚点名;

注意事项:回滚点在事务管理关闭(rollback或commit之后)之后失效,**不能在事务之外使用回滚点。

单机事务

分布式事务
 即数据库不是一个。是数据库集群。分布方式的。 一个数据库不够
北京的数据库  转到 南京的数据库。
保证这两数据库的事务都成功,才能成功。





posted @ 2021-12-01 11:53  小茅棚  阅读(915)  评论(0编辑  收藏  举报