我經歷的ActiveRecord的競爭條件與PostgreSQL的,我正在讀的值,然後遞增,並插入一個新的記錄:PostgreSQL和ActiveRecord的子查詢的競爭條件
num = Foo.where(bar_id: 42).maximum(:number)
Foo.create!({
bar_id: 42,
number: num + 1
})
大規模,多線程會同時讀取然後寫入相同的值number
。將它包裝在事務中並不能解決競爭條件,因爲SELECT不鎖定表。我不能使用自動增量,因爲number
不是唯一的,只有在給定bar_id
時纔是唯一的。我看到3級可能的解決方法:(?行級鎖)
- 明確地使用一個Postgres鎖定
- 採用獨特的約束和重試失敗
覆蓋保存使用子查詢(呸!) ,IE
INSERT INTO foo (bar_id, number) VALUES (42, (SELECT MAX(number) + 1 FROM foo WHERE bar_id = 42));
所有這些解決方案看起來像我會重新實現的ActiveRecord::Base#save!
大部分地區是否有更簡單的方法?
更新: 我想我找到了答案與Foo.lock(true).where(bar_id: 42).maximum(:number)
但使用SELECT FOR UDPATE
這是不允許的聚合查詢
更新2:我剛剛得到了廣大DBA通知 ,即使我們能做INSERT INTO foo (bar_id, number) VALUES (42, (SELECT MAX(number) + 1 FROM foo WHERE bar_id = 42));
不解決任何事情,因爲在選擇不同的鎖比INSERT運行
如果它在飛行中計算,它會隨時間而改變,並且會受到相同的競爭條件 –