当前位置: 首页 > 面试题库 >

带有锁定的Postgresql的一致性,并选择进行更新

诸葛令
2023-03-14
问题内容

我有一个可以支持一定数量的并发操作的应用程序。这由postgres中的“插槽”表表示。当节点联机时,它们会在表中插入许多行,每个插槽一个。当作业声明插槽时,它们会更新表中声明其中一个插槽的行,并在完成时再次释放它。

插槽表如下所示:

CREATE TABLE slots (
    id INT8 PRIMARY KEY DEFAULT nextval('slots_seq'),
    node_name TEXT NOT NULL,
    job_name TEXT
);

在任何时候,它都有一些固定的行数,每行可以或可以不填写job_name。

当新作业要启动时,它将运行以下查询以获取应在其上运行的节点的名称:

BEGIN;
LOCK TABLE slots IN ACCESS EXCLUSIVE MODE;
SELECT id, node_name
    FROM slots
    WHERE job_name IS NULL
    LIMIT 1
    FOR UPDATE;

(从游标中读取出node_name和id)

UPDATE slots
    SET job_name = %(job_name)s
    WHERE id = %(slot_id)s;
COMMIT;

这通常可以声明行而不会丢失任何更新,但是并发级别更高,在执行了许多SELECT … FOR
UPDATE和UPDATE查询的同时,仅声明了几行。最终结果是,我们最终运行的作业要多于其可用的插槽。

我在锁定错误吗?有更好的方法来解决这个问题吗?什么不使用表锁?

事务级别SERIALIZABLE不会削减它,仅填充了几行。

我正在使用PostgreSQL版本8.4。


问题答案:

好吧,我用perl编写了一个程序来模拟正在发生的事情,因为我认为您所说的是不可能的。确实,运行模拟后,即使关闭锁定,我也没有任何问题(因为SELECT FOR UPDATE并且UPDATE应该进行必要的锁定)。

我在PG 8.3和PG 9.0上运行了它,并且在两个位置上都能正常工作。

我敦促您尝试该程序和/或尝试使用python版本,以拥有一个不错的紧密测试用例,您可以与该类共享该用例。如果有效,您可以调查差异之间的区别,如果无效,则可以与其他人一起玩。

#!/usr/bin/perl
use DBI;
$numchild = 0;
$SIG{CHLD} = sub { if (wait) {$numchild--;} };

sub worker($)
{
  my ($i) = @_;
  my ($job);

  my $dbh = DBI->connect("dbi:Pg:host=localhost",undef,undef,{'RaiseError'=>0, 'AutoCommit'=>0});

  my ($x) = 0;
  while(++$x)
  {
#    $dbh->do("lock table slots in access exclusive mode;") || die "Cannot lock at $i\n";
    my @id = $dbh->selectrow_array("select id from slots where job_name is NULL LIMIT 1 FOR UPDATE;");

    if ($#id < 0)
    {
      $dbh->rollback;
      sleep(.5);
      next;
    }
    $job = "$$-$i-($x)";
    $dbh->do("update slots set job_name='$job' where id=$id[0];") || die "Cannot update at $i\n";
    $dbh->commit || die "Cannot commit\n";
    last;
  }
  if (!$job)
  {
    print STDERR "Could not find slots in 5 attempts for $i $$\n" if ($ENV{'verbose'});
    return;
  }
  else
  {
    print STDERR "Got $job\n" if ($ENV{'verbose'} > 1);
  }
  sleep(rand(5));

#  $dbh->do("lock table slots in access exclusive mode;") || die "Cannot lock at $i\n";
  $dbh->do("update slots set usage=usage+1, job_name = NULL where job_name='$job';") || die "Cannot unlock $job";
  print STDERR "Unlocked $job\n" if ($ENV{'verbose'} > 2);
  $dbh->commit || die "Cannot commit";
}

my $dbh = DBI->connect("dbi:Pg:host=localhost",undef,undef,{'RaiseError'=>0, 'AutoCommit'=>0});

$dbh->do("drop table slots;");
$dbh->commit;
$dbh->do("create table slots (id serial primary key, job_name text, usage int);") || die "Cannot create\n";
$dbh->do("insert into slots values (DEFAULT,NULL,0), (DEFAULT,NULL,0), (DEFAULT,NULL,0), (DEFAULT,NULL,0), (DEFAULT,NULL,0), (DEFAULT,NULL,0), (DEFAULT,NULL,0), (DEFAULT,NULL,0), (DEFAULT,NULL,0), (DEFAULT,NULL,0);") || die "Cannot insert";
$dbh->commit;

for(my $i=0;$i<200;$i++)
{
  if (!fork)
  {
    worker($i);
    exit(0);
  }

  if (++$numchild > 50)
  {
    sleep(1);
  }
}
while (wait > 0)
{
  $numchild--;
  print "Waiting numchild $numchild\n";
  sleep(1);
}
my $dbh = DBI->connect("dbi:Pg:host=localhost",undef,undef,{'RaiseError'=>0, 'AutoCommit'=>0});
my $slots = $dbh->selectall_arrayref("select * from slots;") || die "Cannot do final select";
my $sum=0;
foreach my $slot (@$slots)
{
  printf("%02d %3d %s\n",$slot->[0], $slot->[2], $slot->[1]);
  $sum += $slot->[2];
}
print "Successfully made $sum entries\n";


 类似资料:
  • 问题内容: 我需要编写一个过程,该过程将允许我选择x数量的行,并同时更新这些行,以便调用应用程序将知道这些记录已锁定并正在使用。我在表中有一个名为“锁定”的列。下次调用该过程时,它将仅提取下一个x数量未选中“锁定”列的记录。我已经阅读了一些有关SQL Server的OUTPUT方法的信息,但是不确定这是我想要做的。 问题答案: 使用OUTPUT投票表决Cade Roux的答案: 之前: 这是我可以

  • 我有一个用户批次表。我只想选择,直到我的总金额达到一定金额。 考虑以下查询: 查询结果为: 我想在表格上做一个选择,直到余额总数为6。然后只返回id 1,2: 余额总计为1的另一个示例。那么应该只返回ids 1: 余额合计为11的示例。应该只返回ids,3,4: 所以,在那之后,我需要用FORUPDATE ex锁定这些行: 我尝试了窗口功能,但它不允许锁定(更新)。感谢任何帮助。

  • 我有一个表和几个线程(系统),它们从表中读取特定列并更新它。因此,我的问题如下: 我想确保在任何时候只有一个线程可以选择和更新该列。最好的解决方案是什么? PS:据我所知,在SQL Server中使用事务会锁定事务中的所有资源,但我对PostgreSQL一无所知。 非常感谢。

  • 这是我的伪代码: 我想问,如果条件等于“key”的行已经被删除,那么“select for update”阻止的锁是否可以自动解锁,这意味着如果另一个进程在此点进入并选择相同的“key”,它就不能被此进程阻止?

  • 对于我的Hi/Lo实现,我需要一个函数来获取独占表锁、更新值并选择一行。我想出了以下代码: 但是,在调用该函数时,它返回的不是一行,而是一列,内容类似于“未命名门户3”。我想我应该迭代返回的ref(但如何)? 我可以使用的另一种方法是使用UPDATE RETURNING语句,但是我不确定在这种情况下是否会出现竞争情况。任何帮助都将不胜感激。谢谢

  • 问题内容: 我正在寻找以下解决方案。我将数据存储在表中,以跟踪客户希望在数据库中使用的特殊增量号。这是他们内部使用的特殊号码。 我要选择的是自动在表格中递增此数字。因此,我没有其他交易的问题,该交易是由使用该系统的其他人使用相同的ID号码进行的。 因此,我想选择当前数字并立即将其增加一个,这样就不会重复。如果可能的话,我将如何去做? 问题答案: