使用DBIx :: Class :: ResultSet的find_or_create方法时如何避免竞争条件?

Eug*_*ash 5 database postgresql perl dbix-class

从以下文档find_or_create:

注意:因为find_or_create()从数据库中读取,然后可能根据结果插入,所以此方法受竞争条件限制.另一个进程可以在查找完成之后和创建开始之前在表中创建记录.要避免此问题,请在事务中使用find_or_create().

仅仅find_or_create()在PostgreSQL中使用一个事务就足够了吗?

Erw*_*ter 6

不,文档不正确.仅使用事务并不能避免此问题.它只保证在发生异常时回滚整个事务 - 这样就不会将不一致的状态保存到数据库中.

避免此问题,您必须锁定表 - 在事务内部,因为所有锁都在事务结束时释放.就像是:

BEGIN;
LOCK TABLE mytbl IN SHARE MODE;

-- do your find_or_create here

COMMIT;
Run Code Online (Sandbox Code Playgroud)

但这并不是治愈一切的神奇疗法.它可能会成为性能问题,并且可能存在死锁(并发事务相互试图锁定另一个已锁定的资源).PostgreSQL将检测到这种情况,并取消除一个竞争交易之外的所有交易.您必须准备好在失败时重试操作.

关于锁的PostgreSQL手册.

如果您没有很多并发性,那么您也可以忽略该问题.时间段非常小,所以实际上很少发生.如果您发现重复的密钥违规错误,这不会造成任何伤害,那么您也已经涵盖了这一点.

  • 其他有用的页面.文档:http://www.postgresql.org/docs/current/interactive/mvcc.html PostgreSQL 9.1或更高版本的可序列化实现:http://wiki.postgresql.org/wiki/SSI其他隔离级别或PostgreSQL版本:http ://www.postgresql.org/files/developer/concurrency.pdf (2认同)