`
maimode
  • 浏览: 415748 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

oracle非阻塞读需要重视

 
阅读更多

 

 

来自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锁和阻塞原理解析.pdf 了解锁和阻塞是 oracle troubleshooting 必备的技能;在说锁和阻塞之前,我们先了解下 并发(concurrency)和并行(parallel)。并发意思是在数据库中有超过两个以上用户对同样的数 据做修改...

    解决System.Data.OracleClient 需要 Oracle 客户端软件 8.1.7 或更高版本

    标题中的“System.Data.OracleClient 需要 Oracle 客户端软件 8.1.7 或更高版本”是一个常见的错误提示,它涉及到在.NET环境中使用Oracle数据库时遇到的问题。这个错误表明,当你试图在应用程序中使用System.Data....

    oracle 非空闲等待事件详细解释

    oracle 非空闲等待事件详细解释 example db file single write 对数据文件头的更新,以刷新数据文件记录的检查点信息.

    oracle 创建视图为读.doc

    Oracle 创建视图为读 Oracle 创建视图为读是指在 Oracle 数据库中创建一个只读的视图,用于封装复杂的查询数据,使得数据更加简洁和易于管理。在本文中,我们将详细介绍如何创建 Oracle 视图、视图的应用场景、创建...

    百分百解决System.Data.OracleClient 需要 Oracle 客户端软件 8.1.7 或更高版本.rar

    本文将深入探讨“System.Data.OracleClient”组件以及它与Oracle客户端软件的关系,同时提供一个详细的解决方案来处理“需要Oracle客户端软件8.1.7或更高版本”的错误。 System.Data.OracleClient是.NET Framework...

    Oracle查看逻辑读、物理读资源占用排行的SQL语句

    本文将详细讲解这两个概念以及如何通过SQL语句来查看Oracle数据库中逻辑读和物理读资源的占用排行。 首先,我们来理解一下逻辑读和物理读的概念: 1. **逻辑读**:逻辑读是指当Oracle执行一个SQL语句时,从数据...

    Oracle 非安装客户端 for windows

    总的来说,Oracle非安装客户端为开发人员提供了一种简洁、高效的方式来连接和操作Oracle数据库,尤其是在需要快速部署和移动工作环境的情况下,其优势尤为明显。通过了解和熟练使用这些组件,你可以更便捷地完成日常...

    Python连接oracle工具cx_Oracle官方文档

    安装好cx_Oracle后,如果尚未安装Oracle客户端,那么还需要进行安装。cx_Oracle支持Oracle客户端版本12.2、12.1和11.2。 在官方文档中,我们能够了解到cx_Oracle的安装过程。cx_Oracle提供了多种安装方式,包括使用...

    oracle非数值列加和

    Oracle 数据库的几个常用函数,非数值列加和经常会用的到,常用于统计

    Centos7离线安装oracle11g所需要的rpm包下载 ,亲测可用

    在没有网络连接的环境下,离线安装Oracle 11g是一项挑战,因为通常需要通过网络下载大量的依赖包。然而,通过提前准备好的RPM包集合,可以实现离线安装。本篇将详细介绍如何在CentOS 7上离线安装Oracle 11g,以及...

    Oracle逻辑读写深入分析

    本文将围绕“Oracle逻辑读写深入分析”这一主题,结合描述及部分示例内容,全面解析Oracle数据库中逻辑读的概念、分类及其统计方法。 #### 逻辑读的定义与分类 逻辑读是指Oracle在执行查询或事务处理过程中,从...

    Veeam 备份恢复oracle数据库详细配置文档

    在开始备份恢复 Oracle 数据库之前,需要在 Oracle 源机和 Oracle 恢复目标机上设置好 host 解析。为此,需要在 Oracle 源机和 Oracle 恢复目标机上编辑 hosts 文件,添加相应的 host 解析信息。 二、推送 Oracle ...

    解决oracle下非oracle用户下执行sqlplus

    通常linux下用安装oracle后,只有切换至oracle用户下方可执行相关的数据库的sqlplus及imp\exp相关命令的操作,经过这里的此配置后方可解决此问题,所有用户下均可以执行上述相关操作

    vc++与Oracle

    要使用ODBC,首先需要在系统中安装Oracle的ODBC驱动,然后在ODBC管理器中配置数据源。 3. **VC++与ODBC连接**: - 在VC++项目中,可以使用`SQLDriverConnect`函数建立到ODBC数据源的连接。这需要提供DSN(数据源...

    Oracle 9i Client (Oracle 9i 客户端) 简化版 (不安装Oracle客户端,也可以使用PLSQL Developer)

    4、连接oracle服务器时需要手动配置tnsnames.ora,用户可参照tnsnames.ora中的注释部分进行配置。 5、本软件卸载时,将删除已安装的文件、环境变量及相应的注册表信息,绝对绿色! 注: 已安装了oracle的用户,如想...

    Oracle 数据库连接组件 不需要Oracle客户端

    我见过最好的Oracle数据库连接组件,性能和效率甚至比官方的高,最爽的是它不需要安装Oracle客户端即可连接数据库。赶快破解了献给大家,希望大家喜欢。使用方法:一、不用安装,直接使用破解文件夹中的组件,使用...

    SqlDbx连接oracle

    首先,连接Oracle数据库通常需要Oracle客户端软件,但这里提到的“Sqldbx连接oracle,不用安装client”,意味着SqlDbx可能通过一种无需完整Oracle客户端的方式来实现连接。这种连接方式通常依赖于Oracle的Instant ...

Global site tag (gtag.js) - Google Analytics