对于一个开发人员来讲,无论他使用什么编程语言,Java也好,。NET也好,只要是开发基于数据库的系统,就应该理解该数据库是如何工作的。
Oracle作为业界最大、使用范围最广的数据库,自然有其突出的特征。本文将介绍Oracle为开发人员提供的两种最重要,最基本的特性:Oracle的锁机制和多版本视图。作为数据库开发人员来讲,理解这两个特性是非常重要的。只有理解了它们,开发人员才能确保在我要求Oracle做A事时,Oracle的确在做A事,而不是B事。否则,几乎无法保证这个开发的系统是否满足最基本的需求。
1. 锁
这里所讨论的锁并不是指latch(一种保护SGA中共享资源的轻量级锁),仅指lock,合格的DBA应该非常关心latch在系统中的使用。
对于开发人员来讲,如果要开发多用户、并发访问的系统(如今好像几乎不存在单用户的应用了),那么确保并发性当然是最重要的问题之一。在Oracle中,确保数据完整性、高并发的机制之一就是锁,所以理解Oracle实施的锁机制对于成功的开发应用程序是非常重要的。虽然每种数据库都实施了锁,并且存在相似性,不过Oracle在一块做的比其他数据库略胜一筹。
Oracle的“读不阻塞写,写不阻塞读”是对开发人员最有帮助的特性,并且也是其他数据库目前还没有做到的(例如在SQL Server,排他锁会阻塞共享锁,共享锁也会阻塞排他锁;在teradata中,虽然可以使用那个lock……access实现写不阻塞读,但却是读脏,这在设计合格的数据库系统中是不允许出现的情况。),所以我的一些同事对于每次在SQL*Plus中进行查询总是能立刻返回结果,而在Teradata SQL Assistant执行类似的查询经常会耗费几分钟甚至几十分钟感觉很不可思议。所以,开发人员永远必须担心Oracle读写会发生互锁问题,这也是Oracle高性能的原因之一。
从业务逻辑开发角度来讲,确保系统中数据尽可能的完整和高并发必须由你开发人员来保证,虽然Oracle提供了很好的机制,但是开发人员必须知道如何利用它。在测试中我常干的一件事是打开两个web页面,执行相同的数据库查询,然后先后到更新页面更改同一条记录,通常结果只有两种:一、两个页面同时打开,更新两个页面,刷新后两个页面的数据都不是预期的结果;二、第二个页面一直处于等待状态,直到第一个页面返回到显示页面。显然这两种结果都不是用户希望看到的。第一种情况,开发人员必须解决丢失更新问题(数据完整性);第二种情况,则必须解决并发问题。Oracle为这两种问题提供了非常优秀并且易用的解决方案,最简单的方法就是行依赖性跟踪特性(ORA_ROWSCN,虽然启用它会为每行增加6个字节的额外空间开销以及极小的维护代价,不过相对于它带来的好处这是微不足道),ORA_ROWSCN会在每次行被更新时自动更新,不需要开发人员的手工维护,只需要DBA在创建表的时候启用ROWDEPENDENCIES即可。对于开发人员来说,要做的就是在执行更新的代码中将ORA_ROWSCN作为额外的更新条件。作为其他的选择,开发人员也可以使用额外的时间戳列作为条件,不过从使用简单角度讲,还是推荐Oracle已经完成的功能。
考虑一个比较实际的例子,相信读者就能理解为什么正确的使用锁是如此的重要以及不恰当使用锁可能造成的损失。考虑一个订单或订票类系统,假设还剩一张票,两个用户A, B同时到两个不同的窗口准备进行订票。如果我们使用不正确的for update锁定,过程如下:
A: 查询,显示剩余一张票; B: 查询,显示剩余一张票;
A: 进入更新页面,准备订票; B: 进入更新页面,等待中……;
A: 思考; B: 离开;
A: 取消订票;
结果将是没有票售出,这显然造成了损失。
对于企业来讲,是非常不希望发生这种情况的。如果我们利用了Oracle提供的正确的可用于锁的机制,如采用ORA_ROWSCN(或者其他时间戳机制),则显然不会发生这种情况。
这里再稍微提一下,对于目前的应用而言,使用FOR UPDATE锁定应该说是适用范围不大,但某些时候的确应用使用它,仅限于“资源”独占的模块(比如用户帐户管理),因为这些应用中的数据应该只能是特定用户专用的。
因此,作为开发人员,理解Oracle提供的,而其他数据库不具备的并且非常重要的特性对于成功的开发应用程序是很重要的。