我们在应用程序中有一些持久数据,从服务器查询然后存储在数据库中,以便我们可以跟踪其他信息。因为我们不想查询何时在内存中使用对象,所以我们执行select for update
,以便阻止其他想要获取相同数据的线程。
我不确定select for update
如何处理不存在的行。如果该行不存在而另一个线程尝试在同一行上执行另一个select for update
,则该线程是否会被阻塞,直到另一个事务完成或者它是否也会得到一个空的结果集?如果它只获得一个空的结果集是否有任何方法可以阻止它,例如通过立即插入缺失的行?
修改
因为有一个评论,我们可能会锁定太多,这里有一些关于我们案例中具体用法的更多细节。在缩减的伪代码中,我们的程序流程如下所示:
d = queue.fetch();
r = SELECT * FROM table WHERE key = d.key() FOR UPDATE;
if r.empty() then
r = get_data_from_somewhere_else();
new_r = process_stuff( r );
if Data was present then
update row to new_r
else
insert new_r
此代码在多个线程中运行,从队列中提取的数据可能与数据库中的同一行有关(因此锁定)。但是,如果多个线程正在使用需要相同行的数据,则需要对这些线程进行顺序化(顺序无关紧要)。但是,如果该行不存在,则此顺序化将失败,因为我们没有锁定。
修改
现在我有以下解决方案,这对我来说似乎是一个丑陋的黑客。
select the data for update
if zero rows match then
insert some dummy data // this will block if multiple transactions try to insert
if insertion failed then
// somebody beat us at the race
select the data for update
do processing
if data was changed then
update the old or dummy data
else
rollback the whole transaction
我既不是百分百肯定,但这实际上解决了这个问题,这个解决方案似乎也不是很好的风格。因此,如果任何人必须提供更多可用的东西,这将是伟大的。
答案 0 :(得分:16)
我不确定如何选择更新来处理不存在的行。
没有。
如果你知道新行的独特之处,你可以做的最好的事情是使用咨询锁。 (如果需要,使用hashtext(),并使用表的oid来锁定它。)
下一个最好的事情是桌锁。
话虽如此,你的问题让你听起来比你应该更多地锁定方式。只在实际需要时锁定行,即写操作。
答案 1 :(得分:0)
查看第二次编辑中添加的代码,它看起来是正确的。
至于它看起来像黑客,有几个选项 - 基本上都是关于将数据库逻辑移动到数据库。
一个是简单地将整个选择用于更新,如果不存在则在函数中插入逻辑,而是执行select get_object(key1,key2,etc)
。
或者,您可以创建一个插入触发器,该触发器将忽略添加条目(如果已存在)的尝试,并且只需在执行select for update之前执行插入操作。但是,这确实有可能干扰现有的其他代码。
(如果我记得的话,我会稍后编辑并添加示例代码,当我能够检查我正在做什么时。)
答案 2 :(得分:0)
示例解决方案(我还没找到更好:/)
主题A:
BEGIN;
SELECT pg_advisory_xact_lock(42); -- database semaphore arbitrary ID
SELECT * FROM t WHERE id = 1;
DELETE FROM t WHERE id = 1;
INSERT INTO t (id, value) VALUES (1, 'thread A');
SELECT 1 FROM pg_sleep(10); -- only for race condition simulation
COMMIT;
主题B:
BEGIN;
SELECT pg_advisory_xact_lock(42); -- database semaphore arbitrary ID
SELECT * FROM t WHERE id = 1;
DELETE FROM t WHERE id = 1;
INSERT INTO t (id, value) VALUES (1, 'thread B');
SELECT 1 FROM pg_sleep(10); -- only for race condition simulation
COMMIT;
导致事务执行顺序始终正确。