我们正在使用occi来通过C ++进程访问Oracle 12。其中一个操作必须确保客户端必须在数据库中选择最新数据并根据最新值进行操作。声明是
std::string sqlStmt = "SELECT REF(a) FROM O_RECORD a WHERE G_ID= :1 AND P_STATUS IN (:2, :3) FOR UPDATE OF PL_STATUS"
(我们正在使用TYPES)。由于某种原因,此命令没有通过,数据库表是LOCKED。所有其他操作都在等待第一个线程完成,但线程被杀死,我们已经到达了一个deadend。
避免这种灾难性事件的最佳解决方案是什么?我可以在语句中设置超时,以便线程可以在“select for update”上运行100%,让我们说最多10秒?换句话说,执行线程可以锁定数据库表/行但不超过预定时间。
这可能吗?
答案 0 :(得分:4)
会话参数ddl_lock_timeout
但没有 dml_lock_timeout 。所以你不能这样走。所以要么你必须使用
SELECT REF(a)
FROM O_RECORD a
WHERE G_ID= :1 AND P_STATUS IN (:2, :3)
FOR UPDATE OF PL_STATUS SKIP LOCKED
修改应用程序逻辑。或者您可以实现自己的中断机制。只需触发一个并行线程,一段时间后执行OCIBreak
。它是文档化和支持的解决方案。调用OCIBreak
是线程安全的。被阻止的SELECT .. FOR UPDATE
语句将被释放,您将收到错误ORA-01013: user requested cancel of current operation
因此,在OCCI级别,您将不得不处理此错误。
答案 1 :(得分:4)
修改:添加了资源管理器,这可以施加更精确的限制,只关注那些阻止他人的会话......
资源管理器允许定义比配置文件可用的更复杂的策略,在您的情况下比后者更合适。
您必须定义计划以及与计划关联的用户组,必须指定与计划/组关联的策略,最后必须将用户附加到组。要了解如何执行此操作,您可以重复使用此example @support.oracle.com(此处似乎有点太长,无法在此处发布),而是将MAX_IDLE_TIME
替换为MAX_IDLE_BLOCKER_TIME
。
核心线将是
dbms_resource_manager.create_plan_directive(
plan => 'TEST_PLAN',
group_or_subplan => 'my_limited_throttled_group',
comment => 'Limit blocking idle time to 300 seconds',
MAX_IDLE_BLOCKER_TIME => 300)
;
您可以限制指定IDLE_TIME
的会话的非活动期。
如果用户超出
CONNECT_TIME
或IDLE_TIME
会话资源限制,则数据库将回滚当前事务并结束会话。当用户进程下一次发出调用时,数据库将返回错误
为此,请指定具有maximux空闲时间的配置文件,并将其应用于相关用户(这样您就不会影响所有用户或应用程序)
CREATE PROFILE o_record_consumer
LIMIT IDLE_TIME 2; --2 minutes timeout
alter user the_record_consumer profile o_record_consumer;
缺点是此设置是会话范围的,因此如果同一会话在其他操作过程中应该保持空闲,则无论如何都将强制执行此策略。
也许您已经知道其他会话可以通过多种方式协调他们对同一记录的访问:
FOR UPDATE WAIT x;
如果将WAIT x
子句附加到select for update
语句,则等待会话将在“x”秒过后放弃等待。 (整数“x”必须在那里硬编码,例如值“3”;变量不会,至少在Oracle 11gR2中)。SKIP LOCKED;
如果将SKIP LOCKED
子句附加到select for update
语句,则select将不会返回已锁定的记录(已指向ibre5041)。 最后,您可能正在尝试实施自制的队列基础结构。在这种情况下,请记住Oracle已经拥有自己的名为Advanced Queue的队列机制,只需使用它们就可以获得很多东西;见ORACLE-BASE - Oracle Advanced Queuing。