zoukankan      html  css  js  c++  java
  • Oracle 事务 锁

    一、 事务

    是一系列的数据库操作,是数据库应用的基本逻辑单位以及并发控制的基本单位。所谓的事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。

    要将有组语句作为事务考虑,就需要通过ACID测试,即原子性,一致性,隔离性和持久性。

    1. 事务性质

    原子性、一致性或可串性、隔离性、持久性

    ▶ 原子性Atomic:即不可分割性,事务要么全部被执行,要么就全部不被执行。

    ▶ 一致性或可串性Consistency:事务的执行使得数据库从一种正确状态转换成另一种正确状态。

    ▶隔离性Isolation:事务允许多个用户对同一个数据进行并发访问,而不破坏数据的正确性和完整性。同时,并行事务的修改必须与其他并行事务的修改相互独立。在事务正确提交之前,不允许把该事务对数据的任何改变提供给任何其他事

    ▶ 持久性Durability:事务正确提交后,其结果将永久保存在数据库中,即使在事务提交后有了其他故障,事务的处理结果也会得到保存。

    2. 事务语句

    ▶ 开始事物:begin transaction

    ▶ 提交事物:commit transaction

    ▶ 回滚事务:rollback transaction

    用户在事务(transaction)内可以声明(declare)被称为保存点(savepoint)的标记。保存点将一个大事务划分为较小的片断。

    ▶ save transaction 保存点名称 --自定义保存点的名称和位置

    ▶ rollback transaction 保存点名称 --回滚到自定义的保存点

    用户可以使用保存点(savepoint)在事务(transaction)内的任意位置作标记。之后用户在对事务进行回滚操作(rolling back)时,就可以选择从当前执行位置回滚到事务内的任意一个保存点。

    3. 事务使用

    ① 创建一张表以作示例

     1 -- 建表
     2   
     3 create table student(
     4 id varchar2(4),
     5 name varchar2(100), --姓名
     6 sex varchar2(1),  --性别 1 男  2 女  0 未知
     7 score integer default 0
     8 );
     9 
    10 select * from student;
    11 
    12 -- 插入数据
    13 
    14 insert into student (id, name, sex)values ('0001', '大王', '2'); 
    15 insert into student (id, name, sex)values ('0002', '刘一', '1'); 
    16 insert into student (id, name, sex)values ('0003', '陈二', '2');
    17 insert into student (id, name, sex)values ('0004', '张三', '0');
    18 insert into student (id, name, sex)values ('0005', '李四', '1');
    19 insert into student (id, name, sex)values ('0006', '王五', '0');
    20 insert into student (id, name, sex)values ('0007', '赵六', '1');
    21 insert into student (id, name, sex)values ('0008', '孙七', '2');
    22 insert into student (id, name, sex)values ('0009', '周八', '2');
    23 insert into student (id, name, sex)values ('0010', '吴九', '1');
    24 insert into student (id, name, sex)values ('0011', '郑十', '1');
    25 commit;
    View Code

     

    ② 以上表为基础的一个事务示例

     1 CREATE OR REPLACE PROCEDURE p_transaction_test(p_error_no   out int, --错误编号
     2                                                p_error_info out varchar2 --错误信息
     3                                                ) as
     4 
     5 begin
     6   /*
     7   wangrui 事务commit测试
     8   ①处commit:第一条更新成功,第二条更新失败,回滚;由于第二条失败时,第一条已经提交。
     9   ②处commit;第一条更新失败,第二条更新失败;由于没有提交,第二条失败,语句还没到②commit处已经回滚,两条均未更新。
    10   ③处commit;在此,跟②处的结果是一样的,是对事物最后的提交。
    11   
    12   Ⅰ处savepoint会使事务回滚到A为止,如果此时①处有commit,会导致Ⅰ保存点失效,报错,但是事务会回滚到①提交处
    13   Ⅱ处savepoint会使事务回滚到B位置
    14   注意:roll rollback to savepoint之后需要commit。否则事务回到savepoint之后,savepoint之前的获得的锁并未被释放
    15   
    16   一般使用commit提交即可,不使用savepoint。
    17   ……
    18   */
    19 
    20   p_error_no   := 0;
    21   p_error_info := ' ';
    22   begin
    23   
    24     --
    25     --savepoint save1;
    26   
    27     update student set sex = '1' where id = '0001'; --一条会成功更新的SQL语句
    28   
    29     --
    30     commit;
    31     --
    32     --savepoint save1;
    33   
    34     update student set sex = '11' where id = '0002'; --一条语句超长,更新失败的SQL语句
    35   
    36     --
    37     --commit;
    38   
    39   exception
    40     when others then
    41       rollback --to savepoint save1;commit
    42       ;
    43       p_error_no   := -1;
    44       p_error_info := '失败:' || SQLERRM;
    45       return;
    46   end;
    47 
    48   --③提交事务
    49   commit;
    50 
    51 end;
    View Code

    二、 事务的隔离级别

    1. 事务并发访问时存在的问题

    ▶ 脏读:如果一个事务读取的记录是另一个未完成事务的一部分,就发生了脏读。如果第一个事务正常完成,就没有什么问题。但是,如果前一个事务回滚了,那将从数据库从未发生的事务中获取了信息。

    ▶ 不可重复读取:很容易将不可重复性读取和脏读混淆。如果一个事务中两次读取记录,而另一个事务在这期间改变了数据,就会发生不可重复性读取。

    ▶ 幻读:如果一个事务读取一个记录,返回结果集,此时,另一个事务插入一条记录并提交

    2. 事务的隔离级别 

    3. ORACLE支持的事务隔离级别

    只读模式:只读事务只能看到事务执行前就已经提交的数据,且事务中不能执执行 INSERT , UPDATE ,及 DELETE 语句

    已提交读取:Oracle 默认使用的事务隔离级别。事务内执行的查询只能看到查询执行前(而非事务开始前)就已经提交的数据。Oracle 的查询永远不会读取脏数据(未提交的数据)。

    Oracle 不会阻止一个事务修改另一事务中的查询正在访问的数据,因此在一个事务内的两个查询的执行间歇期间,数据有可能被其他事务修改。举例来说,如果一个事务内同一查询执行两次,可能会遇到不可重复读取或不存在读取的现象。 

    串行化:串行化隔离的事务只能看到事务执行前就已经提交的数据,以及事务内 INSERT , UPDATE ,及 DELETE 语句对数据的修改。串行化隔离的事务不会出现不可重复读取或不存在读取的现象。

    1 --只允许select操作,不允许有任何修改数据库(包括 insert、update、delete)中数据的操作语句,允许创建语句create
    2 set transaction read only;
    3 --默认设置,表示在事务中可以有访问语句、修改语句(包括insert、delete、update),允许创建语句create,同read committed
    4 set transaction read  write; 
    5 --同read write; 
    6 set transaction isolation level read committed;
    7 --serialzable可以执行DML操作
    8 set transaction isolation level serializable;

    3.1 read only 

    新建一个事务如下

    set transaction read only;

    ① 执行

    select * from student;

     

    ② 执行

    update student set score = 100 where id = '0002';

     

    3.2 read write

    ① 新建一个事务如下

    set transaction read write;

    ② 执行

    select * from student;

     

    ② 执行

    update student set score = 100 where id = '0002';

    select * from  student;

     

    结论:允许读写(包括insert、delete、update)

    3.3 isolation level read committed(可幻读和重复读)

    新建两个事务如下

    ① 新建事务T1

    set transaction read  write;

    select * from student;

     

    ② 新建事务T2

    set transaction isolation level read committed;

    select * from student;

     

    ③ 在事务T1中修改ID=002的记录并commit;

    update student set score = 100 where id = '0002';--未commit状态下在T2事务中并未看到更新

    commit;

    ④ 在事务T2 中查看commit后的结果如下(commit状态下看到更新结果)

    select * from student;

     

    ⑤ 在事务T1中删除ID=003的记录, 并commit;

    delete from student where id='0003';--未commit时在T2中还可以看到该条记录

    commit;

    ⑥ 在事务T2 中查看commit后的结果如下(commit之后看不到该条记录)

     

    ⑦ 在事务T1中插入ID=0006的记录,并commit;

    insert into student (id, name, sex)values ('0006', '王五', '0');--未commit之前在T2中看不到该条记录

    commit;

    ⑧ 在事务T2 中查看commit后的结果如下(commit之后看到该条记录已经添加)

     

    结论:①-⑥ 说明事务隔离级别为isolation level read committed时,允许可重复读。

    ⑦-⑧ 说明事务隔离级别为 isolation level read committed, 允许幻想读

    3.4  isolation level  serializable

    创建了两个事务如下

    ① 新建事务T1

    set transaction read write;

    select * from student;

     

    ② 新建事务T2

    set transaction isolation level serializable;

    select * from student;

     

    ③ 在事务T1中修改ID=002的记录并commit;

    update student set score = 100 where id = '0002';--未commit状态下在T2事务中并未看到更新

    commit;

    ④ 在在事务T2 中查看commit后的结果如下(commit状态下也没有看到更新)

     

    ⑤ 在事务T1中删除ID=003的记录, 并commit;

    delete  from student where id='0003';--未commit时在T2中还可以看到该条记录

    commit;

    ⑥ 在事务T2 中查看commit后的结果如下(commit状态下依然可以看到该条记录)

     

    ⑦ 在事务T1中插入ID=0006的记录,并commit;

    insert into student (id, name, sex)values ('0006', '王五', '0');--未commit之前在T2中看不到该条记录

    commit;

    ⑧ 在事务T2 中查看commit后的结果如下(commit状态下依然看不到该条记录)

     

    结论:①-⑥ 说明事务隔离级别为isolation level  serializable时,不支持可重复读。

    ⑦-⑧ 说明事务隔离级别为 isolation level  serializable 时, 不支持幻想读

    三、 锁

    锁是一种机制,多个事务同时访问一个数据库对象时,该机制可以实现对并发的控制

    ORCLE 多事务并发可能存在的问题

    丢失更新:丢失更新发生在一个更新成功写入数据库后,而又意外地被另一个事务重写时。这是怎么发生的呢?如果有两个事务读取整个记录,然后其中一个向记录写入了更新信息,而另一个事务也向该记录写入更新信息,这是就会出现丢失更新。

    1. oracle 中锁的类型

    ① DML锁(data locks,数据锁),用于保护数据的完整性。oracle自动的施加和释放。

    ② DDL锁(dictionary locks,字典锁),用于保护数据库对象的结构,如表、索引等的结构定义. 事务开始时施加,使用Commit后者Rollback被释放。

    ③ 内部锁和闩(internal locks and latches),保护数据库的内部结构。由oracle自己管理以保护内部数据库结构。

    2. 锁的粒度

    ① 行级锁(TX):阻止该行上的DML操作,直到Commit或者Rollback。

    ② 表级锁(TM):

    ③ 数据库级锁::

    eg:将数据库锁定为只读模式 alter database open read only;

    eg:将数据库设置为限制模式(导入导出数据库时使用):alter system enable restricted session;

    3. oracle 中锁的模式

    3.1 锁的模式

    3.2 锁的兼容性

    3.3 锁表语句

    1 lock table student in row share mode;
    2 lock table student in row exclusive mode;  --用于行的修改
    3 lock table student in share mode;   --阻止其他DML操作
    4 lock table student in share row exclusive mode;    --阻止其他事务操作
    5 lock table student in exclusive mode;  --独立访问使用

     

    4. 锁的查看

    ① 创建一个事务,执行

    update student set score=888 WHERE ID='0004';

    ② 执行

    select * from v$locked_object; --object_id为表名

     

    ③ 执行

    1 select s.sid, s.serial#, s.username, s.schemaname, s.osuser, s.process, s.machine, s.terminal, s.logon_time, l.type, l.lmode
    2   from v$session s, v$lock l
    3  where s.sid = l.sid
    4    and s.sid= '3231'
    5  order by sid;

     

    如图,DML操作,获得一个TM锁,还获得一个TX锁,还有一个……,故而其他事务可以对该表的其他行进行编辑,但是0004行不允许其他事务有任何操作。

    我们可以认为Oracle 6种MODE的锁,根据锁定的对象不同而有不同的名称,如6号的X锁,既可以是用于锁表的TM锁,也可以是TX锁,也可以是DDL锁。

    四、 死锁

    如果一个锁由于另一个锁占有资源而不能完成应该做的工作,就会导致死锁;反之亦然。

    1 死锁产生条件

    ① Mutual exclusion(互斥):资源不能被共享,只能由一个进程使用。

    ② Hold and wait(请求并保持):已经得到资源的进程可以再次申请新的资源。

    ③ No pre-emption(不可剥夺):已经分配的资源不能从相应的进程中被强制地剥夺。

    ④ Circular wait(循环等待条件):系统中若干进程组成环路,该环路中每个进程都在等待相邻进程正占用的资源。

    2. 死锁模拟

    ① 查看表内容

    select * from student;

     

    ② 新建一个事务T1,执行

    update student set score=111 WHERE ID='0002';

    未提交状态下提示一行被更新

    ③ 新建一个事务T2,执行

    update student set score=333 WHERE ID='0003';

    未提交状态下提示一行被更新

    ④ 在事务T1中执行

    update student set score=222 WHERE ID='0003';

    显示“正在执行……”表示等待中

    ⑤ 在事务T2中执行

    update student set score=444 WHERE ID='0002';

     

    此时,事务T2显示“正在执行……”表示等待,T1事务是提交或者回滚状态

    ⑥ commit 事务T1

     

    显示事务T1只更新了0002,0003并未更新成功。

    ⑦ commit 事务T2,然后查看结果

     

    显示事务T2更新了0002,0003,相当于事务T1的结果丢失了。

    3. 解决死锁冲突

    在没有执行⑥⑦操作的情况下

    新⑥ 执行

    select sid,serial#,username from v$session where sid in (select blocking_session from v$session);

    alter system kill session '1522,42255';

    第一行代码找到sid='1522,serial#=42255'

    第二行代码杀掉这个堵塞的进程

    新⑦ 此时,提交进程事务T1,结果显示。

     

    新⑧ 提交事务T2,显示提交成功,查看结果

     

    显示事务T2更新了0002,0003,相当于事务T1的结果丢失了。

    结论:oracle在死锁情况下,会保存后面一个事务的更新结果。

    4. 事务和死锁预防总结

    ① 避免应用不运行长事务。

    ② 经常提交以避免长时间锁定行。

    ③ 避免使用LOCK命令锁定表。

    ⑤ 在高峰期间执行DDL操作,在非高峰期间执行长时间运行的查询或事务。

  • 相关阅读:
    ACMer第7天Falling Ants
    贪心初步-FatMouse' Trade
    贪心初步-A
    ACM集训第二天
    asp.net中遍历套用母版页的页面的控件
    a 标签中调用js的几种方法
    笔记
    html控件和web控件
    ASP.NET中GUID类
    (转)常见邮件服务器(接收服务器和发送邮件服务器)地址
  • 原文地址:https://www.cnblogs.com/wangrui1587165/p/9319417.html
Copyright © 2011-2022 走看看