如何使用DBIx ::类:: ResultSet中的find_or_create方法时,为了避免竞争

2019-09-16 16:23发布

从文档find_or_create

注意:由于find_or_create()从数据库中读出,然后可能根据该结果插入,该方法是受争用条件。 另一种方法可以在表中创建一个记录查找完成后和前创建已启动。 为了避免此问题,使用find_or_create()在事务内。

它是不够的,只是使用find_or_create() PostgreSQL中事务中?

Answer 1:

不,文档不正确。 单独使用交易回避这个问题。 它只是保证了整个事务回滚如果发生异常 - 所以没有不一致的状态将持久化到数据库。

为了避免这个问题,你必须锁定表-在一个事务中,因为所有的锁在事务结束时释放。 就像是:

BEGIN;
LOCK TABLE mytbl IN SHARE MODE;

-- do your find_or_create here

COMMIT;

但是,这不是一个神奇的治愈一切。 它可以成为一个性能问题,并且有可能是死锁 (相互试图锁定了另外一个已经锁定资源并发事务)。 PostgreSQL将侦测这样的条件并取消所有,但竞争的事务之一。 你必须准备重试失败的操作。

PostgreSQL的手册有关锁。

如果你没有很多并发的也可能只是忽略的问题。 时隙是非常小的,因此只有很少实际情况。 如果赶上重复键冲突错误,这将不伤害,那么你已经把这个。



Answer 2:

此实现find_or_create应防止竞争条件,在OP描述:

eval {
    $row = $self->model->create( { ... } );
}
if($@ && $@ =~ /duplicate/i) {
   $row = $self->model->find( { ... } );
} 

它也能减少find_or_create()在最好的情况下,单一的查询。



文章来源: How to avoid race conditions when using the find_or_create method of DBIx::Class::ResultSet?