Oracle数据库编写有效事务指导方针
 

2009-05-21 作者:Victor 来源:网络

 

数据共享是数据库最基本的特征之一。但是数据共享虽然为员工带来了便利,但也产生了一些负面作用。例如因用户并发存取而导致的对数据一致性的破坏、由于在修改数据过程中的意外事故导致数据完整性破坏、非法用户未经授权修改数据等等。所以,在数据库设计过程中,数据库管理员一方面要想着如何提高数据的共享程度,另一方面,也需要考虑如何保护数据的安全性。

在Oracle数据库中,提供了一种被称为“事务”的控制机制。通过事物,能够完成对数据有效安全的修改操作,使数据库中的数据达到一个数据一致的状态。举个简单的例子,现在有一个借书系统中设涉及到两张表,一张是图书库存表,一张是用户借书情况表。在用户借书的时候,数据库需要进行两个操作,一是从图书库存表中扣掉库存;另一个操作时在用户借书表中加入这个借书操作。数据库在操作时,往往是先扣减库存,然后再在用户借书情况表中加入借书纪录。假设用户在借书的时候,第一步操作成功,即已经从图书库存表中扣除了某书的库存;但是,在第二步时由于发现用户借的书已经超量或者发现用户的卡中还有罚款不能够借书时,借书就会不成功。若没有事务做控制的话,很明显图书库存就会不准。而在事务的管理下,当第二步不成功的话,第一步操作就会发生回滚。也就是说,事务可以把数据库的好几个操作步骤当作一个整体,当其中有某个操作没有成功的话,则所有操作都会发生回滚。Oracle数据库就是通过这种机制来保障数据的一致性问题。

但是,事务若编写的不好的话,则不但起不到应有的作用,还会大大降低数据库的性能。如在数据库事务执行时间比较长,就很有可能导致锁冲突,从而降低数据库的并发访问性能。所以,数据库管理员在编写事务时,还是需要遵守几个指导方针。

指导方针一:在事务中尽量使得访问的纪录最小。

在事务中,若执行Update等记录操作语句,数据库为了保障数据的一致性,会对其所访问的记录加锁,防止在同一时间内其他用户对其修改。此时,若其他用户需要访问加锁的记录,则必须等待。此时就会发生锁冲突。

所以,在事务中要尽量使得访问的记录量最小。如此就可以减少锁定的行数,从而减少事务之间的冲突。这要求数据库管理员在事务中尽量精确的定位语纪录。如在数据库中读取记录时,最好能够使用Where语句进行定位。并且在编写Where语句的时候,要尽量的详细,最好能够实现一对一,则是最好的。

如在库存盘点的时候,事务处理语句需要从数据库中读取一定数量的记录,并且为这些记录进行加锁。此时,若记录读取的过多的话,就会对其他用户访问表中的记录造成麻烦。为此,数据库管理员应该建议应用前台应用程序在开发的过程中,加入一些限制条件。如按照产品的分类来更新库存。如此的话,就可以减少一个事务一次性读取的记录数量,从而把锁的影响降低到最低。

指导方针二:保持事务尽可能的简洁。

在事务中尽量使得访问的纪录最小,这是从数量的角度来考虑锁冲突。而保持事务尽可能的简洁,则是从时间的角度来考虑避免锁冲突的事情。保持事务尽可能的简洁,主要是要求数据库管理员在编写事务的时候,不要把事务写的太过于庞大与复杂。否则,事务在执行的时候就会占用比较多的时间。而这直接导致的后果就是数据库会把某些记录、甚至一张数据表锁住比较长的时间。这就会恶化锁对数据库的负面影响。

故当用户在知道了必须要进行修改的记录之后,就要马上启动事务;并且在最短时间内执行完相关的修改语句,然后立即递交或者回滚。而且,只有在确实需要时,才打开事务语句。具体的来说,数据库管理员在事务的简洁性方面,可以尝试如下方法。

一是在同一个事务中不要加入过多的修改或者删除语句。如当用户需要更新用户信息表中的相关数据。例如在员工的编号前加入YG前缀并且同时根据员工入职年份计算员工工龄。这两个更新语句若从技术上来说,放在同一个事务中并没有什么不妥。但是,当员工数量比较多时,若把他们合并在一起,则这个事务执行得时间会比较久。为此,最好在更新数据库表的时候,若预计执行时间会比较长,则最好能够把更新语句进行分割,如一列列更新等等。

二是在更新时,若一次性更新的语句比较多,最好能够选择合适的时候更新。更新某个数据库中记录,其执行所需要的时间往往跟数据库的记录成正比。其记录越多,更新所需要的时间越多。为此,笔者建议,当需要更新的记录比较多的时候,最好能够选择合理的时机。如有些应用程序在设计时,可以把这个更新放在后台处理。如此的话,应用程序就可以选择数据库比较空的时候,来更新表中的记录。这无疑可以在很大程度上降低事务对数据库的负面影响。

指导方针三:不要在事务处理期间要求用户输入。

数据库管理员在编写事务时,要确保在事务启动之前,让数据库系统获得事务执行所需要的所有内容。如记录的查询条件、所需要更新的内容等等。如果在事务执行的过程中还需要用户的输入,就回滚当前的事务。当用户提供了必要的参数之后,再重新启动这个事务。因为即使在事务执行的过程中,用户立即响应。但是,用户的反应速度要比电脑的响应速度慢的多。所以,当用户在事务执行的过程中需要输入参数的话,就会使得这个事务所占用的数据库资源要保持很长的时间。这就有可能增加阻塞的风险。因为当用户没有及时输入所需要的参数时,事务仍然会保持活动状态,并锁定相关的资源,直到他们响应为止。若用户所需要输入的参数比较多时,用户可能会几分钟甚至一个小时没有输入。

这不是一种理论上的假设,笔者在实际工作中就碰到过这种例子。如在一个ERP系统中有订单变更的功能。若在设计的时候,在用户打开销售订单变更单就触发变更事务的话,则因为用户输入订单变更所需要的时间不能够确定。有时候用户这边改一改,那边再确认一下,可能一个小时就过去了。此时,这张销售订单对应的内容其他用户就无法查看了,因为数据库中已经被这个事务锁住。这显然是设计的不合理的。笔者认为,应到在用户点击确认按钮时,再触发这个变更事务。此时,用户已经输入了所需要更改的所有内容,在更新事务的执行过程中,不需要用户再输入其他的额外参数。通过这种方式,就可以把事务所占用资源的时间缩短到最低。

指导方针四:在浏览数据时,尽量不要打开事务。

根据笔者的经验,用户更改数据所需要的时间,其实很少。而大部分时间则是在更改数据之前对数据的分析上。如在定位需要对哪些数据要进行更改;如在更改事务递交好的审核;如在考虑该怎么进行更改。这个分析工作所占据的时间往往是大头。

故笔者提醒数据库管理员,在所有预备的数据分析完成之前,在用户数据浏览的时候,不要启动事务。也就是说,在用户更改数据的时候,仍然不是触发更新事务的最佳时间。只有到用户确认无误后,选择“更新”按钮,此时,才可以触发这个事务。并且,及时递交或者回滚这个事务。如此,在事后审核的过程中,事务就不会继续占用资源。

除了以上这些指导方针外,还有其他的一些小细节要注意。如尽量采用级别低的事务隔离级别,数据库管理员要切记,不是所有的事务都要求串行事务隔离级别;如事务设计的简短一些;如在事务回退时,可根据实际情况选择回退全部事务或者是部分事务等等。另外,要特别注意在事务中排他锁的副作用。在修改数据时,为了保障数据的一致性,往往需要利用排它锁保护修改过的行,以防止其他任何事务读取这一行,并且必须把排它锁控制到递交或者回滚事务为止。为此,数据库管理员在设计跟更新相关的事务时,要合理选择时机。让事务在保障数据安全性的同时,最大限度的降低其负面作用。


火龙果软件/UML软件工程组织致力于提高您的软件工程实践能力,我们不断地吸取业界的宝贵经验,向您提供经过数百家企业验证的有效的工程技术实践经验,同时关注最新的理论进展,帮助您“领跑您所在行业的软件世界”。
资源网站: UML软件工程组织