zoukankan      html  css  js  c++  java
  • 事务(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之后)之后失效,**不能在事务之外使用回滚点。

    单机事务

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





  • 相关阅读:
    Windows下Rails2.0.2最简安装教程
    xmpp with openfire之三 openfire扩展小试 整合现有系统用户
    母亲的电话
    郁闷……
    博弈
    长春下雪了
    监考
    sql把逗号分隔的字符串拆成临时表
    SQL语句使用大全。
    Left Join、Right Join、Inner Join的区别
  • 原文地址:https://www.cnblogs.com/penguin1024/p/15628314.html
Copyright © 2011-2022 走看看