Twoje opcje to:
-
Uruchom w
SERIALIZABLEizolacja. Transakcje współzależne zostaną przerwane po zatwierdzeniu, ponieważ wystąpiły niepowodzenie serializacji. Otrzymasz dużo spamu w dziennikach błędów i będziesz wykonywać wiele ponownych prób, ale będzie działać niezawodnie. -
Zdefiniuj
UNIQUEograniczenie i ponowna próba w przypadku niepowodzenia, jak zauważyłeś. Takie same problemy jak powyżej. -
Jeśli istnieje obiekt nadrzędny, możesz
SELECT ... FOR UPDATEobiekt nadrzędny przed wykonaniemmaxzapytanie. W tym przypadkuSELECT 1 FROM bar WHERE bar_id = $1 FOR UPDATE. Używaszbarjako blokada dla wszystkichfoos z tymbar_id. Możesz wtedy wiedzieć, że kontynuowanie jest bezpieczne, o ile każde zapytanie, które wykonuje przyrost licznika, robi to niezawodnie. To może działać całkiem dobrze.To nadal tworzy agregujące zapytanie dla każdego wywołania, które (na następną opcję) jest niepotrzebne, ale przynajmniej nie spamuje dziennika błędów, jak powyższe opcje.
-
Użyj stolika licznikowego. To właśnie bym zrobił. Albo w
barlub w tabeli bocznej, takiej jakbar_foo_counter, uzyskaj identyfikator wiersza za pomocąUPDATE bar_foo_counter SET counter = counter + 1 WHERE bar_id = $1 RETURNING counterlub mniej wydajna opcja, jeśli Twój framework nie obsługuje
RETURNING:SELECT counter FROM bar_foo_counter WHERE bar_id = $1 FOR UPDATE; UPDATE bar_foo_counter SET counter = $1;Następnie w tej samej transakcji , użyj wygenerowanego wiersza licznika jako
number. Po zatwierdzeniu wiersz tabeli liczników dla tegobar_idzostanie odblokowany do użycia w następnym zapytaniu. Jeśli cofniesz, zmiana zostanie odrzucona.
Polecam podejście licznikowe, używając dedykowanej tabeli bocznej dla licznika zamiast dodawania kolumny do bar . Model jest czystszy i oznacza, że w bar tworzysz mniej wzdęć aktualizacji , co może spowolnić zapytania do bar .