如何使“选择更新”超时 在 Oracle 中使用 Perl DBI

发布于 2024-07-23 06:05:47 字数 1785 浏览 11 评论 0原文

有没有一种简单的方法可以让 SQL 语句超时,这样它就会失败而不是等待(例如传递空结果集或错误消息或其他任何内容),这样我就可以让作业的资源预留失败,并且给另一个人一个机会吗? 我正在寻找一些迄今为止我忽略的 DBI 选项; 向自己发送 SIGALRM 来自杀并不是我想要的(尽管如果必须的话我可能不得不诉诸于此)。

截取的代码是伪化的,并且被缩短到了极致,但我希望您能明白其中的意思。

my $sql = "SELECT one, two, three FROM sometable WHERE this = ? AND that = ?";
my $sth = $self->make_handle( $sql );
eval {
    foreach my $this ( sort keys %needed_ressources ) {
        # vvv This is where the idle time is spent vvv
        $sth->execute( $this, $that ) or die( "DB connection gone?!" );
        # ^^^ This is where the idle time is spent ^^^
        my ( $one, $two, $three ) = $sth->fetchrow_array();
        unless( $one ) { # undefined record set == not found
            $self->{DB_HANDLE}->rollback();
            die( "$this not defined for $that!" );
        }
    }
    # If we haven't died so far, we can move on
    foreach... #similar loop here doing the actual update statement
    $self->{DB_HANDLE}->commit();
};
return( 1 ) unless $@;
return( undef );

以下是感兴趣的详细信息:

在进行大规模并行数字运算的应用程序中,有一个使用预言机表实现的资源锁定机制。 每个作业需要锁定多个用于读取的资源和/或多个用于写入的资源,并且只有在成功获取所有锁后才能启动。 作业不应耐心等待资源被释放,而应该失败并由其主控稍后重新运行(这可以保持开放事务的数量较低,同时通过在实际紧缩的情况下提供更多作业来提高性能)。

当然,在实际更新表之前,使用“SELECT ... FOR UPDATE”语句保留每一行,因此 Oracle 使用行级锁定,并且表上可以发生并发事务。 为了进一步减少可能的竞争条件和死锁,所有作业首先选择其资源行,然后在执行更新之前使用相同的顺序对它们进行行锁定。

截至目前的实施,这在大多数情况下都可以正常工作。 但是,因为“选择更新”会阻塞,直到 Oracle 实际授予行锁,所以仍然可能会发生作业空闲等待其资源的情况,我希望这些资源能够更好地利用可用的 CPU 能力。 等待一两秒是可以的,但不能只是为了锁定而等待十秒或更长时间。 对于稍后解锁锁定,当然需要等待,因此将整个数据库连接设置为仅接受立即结果是行不通的。

我总是很感激RTFM的答案,只要它们指出M中我应该TFR<的位置/strong> ;-))

提前非常感谢,
奥尔凡

is there an easy way to timeout an SQL statement so that it will fail instead of waiting (e.g. deliver an empty result set or an error message or whatever else) so I can let a job's ressource reservation fail and give another one a chance? I'm looking for some DBI option I've overlooked so far; sending SIGALRMs to myself to commit suicide is rather not what I have in mind (although I might have to resort to that if I had to).

The code snipped is pseudo-ized and shortened to the extreme, but I hope you'll catch the drift.

my $sql = "SELECT one, two, three FROM sometable WHERE this = ? AND that = ?";
my $sth = $self->make_handle( $sql );
eval {
    foreach my $this ( sort keys %needed_ressources ) {
        # vvv This is where the idle time is spent vvv
        $sth->execute( $this, $that ) or die( "DB connection gone?!" );
        # ^^^ This is where the idle time is spent ^^^
        my ( $one, $two, $three ) = $sth->fetchrow_array();
        unless( $one ) { # undefined record set == not found
            $self->{DB_HANDLE}->rollback();
            die( "$this not defined for $that!" );
        }
    }
    # If we haven't died so far, we can move on
    foreach... #similar loop here doing the actual update statement
    $self->{DB_HANDLE}->commit();
};
return( 1 ) unless $@;
return( undef );

Here are the gory details for the interested:

In an application that does massively parallel numbercrunching there is a ressource locking mechanism implemented that uses an oracle table. Each job needs to lock a number of ressources for reading and/or a number of ressources to write to, and can only start if all the locks have been acquired successfully. Instead of patiently waiting for ressources to be freed, jobs should just fail and be re-run later by their master (this keeps the number of open transactions low while boosting performance by having more jobs around that actually crunch).

Of course, before actually updating the table, each row is being reserved using a "SELECT ... FOR UPDATE" statement so Oracle uses row level locking and concurrent transactions can happen on the table. In order to further reduce possible race conditions and deadlocks all jobs first select their ressource rows and then row lock them using the same ordering before performing the update.

As of the current implementation this works fine in most of the cases. But, because the "Select for update" blocks until Oracle actually grants the row lock, it may still happen that a job is idle waiting for its ressources, and I'm after those to better utilize the available CPU power. It's OK to wait a second or two, but not ten or more just for the locking. For unlocking later, waiting is required of course, so setting the whole DB connection to only accept immediate results will not work.

I'm always grateful for RTFM answers as long as they point to the location in the M that I should TF have R ;-))

Thanks a lot in advance,
Olfan

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

我偏爱纯白色 2024-07-30 06:05:47

我认为您需要 FOR UPDATE 子句上的 NOWAIT 参数。 如果无法锁定记录,则选择将失败(“ORA-00054:资源繁忙并指定了NOWAIT”)
您可以根据需要处理异常。 查看SQL 参考手册 。 它是针对 11g 的,但现在几个版本的语法都没有改变。

另一种选择是给出一段等待时间:“FOR UPDATE WAIT 3”等待 3 秒以获取锁,而不是立即失败。

I think you want the NOWAIT parameter on the FOR UPDATE clause. If the record cannot be locked, the select will fail ("ORA-00054: resource busy and acquire with NOWAIT specified")
and you can handle the exception however you need to. Check out the SQL Reference manual. It's for 11g, but the syntax hasn't changed for several versions now.

Another option is to give a time to wait: "FOR UPDATE WAIT 3" to wait 3 seconds for the lock to be acquired rather than failing immediately.

折戟 2024-07-30 06:05:47

事实上,SIGALRM 可能并没有那么糟糕。 此处列出了一些选项。

Actually, SIGALRM may not be so bad after all. There are some options listed here.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文