zoukankan      html  css  js  c++  java
  • 08 Transactions

    本章提要
    ------------------------------------------
    事务的特性
    事务控制语句
    ------------------------------------------
    事务, 把数据库从一种一致状态转变为另一种一致状态.
    事务的特性:
        1) 原子性, 事务所有动作要么都发生, 要么都不发生
        2) 一致性, 事务将数据库从一种状态转变为下一种状态
        3) 隔离性, 一个事务在该事务提交之前对其他事务都不可见
        4) 持久性, 事务一旦提交, 其结果永久保留.
    事务的控制语句
        1) commit, 结束你的事务,并将事务提交到online redo log file中(磁盘文件).
        2) rollback, 结束你的事务, 并回滚, 为此要读取存储在回滚段/undo段中的信息, 并把数据库块恢复到事务开始
            之前的状态.
        3) savepoint, 在你的事务中创建一个标记点
        4) rollback to <savepoint>, 可以把事务回滚到标记点, 而不回滚在此标记点之前的任何工作.
        5) set transaction, 允许你设置不同事务属性, 例如隔离级别
    原子性
        演示事务的原子性
      

    create table t2( cnt int);
    
    insert into t2 values(0);
    
    commit;
    
    create table t ( x int check( x>0) );
    
    create trigger t_trigger
    before insert or delete on t for each row
    begin
        if (inserting) then
            update t2 set cnt = cnt + 1;
        else
            update t2 set cnt = cnt - 1;
        end if;
        dbms_output.put_line('I fired and updated' ||
                                sql%rowcount || 'rows');
    end;
    /
    
    -- test
    set serveroutput on
    insert into t values(1);
    
    select * from t2;    -- result : 1
    
    insert into t values(-1);
    
    select * from t2;    -- result 还是 1, trigger中的output 已经正常输出了.
    -- oracle 保证最初的insert 语句是原子性的, 所以, 这个insert 语句应该与
    -- trigger 是在一个事务内, 而由于一个事务内的原子性特性, 外部insert语句
    -- 被回滚了, 所以trigger中的语句也被回滚了, 不过它确实被执行过了.
    8-1


        语句级原子性(实现模拟, 如下:)
        Savepoint statement1;
            insert into t values(1);
        if error then rollback to statement1;
        Savepoint statement2;
            insert into t values(-1);
        if error then rollback to statement2;
        过程级原子性
        oracle 把 PL/SQL 匿名块也当做是语句.
        演示
       

    /*
     * 例 1
     * ---------------------------------------------------------
     */
    create or replace procedure p
    as
    begin
        insert into t values(1);
        insert into t values(-1);
    end;
    /
    
    update t2 set cnt = 0;
    
    delete from t;
    commit;
    select * from t;  -- now rows selected
    select * from t2; -- 0
    
    begin
        p;
    end;
    /
    -- 同样会得到错误提示
    select * from t;  -- 0 rows selected
    -- 可见, 在匿名块中的两条Insert语句是一个事务, 具有原子性
    select * from t2;  -- 0
    
    -- 另一种测试
    /*
     * 例2
     * --------------------------------------------------------------------
     */
    begin
        p;
    exception
        when others then
            dbms_output.put_line('Error!!!!' || sqlerrm );
    end;
    /
    -- 此时, t 和 t2 发生了变化, 因为第1条insert语句发生了作用
    -- 而此时, 由于捕获了异常, 所以系统并没有自动回滚, 而是等带你的操作
    -- 如果你 rollabck, 那么t中没有行 和 t2 是 0, 而如果你commit, 那么
    -- 第一条 insert 会对数据库产生影响. 从这个例子可以看出, p 中的每条语句具有语句级原子性
    -- 而匿名块具有事务级原子性.
    rollback;
    
    /*
     * 例 3
     * ---------------------------------------------------------------------
     */
    begin
        savepoint sp;
        p;
    exception
        when others then
            rollback to sp;
            dbms_output.put_line('Error!!!!' || sqlerrm);
    -- 这样, 结果又是 0 了, 因为显示回滚到前面了
    8-2


        可见, 在一个存储过程内部的匿名块里的语句是具有相同的事务原子性.
        启示: commit 或 rollback 一般情况下不应该出现在pl/sql块中, 只有pl/sql存储过程的调用者才知道事务何时完成.
        匿名块内部的每条语句, 具有语句级原子性, 参看上边例子的后半段
        ddl 语句具有自动提交, 并且具有事务级原子性, 比如:
        演示
       

    insert into t values(9);
    
    -- 注意, 此处并没有commit
    
    create table xxx(x int);
    
    -- 此时登陆别的session, 可以看到 9 这条记录已经插入了, 换句话说, 上边的
    -- insert 语句也已经提交了
    -- ddl 语句会自动提交, 事务级别的提交
    8-3


    完整性约束 和 事务
        默认情况下, 完整性约束会在整个 sql 语句得到处理之后才进行检查.
        immediate 约束
            如果一个pl/sql存储过程中有很多sql语句,那么在每条sql语句执行之后都会立即验证完整性约束, 而不是在这个
            存储过程完成后才检查它.
        deferrable 约束 和 级联更新 (一般不推荐, 还是使用默认的immediate比较好)
    不好的事务习惯
        1) 在循环中提交
        2) 使用自动提交
    分布式事务
        要利用远端数据库, dblink
        update local_table set x = 5;
        update remote_table@another_database set y = 10;
        commit;
        限制:
        1) 不能在数据库链接上发出commit, 例如 commit@remote_site
        2) 不能在数据库链接上完成ddl, ddl 会提交, 除了发起事务的站点外, 其他站点不能提交.
            (所谓发起事务站点, 比如有1,2,3三个站点, 而1是发出站点, 那么1会问2,3准备好提交没有, 如果准备好了, 1可以
             提交, 而此时网络中断, 那么2,3就会被挂起, 等待站点1的提交, 所以可见修改远端db还是很危险的)
        3) 不能在数据库链接上发出 savepoint

            

  • 相关阅读:
    jar包和war包的区别:
    tail
    redis
    查看Linux操作系统版本
    CentOS 7.0 systemd代替service
    周刊(三月最后一期)
    周刊第四期
    周刊第三期
    周刊第二期
    周刊(第一期)
  • 原文地址:https://www.cnblogs.com/moveofgod/p/3876478.html
Copyright © 2011-2022 走看看