【发布时间】:2014-01-05 12:26:29
【问题描述】:
我想由某个用户锁定一行,直到他无限期地使用这一行,并且他必须在完成后解锁它。因此,任何其他用户将无法为自己锁定此行。可以在数据库级别上做吗?
【问题讨论】:
标签: atomic postgresql-9.2 interlocked
我想由某个用户锁定一行,直到他无限期地使用这一行,并且他必须在完成后解锁它。因此,任何其他用户将无法为自己锁定此行。可以在数据库级别上做吗?
【问题讨论】:
标签: atomic postgresql-9.2 interlocked
您可以使用长期事务来执行此操作,但这样做会出现性能问题。这听起来更像是乐观并发控制的工作。
您可以打开一个事务并执行SELECT 1 FROM mytable WHERE clause to match row FOR UPDATE;。然后保持交易开放,直到你完成。这样做的问题是,它可能会导致真空问题,从而导致表和索引膨胀,其中表被删除的数据填满,而索引则被指向过时块的条目填满。
最好使用advisory lock。您仍然必须保持连接保持锁打开,但它不必保持打开的空闲事务,因此影响要小得多。但是,希望更新行的事务必须显式检查冲突的咨询锁,否则它们可以继续进行,就好像它没有被锁定一样。这种方法对于大量表(由于有限的咨询锁命名空间)或大量并发锁(由于连接数)的扩展性也很差。
如果您不能确保您的客户端应用始终明确获得咨询锁,您可以使用触发器检查咨询锁并等待它。但是,这可能会造成死锁问题。
因此,最好的方法可能是有一个记录用户 ID 的locked_by 字段和一个记录锁定时间的locked_time 字段。在应用程序级别和/或使用触发器执行此操作。要处理获取锁的并发尝试,您可以使用optimistic concurrency control 技术,其中设置locked_by 和locked_time 的UPDATE 上的WHERE 子句如果其他人先到达那里将不匹配,因此行数将是零,你会知道你失去了锁的比赛,必须重新检查。 WHERE 子句通常测试 locked_by 和 locked_time。所以你会写这样的东西:
UPDATE t
SET locked_by = 'me' AND locked_time = current_timestamp
WHERE locked_by IS NULL AND locked_time IS NULL
AND id = [ID of row to update];
(这是一种用于获取锁的简化乐观锁定模式,您不介意其他人是否加入并执行了整个事务。如果您想要更严格的排序,则使用行版本列或检查last_modified 列没有改变。)
【讨论】: