全面解析oracle中的锁机制2

简介: 默认情况下 T@ora>create table t1 as select * from t ; Table created. Elapsed: 00:00:00.

默认情况下
T@ora>create table t1 as select * from t ;

Table created.

Elapsed: 00:00:00.07
T@ora>select rowid,ora_rowscn,a from t1;

ROWID                         ORA_ROWSCN          A
------------------                      ----------             ----------
AAARN5AAGAAAXHcAAA   45387504          1
AAARN5AAGAAAXHcAAB   45387504          2
AAARN5AAGAAAXHcAAC   45387504          3
AAARN5AAGAAAXHcAAD   45387504          4
AAARN5AAGAAAXHcAAE   45387504          5
AAARN5AAGAAAXHcAAF   45387504          6
AAARN5AAGAAAXHcAAG   45387504          7
AAARN5AAGAAAXHcAAH   45387504          8
AAARN5AAGAAAXHcAAI   45387504          9
AAARN5AAGAAAXHcAAJ   45387504         10

10 rows selected.

Elapsed: 00:00:00.20
T@ora>update t1 set a = 0 where a <3;

2 rows updated.

Elapsed: 00:00:00.01
T@ora>commit;

Commit complete.

Elapsed: 00:00:00.00
T@ora>select rowid,ora_rowscn,a from t1;

ROWID                           ORA_ROWSCN          A
------------------                          ----------          ----------
AAARN5AAGAAAXHcAAA   45387522          0
AAARN5AAGAAAXHcAAB   45387522          0
AAARN5AAGAAAXHcAAC   45387522          3 《----这里开始后面的记录都没做修改,但是ora_rowscn却改变了
AAARN5AAGAAAXHcAAD   45387522          4
AAARN5AAGAAAXHcAAE   45387522          5
AAARN5AAGAAAXHcAAF   45387522          6
AAARN5AAGAAAXHcAAG   45387522          7
AAARN5AAGAAAXHcAAH   45387522          8
AAARN5AAGAAAXHcAAI   45387522          9
AAARN5AAGAAAXHcAAJ   45387522         10

10 rows selected.
增加ROWDEPENDENCIES 
T@ora>create table t2 ROWDEPENDENCIES  as select * from t ;

Table created.

Elapsed: 00:00:00.06
T@ora>select rowid,ora_rowscn,a from t2;

ROWID                              ORA_ROWSCN          A
------------------                      ----------               ----------
AAARN6AAGAAAXHkAAA   45387561          1
AAARN6AAGAAAXHkAAB   45387561          2
AAARN6AAGAAAXHkAAC   45387561          3
AAARN6AAGAAAXHkAAD   45387561          4
AAARN6AAGAAAXHkAAE   45387561          5
AAARN6AAGAAAXHkAAF   45387561          6
AAARN6AAGAAAXHkAAG   45387561          7
AAARN6AAGAAAXHkAAH   45387561          8
AAARN6AAGAAAXHkAAI   45387561          9
AAARN6AAGAAAXHkAAJ   45387561         10

10 rows selected.

Elapsed: 00:00:00.03
T@ora>update t2 set a = 0 where a <3;

2 rows updated.

Elapsed: 00:00:00.01
T@ora>commit;

Commit complete.

Elapsed: 00:00:00.00
T@ora>select rowid,ora_rowscn,a from t2;

ROWID                          ORA_ROWSCN          A
------------------                         ----------         ----------
AAARN6AAGAAAXHkAAA   45387578          0
AAARN6AAGAAAXHkAAB   45387578          0
AAARN6AAGAAAXHkAAC   45387561          3 《--没有修改的scn就不会改变
AAARN6AAGAAAXHkAAD   45387561          4
AAARN6AAGAAAXHkAAE   45387561          5
AAARN6AAGAAAXHkAAF   45387561          6
AAARN6AAGAAAXHkAAG   45387561          7
AAARN6AAGAAAXHkAAH   45387561          8
AAARN6AAGAAAXHkAAI   45387561          9
AAARN6AAGAAAXHkAAJ   45387561         10

存储空间的影响,dump出来就可以看到2个存储结构不一样
普通表
tab 0, row 0, @0x1f1e
tl: 8 fb: --H-FL-- lb: 0x2  cc: 2
col  0: [ 1]  80
col  1: [ 2]  c1 02


2个number类型
tab 0, row 0, @0x1edc
tl: 14 fb: --H-FL-- lb: 0x2  cc: 2
dscn 0x0000.02b48f29
col  0: [ 1]  80
col  1: [ 2]  c1 02

多出6个字节记录dscn。


将SCN 转换为墙上时钟时间:
使用透明的ORA_ROWSCN 列还有一个好处:可以把SCN 转换为近似的墙上时钟时间(有+/–3 秒的偏差),从而发现行最后一次修改发生在什么时间。例如,可以执行以下查询:


ops$tkyte@ORA10G> select deptno, ora_rowscn, scn_to_timestamp(ora_rowscn) ts2 from dept;


DEPTNO ORA_ROWSCN          TS
----------     ----------                -------------------------------
10            34676381         25-APR-05 02.37.04.000000000 PM

20            34676364         25-APR-05 02.34.42.000000000 PM
30           34676364          25-APR-05 02.34.42.000000000 PM
40           34676364          25-APR-05 02.34.42.000000000 PM


在此可以看到,在表的最初创建和更新DEPTNO = 10 行之间,我等了大约3 分钟。不过,从SCN 到墙上时钟时间的这种转换有一些限制:数据库的正常运行时间只有5 天左右。例如,如果查看一个“旧”表,查找其中最旧ORA_ROWSCN(注意,在此我作为SCOTT 登录;没有使用前面的新表):


scott@ORA10G> select min(ora_rowscn) from dept;


MIN(ORA_ROWSCN)
---------------
364937


如果我试图把这个SCN 转换为一个时间戳,可能看到以下结果(取决于DEPT 表有多旧!):


scott@ORA10G> select scn_to_timestamp(min(ora_rowscn)) from dept;


select scn_to_timestamp(min(ora_rowscn)) from dept
*
ERROR at line 1:
ORA-08181: specified number is not a valid system change number
ORA-06512: at "SYS.SCN_TO_TIMESTAMP", line 1
ORA-06512: at line 1


所以从长远看不能依赖这种转换。

3、乐观悲观锁总结:

到底是用乐观锁呢还是悲观锁呢?现实生产环境中,使用乐观锁的机会远多于悲观锁,最大的原因就是悲观锁需要与数据库建立一个连接,这个连接会耗费很多的资源,特别是在用户数超过一百以上,每个用户有一个连接,这样是不现实的。

一般用乐观锁定的版本戳,然后再加一个时间戳,这样一来,就可以查询到每一行最后一次修改的时间啦,真是省劲快捷,hash方法那个如果碰到LONG,LONG RAW,CLOB,BLOB这等大数据就死翘翘了。。

4、阻塞和死锁:

数据库中有5条常见的语句会发生阻塞,他们分别是:insert,update,delete,merge,select for update

对于一个阻塞的SELECT FOR UPDATE,解决方案很简单:只需增加NOWAIT 子句,它就不会阻塞了。这样一来, 你的应用会向最终用户报告,这一行已经锁定。另外4 条DML 语句才有意思。我们会分别分析这些DML 语句,看看它们为什么不应阻塞,如果真的阻塞了又该如何修正。

1、阻塞的insert:

INSERT 阻塞的情况不多见。最常见的情况是,你有一个带主键的表,或者表上有惟一的约束,但有两个会话试图用同样的值插入一行。如果是这样,其中一个会话就会阻塞, 直到另一个会话提交或者回滚为止:如果另一个会话提交,那么阻塞的会话会收到一个错误,指出存在一个重复值;倘若另一个会话回滚,在这种情况下,阻塞的会话则会成功。还有一种情况,可能多个表通过引用完整性约束相互链接。对子表的插入可能会阻塞,因为它所依赖的父表正在创建或删除。如果应用允许最终用户生成主键/惟一列值,往往就会发生INSERT 阻塞。为避免这种情况,最容易的做法是使用一个序列来生成主键/惟一列值。序列(sequence)设计为一种高度并发的方法,用在多用户环境中生成惟一键。如果无法使用序列,那你可以使用以下技术,也就是使用手工锁来避免这个问题,这里的手工锁通过内置的DBMS_LOCK 包来实现。

当然,如果表的主键是一个INTEGER,而你不希望这个主键超过1 000 000 000,那么可以跳过散列,直接使用这个数作为锁ID。要适当地设置散列表的大小(在这个例子中,散列表的大小是1 024),以避免因为不同的串散列为同一个数(这称为散列冲突)而人工地导致资源忙消息。散列表的大小与特定的应用(数据)有关,并发插入的数量也会影响散列表的大小。最后,还要记住,尽管Oracle 有无限多个行级锁,但是enqueue 锁(这是一种队列锁)的个数则是有限的。如果在会话中插入大量行,而没有提交,可能就会发现创建了太多的enqueue 队列锁,而耗尽了系统的队列资源(超出了ENQUEUE_RESOURCES 系统参数设置的最大值),因为每行都会创建另一个enqueue 锁。如果确实发生了这种情况, 就需要增大ENQUEUE_RESOURCES 参数的值。还可以向触发器增加一个标志,允许打开或关闭这种检查。例如,如果我准备插入数百条或数千条记录,可能就不希望启用这个检查。

2、阻塞的update、delete和merge:

select for updatre nowait 能够避免丢失更新的问题。他能验证你自从获取数据到执行修改这期间没有别的事务来修改这个数据库,锁住这一行,防止update和delect阻塞。不论是乐观锁还是悲观锁,都会用这一句实现这些功能,只是悲观锁会在你获取这一行之后就执行了锁定,而乐观锁是在即将提交的时候才执行的。

3、死锁:

一般来看死锁的出现常见原因有两个:外键未加索引 表上的位图索引遭到并发更新

在以下情况,oracle修改父表后会对字表加一个索引:1、果更新了父表的主键,因为外键上没有索引,所以子表上会被锁住;2、删除了父表的一行,子表也会被锁住。

以上这种锁,只有在执行对应的DML语句是才会发生,而不是整个事务期间都会锁住字表,这样可以减少死锁的可能性,但是即使这样,死锁还是可以发生的。

删除父表中的一行可能导致子表被锁住,由此产生的问题更多。我已经说过,如果删除父表P 中的一行,那么在DML 操作期间,子表C 就会锁定,这样能避免事务期间对C 执行其他更新(当然,这有一个前提,即没有人在修改C;如果确实已经有人在修改C,删除会等待)。此时就会出现阻塞和死锁问题。通过锁定整个表C,数据库的并发性就会大幅下降,以至于没有人能够修改C 中的任何内容。另外,出现死锁的可能性则增加了,因为我的会话现在“拥有”大量数据,直到提交时才会交出。其他会话因为C 而阻塞的可能性也更大;只要会话试图修改C 就会被阻塞。因此,我开始注意到,数据库中大量会话被阻塞,这些会话持有另外一些资源的锁。实际上,如果其中任何阻塞的会话锁住了我的会话需要的资源,就会出现一个死锁。在这种情况下,造成死锁的原因是:我的会话不允许别人访问超出其所需的更多资源(在这里就是一个表中的所有行)。

相关文章
|
10月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
267 2
|
11月前
|
存储 缓存 算法
分布式锁服务深度解析:以Apache Flink的Checkpointing机制为例
【10月更文挑战第7天】在分布式系统中,多个进程或节点可能需要同时访问和操作共享资源。为了确保数据的一致性和系统的稳定性,我们需要一种机制来协调这些进程或节点的访问,避免并发冲突和竞态条件。分布式锁服务正是为此而生的一种解决方案。它通过在网络环境中实现锁机制,确保同一时间只有一个进程或节点能够访问和操作共享资源。
404 3
|
8月前
|
机器学习/深度学习 自然语言处理 搜索推荐
自注意力机制全解析:从原理到计算细节,一文尽览!
自注意力机制(Self-Attention)最早可追溯至20世纪70年代的神经网络研究,但直到2017年Google Brain团队提出Transformer架构后才广泛应用于深度学习。它通过计算序列内部元素间的相关性,捕捉复杂依赖关系,并支持并行化训练,显著提升了处理长文本和序列数据的能力。相比传统的RNN、LSTM和GRU,自注意力机制在自然语言处理(NLP)、计算机视觉、语音识别及推荐系统等领域展现出卓越性能。其核心步骤包括生成查询(Q)、键(K)和值(V)向量,计算缩放点积注意力得分,应用Softmax归一化,以及加权求和生成输出。自注意力机制提高了模型的表达能力,带来了更精准的服务。
10334 46
|
10月前
|
存储 缓存 监控
后端开发中的缓存机制:深度解析与最佳实践####
本文深入探讨了后端开发中不可或缺的一环——缓存机制,旨在为读者提供一份详尽的指南,涵盖缓存的基本原理、常见类型(如内存缓存、磁盘缓存、分布式缓存等)、主流技术选型(Redis、Memcached、Ehcache等),以及在实际项目中如何根据业务需求设计并实施高效的缓存策略。不同于常规摘要的概述性质,本摘要直接点明文章将围绕“深度解析”与“最佳实践”两大核心展开,既适合初学者构建基础认知框架,也为有经验的开发者提供优化建议与实战技巧。 ####
|
10月前
|
缓存 NoSQL Java
千万级电商线上无阻塞双buffer缓冲优化ID生成机制深度解析
【11月更文挑战第30天】在千万级电商系统中,ID生成机制是核心基础设施之一。一个高效、可靠的ID生成系统对于保障系统的稳定性和性能至关重要。本文将深入探讨一种在千万级电商线上广泛应用的ID生成机制——无阻塞双buffer缓冲优化方案。本文从概述、功能点、背景、业务点、底层原理等多个维度进行解析,并通过Java语言实现多个示例,指出各自实践的优缺点。希望给需要的同学提供一些参考。
177 8
|
9月前
|
PHP 开发者 UED
PHP中的异常处理机制解析####
本文深入探讨了PHP中的异常处理机制,通过实例解析try-catch语句的用法,并对比传统错误处理方式,揭示其在提升代码健壮性与可维护性方面的优势。文章还简要介绍了自定义异常类的创建及其应用场景,为开发者提供实用的技术参考。 ####
|
9月前
|
Java 数据库连接 开发者
Java中的异常处理机制:深入解析与最佳实践####
本文旨在为Java开发者提供一份关于异常处理机制的全面指南,从基础概念到高级技巧,涵盖try-catch结构、自定义异常、异常链分析以及最佳实践策略。不同于传统的摘要概述,本文将以一个实际项目案例为线索,逐步揭示如何高效地管理运行时错误,提升代码的健壮性和可维护性。通过对比常见误区与优化方案,读者将获得编写更加健壮Java应用程序的实用知识。 --- ####
|
10月前
|
Java 开发者 Spring
深入解析:Spring AOP的底层实现机制
在现代软件开发中,Spring框架的AOP(面向切面编程)功能因其能够有效分离横切关注点(如日志记录、事务管理等)而备受青睐。本文将深入探讨Spring AOP的底层原理,揭示其如何通过动态代理技术实现方法的增强。
360 8
|
10月前
|
Java 测试技术 API
Java 反射机制:深入解析与应用实践
《Java反射机制:深入解析与应用实践》全面解析Java反射API,探讨其内部运作原理、应用场景及最佳实践,帮助开发者掌握利用反射增强程序灵活性与可扩展性的技巧。
396 5
|
10月前
|
存储 消息中间件 算法
深入探索操作系统的心脏——内核机制解析
本文旨在揭示操作系统核心——内核的工作原理,通过剖析其关键组件与机制,为读者提供一个清晰的内核结构图景。不同于常规摘要的概述性内容,本文摘要将直接聚焦于内核的核心概念、主要功能以及其在系统管理中扮演的角色,旨在激发读者对操作系统深层次运作原理的兴趣与理解。

推荐镜像

更多