Jeśli nie trzeba znać wartość licznika gdy go ustawić górny odpowiedź jest zdecydowanie najlepszym:
counter = Counter.objects.get_or_create(name = name)
counter.count = F('count') + 1
counter.save()
mówi to baza danych, aby dodać 1 do wartości count
, która może doskonale działać bez blokowania innych operacji. Wadą jest to, że nie masz możliwości dowiedzenia się, co właśnie ustawiłeś. Jeśli dwa wątki jednocześnie uderzyłyby w tę funkcję, obaj zobaczyliby tę samą wartość i obaj powiedzieliby dbowi o dodaniu 1. Baza danych zakończy się dodaniem 2 zgodnie z oczekiwaniami, ale nie będzie wiadomo, która z nich poszła pierwsza.
Jeśli teraz dbasz o liczbę, możesz skorzystać z opcji select_for_update
, do której odwołuje się Emil Stenstrom. Oto, jak to wygląda:
from models import Counter
from django.db import transaction
@transaction.atomic
def increment_counter(name):
counter = (Counter.objects
.select_for_update()
.get_or_create(name=name)[0]
counter.count += 1
counter.save()
To odczytuje bieżącą wartość i blokuje pasujące wiersze do końca transakcji. Teraz tylko jeden pracownik może czytać na raz. Więcej informacji na temat select_for_update można uzyskać pod numerem the docs.
Jakiej bazy danych używasz? –
Dla mnie wygląda na to, że nie należy używać '+ =', aby uniknąć warunków wyścigu. Użytkownicy Pythona powinni już wiedzieć, że istnieje różnica między "a + = b" i "a = a + b", więc dlaczego nie skorzystać z tego? Może to będzie sprzeczne z niektórymi danymi z pamięci podręcznej? Niepewny. – aliqandil