`
pterodactyl
  • 浏览: 766597 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Oracle中的数据锁定机制全面解析

阅读更多
http://www.diybl.com/course/7_databases/oracle/oraclejs/20071211/91889.html


为了得到最大的性能,一般数据库都有并发机制,不过带来的问题就是数据访问的冲突。为了解决这个问题,大多数数据库用的方法就是数据的锁定。

数据的锁定分为两种方法,第一种叫做悲观锁,第二种叫做乐观锁。什么叫悲观锁呢,悲观锁顾名思义,就是对数据的冲突采取一种悲观的态度,也就是说假设数据肯定会冲突,所以在数据开始读取的时候就把数据锁定住。而乐观锁就是认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让用户返回错误的信息,让用户决定如何去做。

先从悲观锁开始说。在SqlServer等其余很多数据库中,数据的锁定通常采用页级锁的方式,也就是说对一张表内的数据是一种串行化的更新插入机制,在任何时间同一张表只会插1条数据,别的想插入的数据要等到这一条数据插完以后才能依次插入。带来的后果就是性能的降低,在多用户并发访问的时候,当对一张表进行频繁操作时,会发现响应效率很低,数据库经常处于一种假死状态。而Oracle用的是行级锁,只是对想锁定的数据才进行锁定,其余的数据不相干,所以在对Oracle表中并发插数据的时候,基本上不会有任何影响。

Oracle的悲观锁需要利用一条现有的连接,分成两种方式,从SQL语句的区别来看,就是一种是for update,一种是for update nowait的形式。比如我们看一个例子。首先建立测试用的数据库表。

CREATE TABLE TEST(ID,NAME,LOCATION,VALUE,CONSTRAINT test_pk PRIMARY KEY(ID))AS SELECT deptno, dname, loc, 1 FROM scott.dept


这里我们利用了Oracle的Sample的scott用户的表,把数据copy到我们的test表中。首先我们看一下for update锁定方式。首先我们执行如下的select for update语句。

select * from test where id = 10 for update


通过这条检索语句锁定以后,再开另外一个sql*plus窗口进行操作,再把上面这条sql语句执行一便,你会发现sqlplus好像死在那里了,好像检索不到数据的样子,但是也不返回任何结果,就属于卡在那里的感觉。这个时候是什么原因呢,就是一开始的第一个Session中的select for update语句把数据锁定住了。由于这里锁定的机制是wait的状态(只要不表示nowait那就是wait),所以第二个Session(也就是卡住的那个sql*plus)中当前这个检索就处于等待状态。当第一个session最后commit或者rollback之后,第二个session中的检索结果就是自动跳出来,并且也把数据锁定住。不过如果你第二个session中你的检索语句如下所示。

select * from test where id = 10


也就是没有for update这种锁定数据的语句的话,就不会造成阻塞了。另外一种情况,就是当数据库数据被锁定的时候,也就是执行刚才for update那条sql以后,我们在另外一个session中执行for update nowait后又是什么样呢。比如如下的sql语句。由于这条语句中是制定采用nowait方式来进行检索,所以当发现数据被别的session锁定中的时候,就会迅速返回ORA-00054错误,内容是资源正忙, 但指定以 NOWAIT 方式获取资源。所以在程序中我们可以采用nowait方式迅速判断当前数据是否被锁定中,如果锁定中的话,就要采取相应的业务措施进行处理。

select * from test where id = 10 for update nowait


那这里另外一个问题,就是当我们锁定住数据的时候,我们对数据进行更新和删除的话会是什么样呢。比如同样,我们让第一个Session锁定住id=10的那条数据,我们在第二个session中执行如下语句。

update test set value=2 where id = 10


这个时候我们发现update语句就好像select for update语句一样也停住卡在这里,当你第一个session放开锁定以后update才能正常运行。当你update运行后,数据又被你update 语句锁定住了,这个时候只要你update后还没有commit,别的session照样不能对数据进行锁定更新等等。

总之,Oracle中的悲观锁就是利用Oracle的Connection对数据进行锁定。在Oracle中,用这种行级锁带来的性能损失是很小的,只是要注意程序逻辑,不要给你一不小心搞成死锁了就好。而且由于数据的及时锁定,在数据提交时候就不呼出现冲突,可以省去很多恼人的数据冲突处理。缺点就是你必须要始终有一条数据库连接,就是说在整个锁定到最后放开锁的过程中,你的数据库联接要始终保持住。

与悲观锁相对的,我们有了乐观锁。乐观锁一开始也说了,就是一开始假设不会造成数据冲突,在最后提交的时候再进行数据冲突检测。在乐观锁中,我们有5种常用的做法来实现。

第一种就是在数据取得的时候把整个数据都copy到应用中,在进行提交的时候比对当前数据库中的数据和开始的时候更新前取得的数据。当发现两个数据一模一样以后,就表示没有冲突可以提交,否则则是并发冲突,需要去用业务逻辑进行解决。

第二种乐观锁的做法就是采用版本戳,这个在Hibernate中得到了使用。采用版本戳的话,首先需要在你有乐观锁的数据库table上建立一个新的 column,比如为number型,当你数据每更新一次的时候,版本数就会往上增加1。比如同样有2个session同样对某条数据进行操作。两者都取到当前的数据的版本号为1,当第一个session进行数据更新后,在提交的时候查看到当前数据的版本还为1,和自己一开始取到的版本相同。就正式提交,然后把版本号增加1,这个时候当前数据的版本为2。当第二个session也更新了数据提交的时候,发现数据库中版本为2,和一开始这个session取到的版本号不一致,就知道别人更新过此条数据,这个时候再进行业务处理,比如整个Transaction都Rollback等等操作。在用版本戳的时候,可以在应用程序侧使用版本戳的验证,也可以在数据库侧采用Trigger(触发器)来进行验证。不过数据库的Trigger的性能开销还是比较的大,所以能在应用侧进行验证的话还是推荐不用Trigger。

第三种做法和第二种做法有点类似,就是也新增一个Table的Column,不过这次这个column是采用timestamp型,存储数据最后更新的时间。在Oracle9i以后可以采用新的数据类型,也就是timestamp with time zone类型来做时间戳。这种Timestamp的数据精度在Oracle的时间类型中是最高的,精确到微秒(还没与到纳秒的级别),一般来说,加上数据库处理时间和人的思考动作时间,微秒级别是非常非常够了,其实只要精确到毫秒甚至秒都应该没有什么问题。和刚才的版本戳类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。如果不想把代码写在程序中或者由于别的原因无法把代码写在现有的程序中,也可以把这个时间戳乐观锁逻辑写在Trigger或者存储过程中。

第四种做法就是采用散列算法。这种做法和第一种做法有点类似,就是把当前数据内容进行传递,最后在提交的时候进行比较。但是区别还是比较大的,第一种做法把整个更新前的数据都copy了下来进行传递,而这种做法只是把数据做了一个散列码,所以传递的数据要小的多,网络负荷也会因此小很多。这种采用散列算法的做法如果在Oracle侧生成散列的话,有三种方法可以生成。第一种就是利用Oracle8i 8.1.5以后的OWA_OPT_LOCK.CHECKSUM函数来进行数据的散列生成比如下面的例子。

select owa_opt_lock.checksum (to_char(sysdate,''YYYYMMDDHHMISS'') ) from dual


在这里我用这个OWA_OPT_LOCK.CHECKSUM函数对当前的系统时间进行了散列,用这个方法生成的出土的可能性是65536,因为生成的散列算法是一个16位置的值,所以值范围也就是65536个值而已。

第二种散列算法采用Oracle8i 8.1.7以后提供的DBMS_OBFUSCATION_TOOLKIT.MD5方法,不过这个方法不能直接在SQL语句里面使用,需要在存储过程中调用。所以可以先写一个共通的存储过程的Function,然后利用这个function去进行加密。

第三种散列方法就是用Oracle 10g Release1种提供的DBMS_CRYPTO.HASH方法。这个方法可以计算一个SHA-1或者是MD5摘要,所以如果实际情况下使用Oracle 10g的数据库,建议采用这个方法。不过和第二种类似,这个方法也是需要采用存储过程才能够编写,不能直接应用在SQL语句中的。

最后一种做法就是采用Oracle 10g所带有的ORA_ROWSCN函数来进行乐观锁。ORA_ROWSCN是根据系统最后更新时间来进行计算。这个ORA_ROWSCN在默认情况下是采用数据块为单位的,也就是一个数据库块(block)上共享一个ORA_ROWSCN,当数据更新的时候,这个block快的ORA_ROWSCN就会自动更新。所以在默认情况下的话,有可能出现假冲突的情况。比如A,B,C,D四条数据都在一个block上,这个时候A数据更新了,ORA_ROWSCN也会更新,这个时候因为ABCD四条数据存储在一个block上,所以BCD的ORA_ROWSCN也更新过了,其实BCD三条数据并没有更新过,这个就造成了假更新的情况出现。见下面的SQL语句:

select id, name, location, value, ora_rowscn from test


检索结果如下:

ID NAME LOCATION VALUE ORA_ROWSCN
10 ACCOUNTING NEW YORK 1 610795
20 RESEARCH DALLAS 1 610795
30 SALES CHICAGO 1 610795
40 OPERATIONS BOSTON 1 610795


然后执行如下的更新语句:

update test set value=2 where id = 20


在数据commit过后,会发现ORA_ROWSCN发生了变化,如下所示:

ID NAME LOCATION VALUE ORA_ROWSCN
10 ACCOUNTING NEW YORK 1 611151
20 RESEARCH DALLAS 1 611151
30 SALES CHICAGO 1 611151
40 OPERATIONS BOSTON 1 611151


对于这种情况还是可以有办法解决的,就是利用Oracle 9i提供的ROWDEPENDENCIES建表关键字,这个关键字在Oracle9i中是为了增加行依赖性跟踪特性的,支持推进复制。在 Oracle10g中有可以用来做行级别的ORA_ROWSCN用了。用这个关键字建表以后,在每行会增加一个隐藏的COLUMN,所以每行会增加6个 byte的开销。建表语句如下所示:

CREATE TABLE TEST(ID,NAME,LOCATION,VALUE,CONSTRAINT test_pk PRIMARY KEY(ID))ROWDEPENDENCIESAS SELECT deptno, dname, loc, 1 FROM scott.dept


同样再执行:

update test set value=2 where id = 20


语句后,我们再看一下commit过后检索的结果:

ID NAME LOCATION VALUE ORA_ROWSCN
10 ACCOUNTING NEW YORK 1 611370
20 RESEARCH DALLAS 1 611370
30 SALES CHICAGO 1 611370
40 OPERATIONS BOSTON 1 611370


可以看到这次只有id=20的数据的ORA_ROWSCN发生了更新。所以这个时候我们就可以利用ORA_ROWSCN作为乐观锁,用不着专门再增加一个额外的域了。而且这个域是Oracle负责维护的,开销也不是很大,不过需要Oracle 10g才可以使用。

总之,介绍了Oracle的锁定机制,究竟是悲观锁好还是乐观锁好,其实也是不一定的。

Oracle中悲观锁还是很不错的,而且从开始的时候就把数据锁定。免除了后面的很多冲突处理。不过悲观锁需要保持一个Oracle连接,在我们常见的 B/S应用中,特别是数据先取得,然后让用户再更新,再返回提交这种流程来说,悲观锁是不大可能的。首先是因为B/S应用中,一般是利用一个连接池,在两次Http Request请求都是不同的数据库Connection。而且也不能锁定一个数据太长时间,否则人人都这么锁定,应用很容易进入死锁状态,这个时候就要采用乐观锁了。而在乐观锁中,虽然采用散列的效果不错,网络传输数据会减少很多,而且不用在数据库中增加COLUMN,不过由于散列计算比较占用CPU,所以还是需要仔细考虑一下的。ORA_ROWSCN可以说是比较好的选择,不过第一需要重新建表,第二需要在Oracle10g下才有这个特性,所以也不是到处都可以使用,当一个万金油的。


分享到:
评论

相关推荐

    Oracle数据库精讲与疑难解析

    "Oracle数据库精讲与疑难解析"这本电子书深入探讨了Oracle数据库的各种核心概念和技术,旨在帮助读者全面理解和掌握Oracle数据库的运作机制,并解决实际工作中遇到的问题。 在第13章中,我们可以预见到作者可能会...

    Oracle中文教程(最经典教程)

    此外,教程还会讲解Oracle的并发控制机制,如行级锁定和多版本并发控制(MVCC),这对于理解并发环境下数据的一致性至关重要。数据库的事务管理,包括ACID属性(原子性、一致性、隔离性和持久性),也是这一部分的...

    oracle死锁故障分析和诊断解决

    #### 二、Oracle中的死锁机制 在Oracle中,死锁主要由以下几种情况引起: 1. **资源竞争**:当多个事务同时尝试修改同一数据行或表时,可能会发生死锁。 2. **锁定模式不一致**:例如,一个事务使用共享锁(S Lock...

    深度解析Oracle 实战与提高

    Oracle数据库系统是全球广泛使用的大型关系型数据库管理系统之一,它在企业级数据管理、数据分析以及事务处理中扮演着核心角色。本资料“深度解析Oracle 实战与提高”旨在帮助读者深入理解Oracle的内部机制,提升...

    Oracle基础第三版 Oracle Essentials

    Oracle使用事务和锁定机制来实现这一目标,书中的这部分内容将帮助读者理解这些机制并学会如何在实践中应用。 数据库安全性是另一个重点。Oracle提供了多种安全机制,如用户权限管理、角色、审计和网络加密。读者将...

    ORACLE11G性能分析及优化对策

    为了有效提升Oracle 11g的性能,DBA和开发人员需要全面掌握数据库的工作原理、查询优化器的行为以及锁机制的应用。通过对这些关键领域的深入了解,可以显著提高系统的响应速度和稳定性,从而为企业带来更大的价值。

    Oracle 11g concept 中英文对照版

    5. **事务与并发控制**:理解事务的ACID属性(原子性、一致性、隔离性和持久性),以及Oracle如何通过回滚段、多版本并发控制(MVCC)和锁定机制确保数据的一致性。 6. **恢复机制**:包括重做日志和归档日志的概念,...

    Oracle 11gR2 concept中英文对照版

    10. **数据仓库与OLAP**:Oracle提供了强大的数据仓库工具,如Data Warehouse Builder和OLAP选项,用于构建高性能的数据分析环境。 通过学习Oracle 11gR2 Concept,无论是初学者还是有经验的DBA,都能深入理解...

    oracle官方中文文档+学习笔记

    7. **并发控制**:介绍了Oracle的锁定机制和多版本并发控制(MVCC),如读已提交和可重复读的隔离级别。 8. **备份与恢复**:涵盖了数据库备份的各种策略,如完整备份、增量备份,以及如何使用RMAN(恢复管理器)...

    Oracle9i OCA中文教程

    3. **并发控制**:解释Oracle的锁定机制,死锁的预防和处理,以及事务管理。 4. **数据库维护**:如何执行日常维护任务,如空间管理、日志切换和归档操作。 5. **网络配置**:介绍Oracle Net Services,配置监听器...

    Oracle Locking Survival Guide

    总的来说,《Oracle Locking Survival Guide》是一份全面的参考资料,涵盖了Oracle数据库中锁定机制的各个方面,包括锁定原理、问题诊断和优化策略,对于任何使用Oracle数据库的IT专业人员来说都是宝贵的财富。

    Oracle 11g 中文API Oracle Database Concepts 11gR2 中英文对照版

    锁定机制则用于避免数据冲突。 6. **备份与恢复**:Oracle支持多种备份策略,如完整数据库备份、增量备份和归档日志备份。在发生数据丢失或故障时,可以通过RMAN(Recovery Manager)进行恢复操作。 7. **性能优化...

    完全接触ORACLE

    Oracle使用行级锁定机制,确保多用户环境下数据的一致性。此外,Oracle还提供备份和恢复功能,确保数据的安全性。 四、Oracle性能优化 性能优化是Oracle数据库管理的重要部分。通过分析SQL语句执行计划、使用索引、...

    Oracle8i Internal Services

    **闩锁**是一种轻量级的锁定机制,用于保护共享内存中的数据结构免受并发访问的影响。Oracle数据库使用闩锁来确保多用户环境下的数据一致性。 - **Parent and Child Latches(父闩锁与子闩锁)**:这一节解释了闩锁...

    Oracle公司的PPT(全英文)

    5. **事务和并发控制**:PPT会讨论事务的概念,事务的ACID属性(原子性、一致性、隔离性和持久性),以及Oracle如何通过锁定机制和多版本并发控制(MVCC)来处理并发问题。 6. **备份与恢复**:Oracle提供了一系列...

    ORACLE10G联机文档中英文对照Conceptes

    4. **并发控制**:讲解Oracle的多用户并发访问机制,如锁定、事务和并发控制策略,确保数据的一致性和完整性。 5. **备份与恢复**:涵盖Oracle的备份策略、RMAN(恢复管理器)的使用,以及如何进行数据库的完整恢复...

    精通Oracle_SQL(第2版)含源码

    《精通Oracle SQL(第2版)》是一本深入解析Oracle数据库管理系统中SQL语言的专业书籍,旨在帮助读者全面理解和掌握在Oracle环境下高效、精准地运用SQL进行数据查询、操作和管理的技能。书中不仅包含了基础的SQL语法...

    Oracle培训文档(公司内部培训文档)

    此外,Oracle的锁定机制也是保证数据一致性的重要工具,需要理解行级锁、表级锁以及读写锁等概念。 第六,Oracle的高可用性方案,如RAC(Real Application Clusters)和Data Guard,可以提供故障切换和灾难恢复能力...

    oracle 本科教学ppt

    在多用户环境下,Oracle使用事务和锁定机制来保证数据的一致性和完整性。这部分会讲解并发控制的基础知识,如读写锁、行级锁定和死锁的避免。 八、数据库复制 Oracle的复制技术,如逻辑复制(如GoldenGate)、物理...

    oracle查询优化pdf

    Oracle查询优化是数据库管理中的关键环节,它涉及到提高数据检索速度、降低系统资源消耗和提升整体应用性能。这本书“Oracle查询优化PDF”很可能是对Oracle数据库查询优化技术的深入探讨,涵盖了一系列相关主题,...

Global site tag (gtag.js) - Google Analytics