来自oracle资料 写道
Oracle 的无阻塞读方法有一个副作用,如果确实想保证一次最多只有一个用户访问一行数据,开发人员就得自己做些工作。
考虑下面这个例子。一位开发人员向我展示了他刚开发的一个资源调度程序(可以用来调度会议室、
投影仪等资源),这个程序正在部署当中。应用中实现了这样一个业务规则:在给定的任何时间段都不能
将一种资源分配给多个人。也就是说,应用中包含了实现这个业务规则的代码,它会明确地检查此前这个
时间片没有分配给其他用户(至少,这个开发人员认为是这样)。这段代码先查询SCHEDULES 表,如果不
存在与该时间片重叠的记录行(该时间片尚未分配),则插入新行。所以,开发人员主要考虑两个表:
create table resources ( resource_name varchar2(25) primary key, ... );
create table schedules
( resource_name references resources,
start_time date not null,
end_time date not null,
check (start_time < end_time ),
primary key(resource_name,start_time)
);
在分配资源(如预订房间)之前,应用将查询:
select count(*)
from schedules
where resource_name = :room_name
and (start_time <= :new_end_time)
and (end_time >= :new_start_time)
看上去很简单,也很安全(在开发人员看来):如果得到的计数为0,这个房间就是你的了。如果返回的数非0,那在此期间你就不能预订这个房间。了解他的逻辑后,我建立了一个非常简单的测试,来展示这个应用运行时可能出现的一个错误,这个错误极难跟踪,而且事后也很难诊断。有人甚至以为这必定是一个数据库bug。
我所做的其实很简单,就是让另外一个人使用这个开发人员旁边的一台机器,两个人都浏览同一个屏幕,然后一起数到3 时,两人都单击Go 按钮,尽量同时预订同一个房间,一个人想预订下午3:00 到下午4:00 这个时段,另一个人要预订下午3:30 到下午4:00 这个时段。结果两个人都预订成功。这个逻辑独立执行时原本能很好地工作,但到多用户环境中就不行了。为什么会出现这个问题?部分原因就在于Oracle的非阻塞读。这两个会话都不会阻塞对方,它们只是运行查询,然后完成调度房间的逻辑。两个会话都通过运行查询来查找是否已经有预订,尽管另一个会话可能已经开始修改SCHEDULES 表,但查询看不到这些修改(所做的修改在提交之前对其他会话来说是不可见的,而等到提交时已为时过晚)。由于这两个会话并没有试图修改SCHEDULES 表中的同一行,所以它们不会相互阻塞。由此说来,这个应用不能像预期的那样保证前面提到的业务规则。
开发人员需要一种方法使得这个业务规则在多用户环境下也能得到保证,也就是要确保一次只有一个人预订一种给定的资源。在这种情况下,解决方案就是加入他自己的一些串行化机制。他的做法是,在对SCHEDULES 表进行修改之前,先对RESOURCES 表中的父行锁定。这样一来, SCHEDULES 表中针对给定RESOURCE_NAME 值的所有修改都必须依次按顺序进行,一次只能执行一个修改。
这样一来, SCHEDULES 表中针对给定RESOURCE_NAME 值的所有修改都必须依次按顺序进行,一次只能执行一个修改。也就是说,要预订资源X一段时间,就要锁定RESOURCES 表中对应X 的那一行,然后修改SCHEDULES 表。所以,除了前面的count(*)外,开发人员首先需要完成以下查询:
select * from resources where resource_name = :room_name FOR UPDATE;
这里,他在调度资源之前先锁定了资源(这里指房间),换句话说,就是在SCHEDULES 表中查询该资源的预订情况之前先锁定资源。通过锁定所要调度的资源,开发人员可以确保别人不会同时修改对这个资源的调度。其他人都必须等待,直到他提交了事务为止,此时就能看到他所做的调度。这样就杜绝了调度重叠的可能性。
开发人员必须了解到,在多用户环境中,他们必须不时地采用多线程编程中使用的一些技术。在这里,FOR UPDATE 子句的作用就像是一个信号量semaphore),只允许串行访问RESOURCES 表中特定的行,这样就能确保不会出现两个人同时调度的情况。我建议把这个逻辑实现为一个事务API,也就是说,所有逻辑都打包进一个存储过程中,只允许应用通过这个API 修改数据。代码如下:
create or replace procedure schedule_resource
( p_resource_name in varchar2,
p_start_time in date,
p_end_time in date
)
as
l_resource_name resources.resource_name%type;
l_cnt number;
begin
首先在RESOURCES 表中锁定我们想调度的那个资源的相应行。如果别人已经锁定了这一行,我们就会阻塞并等待:
select resource_name into l_resource_name
from resources
where resource_name = p_resource_name
FOR UPDATE;
既然我们已经有了锁,那么只有我们能在这个SCHEDULES 表中插入对应此资源名的调度,所以如下查看这个表是安全的:
select count(*)
into l_cnt
from schedules
where resource_name = p_resource_name
and (start_time <= p_end_time)
and (end_time >= p_start_time);
if ( l_cnt <> 0 )
then
raise_application_error
(-20001, 'Room is already booked!' );
end if;
如果能运行到这里而没有发生错误,就可以安全地在SCHEDULES 表中插入预订资源的相应记录行,而不用担心出现重叠:
insert into schedules
( resource_name, start_time, end_time )
values
( p_resource_name, p_start_time, p_end_time );
end schedule_resources;
这个解决方案仍是高度并发的,因为可能有数以千计要预订的资源。这里的做法是,确保任何时刻只能有一个人修改资源。这是一种很少见的情况,在此要对并不会真正更新的数据手动锁定。
我们要知道哪些情况下需要这样做,还要知道哪些情况下不需要这样做(稍后会给出这样一个例子),这同样很重要。另外,如果别人只是读取数据,就不会锁定资源不让他们读(但在其他数据库中可能不是这样),所以这种解决方案可以很好地扩缩。
以上代码有一个前提,即假设事务隔离级别是READ COMMITTED。如果事务隔离级别是SERIALIZABLE,这个逻辑将无法正常工作。
分享到:
相关推荐
Oracle锁和阻塞原理解析.pdf 了解锁和阻塞是 oracle troubleshooting 必备的技能;在说锁和阻塞之前,我们先了解下 并发(concurrency)和并行(parallel)。并发意思是在数据库中有超过两个以上用户对同样的数 据做修改...
oracle非数字合计,将字段中含有汉子的数据自动转换为 数字0
oracle 非空闲等待事件详细解释 example db file single write 对数据文件头的更新,以刷新数据文件记录的检查点信息.
oracle 创建视图为读
Oracle 数据库的几个常用函数,非数值列加和经常会用的到,常用于统计
oracle原厂工程师oracle执行计划培训pdf
百分百解决“System.Data.OracleClient 需要 Oracle 客户端软件 8.1.7 或更高版本”这个问题 详细操作请看压缩包TXT文档说明……
本驱动连接的版本为oracle 10.2.0.3.0
主要介绍了Oracle查看逻辑读、物理读资源占用排行的SQL语句,需要的朋友可以参考下
4、连接oracle服务器时需要手动配置tnsnames.ora,用户可参照tnsnames.ora中的注释部分进行配置。 5、本软件卸载时,将删除已安装的文件、环境变量及相应的注册表信息,绝对绿色! 注: 已安装了oracle的用户,如想...
Oracle数据库监听,为数据库的运行状态,向外连接提供了时时通讯方法,就犹如房屋中介一般,为租客与房东提供相互所需要的信息!
oracle系统表查询,详细讲解各个系统表的使用
通常linux下用安装oracle后,只有切换至oracle用户下方可执行相关的数据库的sqlplus及imp\exp相关命令的操作,经过这里的此配置后方可解决此问题,所有用户下均可以执行上述相关操作
大神专业讲解oracle执行计划,全面分析数据库执行计划的方方面面
Dbmover for Mysql to Oracle 是高效的将Mysql导入到Oracle数据库的工具。 使用DBMover可以灵活定义Mysql和Oracle之间表和字段的对照关系,也可以在DBMover创建一个查询,把查询结果当作源表转入到Oracle中。 ...
我见过最好的Oracle数据库连接组件,性能和效率甚至比官方的高,最爽的是它不需要安装Oracle客户端即可连接数据库。赶快破解了献给大家,希望大家喜欢。使用方法:一、不用安装,直接使用破解文件夹中的组件,使用...
Oracle宝典Oracle宝典Oracle宝典Oracle宝典Oracle宝典Oracle宝典Oracle宝典Oracle宝典Oracle宝典Oracle宝典Oracle宝典Oracle宝典Oracle宝典Oracle宝典Oracle宝典Oracle宝典Oracle宝典Oracle宝典Oracle宝典Oracle宝典...
oracle网络配置(连接非本机oracle服务器)
cengtos7离线安装oracle11g所需要的rpm包,亲测可用,货真价实,cengtos7离线安装oracle11g所需要的rpm包,亲测可用,货真价实cengtos7离线安装oracle11g所需要的rpm包,亲测可用,货真价实cengtos7离线安装oracle11...
为了方便oracle开发使用,提供一个非安装的oracle 客户端版本。省去oracle客户端安装时间