首页 数据库 mysql教程 Oracle锁定策略的总结

Oracle锁定策略的总结

Jun 07, 2016 pm 03:26 PM
oracle 总结 策略 锁定

以下是对Oracle锁定策略的总结: · Oracle只在修改时才对数据加行级锁。正常情况下不会升级到块级锁或表级锁。 · 如果只是读数据,Oracle 绝不会对数据锁定。不会因为简单的读操作在数据行上锁定。 · 写入器(writer)不会阻塞读取器(reader)。换种说法

以下是对Oracle锁定策略的总结:
· Oracle只在修改时才对数据加行级锁。正常情况下不会升级到块级锁或表级锁。
· 如果只是读数据,Oracle 绝不会对数据锁定。不会因为简单的读操作在数据行上锁定。
· 写入器(writer)不会阻塞读取器(reader)。换种说法:读(read)不会被写(write)阻塞。这一点几乎与其他所有数据库都不一样。在其他数据库中,读往往会被写阻塞。
· 写入器想写某行数据,但另一个写入器已经锁定了这行数据,此时该写入器才会被阻塞。读取器绝对不会阻塞写入器。

需要了解以下内容:
· 事务是每个数据库的核心。
· 必要时才提交事务。事务的大小只应该根据业务逻辑来定。
· 只要需要,就应该尽可能长时间地保持对数据所加的锁。锁不是稀有资源。
· 在Oracle中,行级锁没有相关的开销,对1 000 000行锁定所需的资源数与对1行锁定所需的资源数完全相同,这是一个固定的常量。
· 不要以为锁升级“对系统更好”(例如,使用表锁而不是行锁)。在Oracle中,锁升级(lock escalate)对系统没有任何好处,不会节省任何资源。
· 可以同时得到并发性和一致性。

Oracle中主要有3类锁,具体是:
· DML锁(DML lock):DML代表数据操纵语言(Data Manipulation Language)。如SELECT、INSERT、UPDATE、MERGE和DELETE语句。DML锁机制允许并发执行数据修改。
· DDL锁(DDL lock):DDL代表数据定义语言(Data Definition Language),如CREATE和ALTER语句等。DDL锁可以保护对象结构定义。
· 内部锁和闩:Oracle 使用这些锁来保护其内部数据结构。
具体介绍-〉http://www.linuxidc.com/Linux/2012-01/51267.htm


Oracle中的锁定一行的过程如下:
(1) 找到想锁定的那一行的地址。
(2) 到达那一行。
(3) 锁定这一行(如果这一行已经锁定,则等待锁住它的事务结束,除非使用了NOWAIT选项)。

仅此而已。由于闩为数据的一个属性,Oracle不需要传统的锁管理器。事务只是找到数据,如果数据还没有被锁定,则对其锁定。
需要注意,找到数据时,它可能看上去被锁住了,但实际上并非如此。在Oracle中对数据行锁定时,行指向事务ID的一个副本,事务ID存储在包含数据的块中,释放锁时,事务ID却会保留下来。这个事务ID是事务所独有的,表示了回滚段号、槽和序列号。事务ID留在包含数据行的块上,可以告诉其他会话:你“拥有”这个数据(并非块上的所有数据都是你的,只是你修改的那一行“归你所有”)。
另一个会话到来时,它会看到锁ID,由于锁ID表示一个事务,所以可以很快地查看持有这个锁的事务是否还是活动的。如果锁不活动,则允许会话访问这个数据。如果锁还是活动的,会话就会要求一旦释放锁就得到通知。因此,这就有了一个排队机制:请求锁的会话会排队,等待目前拥有锁的事务执行,然后得到数据。

如何用数据本身来管理锁定和事务信息?这是块开销的一部分。数据库块的最前面有一个“开销”空间(overhead),这里会存放该块的一个事务表,对于锁定了该块中某些数据的各个“实际”事务,在这个事务表中都有一个相应的条目。这个结构的大小由创建对象时CREATE语句上的两个物理属性参数决定:
· INITRANS:这个结构初始的预分配大小。对于索引和表,这个大小默认为2
· MAXTRANS:这个结构可以扩大到的最大大小。Oracle 10之后中,这个设置已经废弃了,MAXTRANS 总是255。
默认情况下,每个块最开始都有两个事务槽。一个块上同时的活动事务数受MAXTRANS值的约束,另外也受块上空间可用性的限制。
如果没有足够的空间来扩大这个结构,块上就无法得到255个并发事务,这时候就会等待,产生阻塞。
因此在频繁修改的表上就可能要增加INITRANS 设置,或者更常见的是,对于频繁修改的索引也可能需要这么做,因为索引块中的行一般比表中的行多。你可能需要增加PCTFREE或INITRANS,从而在块上提前预留足够的空间以应付可能的并发事务数。尤其是,如果你预料到块开始时几乎是满的(这说明块上没有空间来动态扩缩事务结构),则更需要增加PCTFREE或INITRANS。

可以做一个测试:
创建1个表,里面包含很多行,而且都存入一个块,使这个块一开始就比较满,只留下很少的空间,会限制事务表的增长。

[sql]

  1. create table t   
  2. ( x int primary key,   
  3.   y varchar2(4000)  
  4. );  
  5.   
  6. insert into t (x,y)  
  7. select rownum, rpad('*',148,'*')   
  8.   from dual  
  9. connect by level 

可以通过以下语句,可以看到表中有46行,都在同一个块上。之所以选择148个字符,是因为再多一个字符,就需要2个块才能放下这46行。
如果你的Oracle当前的块大小是8K,就能得到同下面的测试相同的结果。

[sql]

  1.   select length(y),   
  2.        dbms_rowid.rowid_block_number(rowid) blk,   
  3.        count(*), min(x), max(x)  
  4.   from t  
  5.   group by length(y), dbms_rowid.rowid_block_number(rowid);  
  6.     
  7.  LENGTH(Y)        BLK   COUNT(*)     MIN(X)     MAX(X)  
  8. ---------- ---------- ---------- ---------- ----------   
  9.        148        291         46          1         46  

     
下面通过让多个事务通过select ... for update nowait语句同时锁定这个块上的数据,第一个事务锁定第一行,第二个事务锁定第二行,以此类推。
如果需要等待,则会产生1个ORA-54 resource busy的错误,这说明已经用完了这个块上的事务表。
通过使用自治事务(AUTONOMOUS_TRANSACTION),可以使用一个会话就完成这个测试,不需要运行大量SQL*PLUS会话。

[sql]

  1. create or replace procedure do_update( p_n in number )  
  2. as  
  3.     pragma autonomous_transaction;  
  4.     l_rec t%rowtype;  
  5.     resource_busy exception;  
  6.     pragma exception_init( resource_busy, -54 );  
  7. begin  
  8.     select *   
  9.       into l_rec   
  10.       from t   
  11.      where x = p_n   
  12.        for update NOWAIT;  
  13.   
  14.     do_update( p_n+1 );  
  15.     commit;  
  16. exception  
  17. when resource_busy   
  18. then  
  19.     dbms_output.put_line( 'locked out trying to select row ' || p_n );  
  20.     commit;  
  21. when no_data_found  
  22. then  
  23.     dbms_output.put_line( 'we finished - no problems' );  
  24.     commit;  
  25. end;  
  26. /  

执行这个过程,可以得到如下结果:

[sql]

  1. tony@ORA11GR2> exec do_update(1);  
  2. locked out trying to select row 38  
  3.   
  4. PL/SQL 过程已成功完成。  

阻塞
有5条常见的DML语句会产生阻塞::INSERT、UPDATE、DELETE、MERGE 和SELECT FOR UPDATE。
对于一个阻塞的SELECT FOR UPDATE,只需增加NOWAIT 子句,它就不会阻塞,会报告一个ORA-00054:Resource Busy的错误。

1)阻塞的Insert
最常见的情况是,有一个带主键的表,或者表上有惟一的约束,但有两个会话试图用同样的值插入一行。如果是这样,其中一个会话就会阻塞。
此外,如果使用外键,对子表的插入可能会阻塞,因为它所依赖的父表正在创建或删除。

2)阻塞的Update 和Delete
如果有未提交的事务在这一行上执行了Update或者Delete操作,那么就会产生阻塞。

3)阻塞的Merge
Merge只是Insert和UPDATE(10g之后还有Delete)的组合。因此结果同上。

死锁
如果两个会话,每个会话都持有另一个会话想要的资源,此时就会出现死锁(deadlock)。
根据经验,导致死锁的头号原因是外键未加索引(第二号原因是表上的位图索引遭到并发更新)。
在以下两种情况下,Oracle在修改父表后会对子表加一个全表锁:
· 如果更新了父表的主键,由于外键上没有索引,所以子表会被锁住。
· 如果删除了父表中的一行,由于外键上没有索引,整个子表也会被锁住。
在 Oracle9i 及以上版本中,这些全表锁都是短期的,这意味着它们仅在DML 操作期间存在,而不是在整个事务期间都存在。即便如此,这些全表锁还是会导致很严重的锁定问题。

除了全表锁外,在下面情况下,未加索引的外键还可能带来性能问题。
· 如果有ON DELETE CASCADE,而且没有对子表加索引
例如,EMP是DEPT的子表,DELETE DEPTNO = 10 应该CASCADE(级联)至EMP[4]。如果EMP中的DEPTNO没有索引,那么删除DEPT表中的每一行时都会对EMP做一个全表扫描。这个全表扫描可能是不必要的,而且如果从父表删除多行,父表中每删除一行就要扫描一次子表。
· 从父表查询子表
例如利用DEPTNO查询EMP表是相当常见的。如果频繁地运行以下查询,没有索引会使查询速度变慢:
select * from dept, emp where emp.deptno = dept.deptno and dept.deptno = :X;

一般来说,仅当满足以下条件时不需要给外键加索引:
· 没有从父表删除行。
· 没有更新父表的惟一键/主键值(当心工具有时会无意地更新主键!)。
· 没有从父表联结子表

可以利用下面的脚本来查找没有加索引的外键。

[sql]

  1. column columns format a30 word_wrapped  
  2. column tablename format a15 word_wrapped  
  3. column constraint_name format a15 word_wrapped  
  4.   
  5. select table_name, constraint_name,  
  6.      cname1 || nvl2(cname2,','||cname2,null) ||  
  7.      nvl2(cname3,','||cname3,null) || nvl2(cname4,','||cname4,null) ||  
  8.      nvl2(cname5,','||cname5,null) || nvl2(cname6,','||cname6,null) ||  
  9.      nvl2(cname7,','||cname7,null) || nvl2(cname8,','||cname8,null)  
  10.             columns  
  11.   from ( select b.table_name,  
  12.                 b.constraint_name,  
  13.                 max(decode( position, 1, column_name, null )) cname1,  
  14.                 max(decode( position, 2, column_name, null )) cname2,  
  15.                 max(decode( position, 3, column_name, null )) cname3,  
  16.                 max(decode( position, 4, column_name, null )) cname4,  
  17.                 max(decode( position, 5, column_name, null )) cname5,  
  18.                 max(decode( position, 6, column_name, null )) cname6,  
  19.                 max(decode( position, 7, column_name, null )) cname7,  
  20.                 max(decode( position, 8, column_name, null )) cname8,  
  21.                 count(*) col_cnt  
  22.            from (select substr(table_name,1,30) table_name,  
  23.                         substr(constraint_name,1,30) constraint_name,  
  24.                         substr(column_name,1,30) column_name,  
  25.                         position  
  26.                    from user_cons_columns ) a,  
  27.                 user_constraints b  
  28.           where a.constraint_name = b.constraint_name  
  29.             and b.constraint_type = 'R'  
  30.           group by b.table_name, b.constraint_name  
  31.        ) cons  
  32.  where col_cnt > ALL  
  33.          ( select count(*)  
  34.              from user_ind_columns i  
  35.             where i.table_name = cons.table_name  
  36.               and i.column_name in (cname1, cname2, cname3, cname4,  
  37.                                     cname5, cname6, cname7, cname8 )  
  38.               and i.column_position 
  39.             group by i.index_name  
  40.          )  
  41. /  

有一个办法可以快速定位导致子表被锁住的代码段。可以发出以下命令:
ALTER TABLE DISABLE TABLE LOCK;
现在,对父表的可能导致子表被锁住的任何UPDATE或DELETE都会接收到以下错误:
ERROR at line 1:
ORA-00069: cannot acquire lock -- table locks disabled for
这有助于跟踪到有问题的代码段,(比如,你认为并没有对父表的主键执行UPDATE或DELETE)。

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

oracle如何查表空间大小 oracle如何查表空间大小 Apr 11, 2025 pm 08:15 PM

要查询 Oracle 表空间大小,请遵循以下步骤:确定表空间名称,方法是运行查询:SELECT tablespace_name FROM dba_tablespaces;查询表空间大小,方法是运行查询:SELECT sum(bytes) AS total_size, sum(bytes_free) AS available_space, sum(bytes) - sum(bytes_free) AS used_space FROM dba_data_files WHERE tablespace_

oracle视图如何加密 oracle视图如何加密 Apr 11, 2025 pm 08:30 PM

Oracle 视图加密允许您加密视图中的数据,从而增强敏感信息安全性。步骤包括:1) 创建主加密密钥 (MEk);2) 创建加密视图,指定要加密的视图和 MEk;3) 授权用户访问加密视图。加密视图工作原理:当用户查询加密视图时,Oracle 使用 MEk 解密数据,确保只有授权用户可以访问可读数据。

oracle如何查看实例名 oracle如何查看实例名 Apr 11, 2025 pm 08:18 PM

在 Oracle 中查看实例名的方法有三种:命令行中使用 "sqlplus" 和 "select instance_name from v$instance;" 命令。在 SQL*Plus 中使用 "show instance_name;" 命令。通过操作系统的任务管理器、Oracle Enterprise Manager 或检查环境变量 (Linux 上的 ORACLE_SID)。

Oracle安装失败如何卸载 Oracle安装失败如何卸载 Apr 11, 2025 pm 08:24 PM

Oracle 安装失败的卸载方法:关闭 Oracle 服务,删除 Oracle 程序文件和注册表项,卸载 Oracle 环境变量,重新启动计算机。若卸载失败,可使用 Oracle 通用卸载工具手动卸载。

oracle游标关闭怎么解决 oracle游标关闭怎么解决 Apr 11, 2025 pm 10:18 PM

解决 Oracle 游标关闭问题的方法包括:使用 CLOSE 语句显式关闭游标。在 FOR UPDATE 子句中声明游标,使其在作用域结束后自动关闭。在 USING 子句中声明游标,使其在关联的 PL/SQL 变量关闭时自动关闭。使用异常处理确保在任何异常情况下关闭游标。使用连接池自动关闭游标。禁用自动提交,延迟游标关闭。

oracle无效数字如何排查 oracle无效数字如何排查 Apr 11, 2025 pm 08:27 PM

Oracle 无效数字错误可能由数据类型不匹配、数字溢出、数据转换错误或数据损坏引起。排查步骤包括检查数据类型、检测数字溢出、检查数据转换、排查数据损坏,并探索其他可能的解决方案,如配置 NLS_NUMERIC_CHARACTERS 参数和启用数据验证日志记录。

oracle动态sql怎么创建 oracle动态sql怎么创建 Apr 12, 2025 am 06:06 AM

可以通过使用 Oracle 的动态 SQL 来根据运行时输入创建和执行 SQL 语句。步骤包括:准备一个空字符串变量来存储动态生成的 SQL 语句。使用 EXECUTE IMMEDIATE 或 PREPARE 语句编译和执行动态 SQL 语句。使用 bind 变量传递用户输入或其他动态值给动态 SQL。使用 EXECUTE IMMEDIATE 或 EXECUTE 执行动态 SQL 语句。

oracle怎么循环创建游标 oracle怎么循环创建游标 Apr 12, 2025 am 06:18 AM

Oracle 中,FOR LOOP 循环可动态创建游标, 步骤为:1. 定义游标类型;2. 创建循环;3. 动态创建游标;4. 执行游标;5. 关闭游标。示例:可循环创建游标,显示前 10 名员工姓名和工资。

See all articles