Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
In dit artikel wordt uitgelegd hoe u transactieafhandelings- en isolatieniveaus configureert voor Django-toepassingen met behulp van de mssql-django back-end met SQL Server.
Standaardgedrag
Django werkt standaard in de autocommit-modus. Elke databasequery wordt uitgevoerd in een eigen transactie en onmiddellijk vastgelegd. U kunt dit gedrag wijzigen met behulp van de AUTOCOMMIT instelling of de API voor transactiebeheer van Django.
AUTOCOMMIT-instelling
Stel AUTOCOMMIT in op False in uw databaseconfiguratie om de autocommitmodus uit te schakelen:
DATABASES = {
"default": {
"ENGINE": "mssql",
"NAME": "<your-database>",
"USER": "<your-username>",
"PASSWORD": "<your-password>",
"HOST": "<your-server>",
"PORT": "1433",
"AUTOCOMMIT": False,
"OPTIONS": {
"driver": "ODBC Driver 18 for SQL Server",
},
},
}
Note
Als u autotoewijzing uitschakelt, moet u expliciet transacties doorvoeren of terugdraaien. De meeste Django-toepassingen laten autocommit ingeschakeld en gebruiken transaction.atomic() voor specifieke bewerkingen.
Transaction.atomic() gebruiken
Verpak databasebewerkingen in transaction.atomic() zodat ze in één transactie worden uitgevoerd:
from django.db import transaction
from myapp.models import Account
def transfer_funds(from_account_id, to_account_id, amount):
with transaction.atomic():
sender = Account.objects.select_for_update().get(pk=from_account_id)
receiver = Account.objects.select_for_update().get(pk=to_account_id)
sender.balance -= amount
receiver.balance += amount
sender.save()
receiver.save()
Als er een uitzondering optreedt binnen een atomic() blok, wordt de hele transactie teruggedraaid.
Geneste transacties
Django ondersteunt geneste atomic() blokken via de savepoints van SQL Server:
from django.db import transaction
with transaction.atomic():
# Outer transaction
Product.objects.create(name="Widget A", price=9.99)
try:
with transaction.atomic():
# Inner savepoint
Product.objects.create(name="Widget B", price=14.99)
raise ValueError("Simulated error")
except ValueError:
pass # Inner savepoint is rolled back, outer continues
# Widget A is committed, Widget B is not
Isolatieniveaus voor transacties
Configureer het niveau van transactieisolatie met behulp van de isolation_level optie in uw databaseconfiguratie:
DATABASES = {
"default": {
"ENGINE": "mssql",
"NAME": "<your-database>",
"USER": "<your-username>",
"PASSWORD": "<your-password>",
"HOST": "<your-server>",
"PORT": "1433",
"OPTIONS": {
"driver": "ODBC Driver 18 for SQL Server",
"isolation_level": "READ COMMITTED",
},
},
}
Ondersteunde isolatieniveaus
| Isolatieniveau | Description |
|---|---|
READ UNCOMMITTED |
Staat vuile leesbewerkingen toe. Laagste isolatie, hoogste gelijktijdigheid. |
READ COMMITTED |
SQL Server standaardinstelling. Voorkomt vuile leesbewerkingen. |
REPEATABLE READ |
Voorkomt vuile en niet-herhaalbare leesbewerkingen. |
SNAPSHOT |
Maakt gebruik van rijversies voor consistente leesacties zonder blokkeringen. Vereist dat momentopname-isolatie op databaseniveau is ingeschakeld. |
SERIALIZABLE |
Hoogste isolatie. Voorkomt fantoomlezingen. |
Momentopname-isolatie inschakelen
Als u isolatie wilt gebruiken SNAPSHOT , schakelt u deze eerst in op de database:
ALTER DATABASE [<your-database>]
SET ALLOW_SNAPSHOT_ISOLATION ON;
Configureer deze vervolgens in settings.py:
DATABASES = {
"default": {
"ENGINE": "mssql",
"NAME": "<your-database>",
"USER": "<your-username>",
"PASSWORD": "<your-password>",
"HOST": "<your-server>",
"PORT": "1433",
"OPTIONS": {
"driver": "ODBC Driver 18 for SQL Server",
"isolation_level": "SNAPSHOT",
},
},
}
Gebruik de @transaction.atomic decorator
Transacties toepassen op volledige weergavefuncties:
from django.db import transaction
from django.http import JsonResponse
@transaction.atomic
def create_order(request):
# All database operations in this view run in a single transaction
order = Order.objects.create(customer_id=request.user.id)
for item in request.POST.getlist("items"):
OrderItem.objects.create(order=order, product_id=item)
return JsonResponse({"order_id": order.pk})
Gegevens lezen zonder te blokkeren (NOLOCK-equivalent)
Een veelvoorkomend verzoek is om SQL Server te bevragen met de hint NOLOCK of de isolatie-instelling READ UNCOMMITTED om blokkeringen op drukbezette tabellen te voorkomen. Django's ORM genereert geen tabelhints, maar u hebt twee opties.
Optie 1: READ UNCOMMITTED per verbinding instellen
Stel het isolatieniveau READ UNCOMMITTED in op een toegewezen alleen-lezen-databasealias om deze toe te passen op alle query's op die verbinding:
DATABASES = {
"default": {
"ENGINE": "mssql",
"NAME": "<your-database>",
"HOST": "<your-server>",
"PORT": "1433",
"OPTIONS": {
"driver": "ODBC Driver 18 for SQL Server",
},
},
"read_uncommitted": {
"ENGINE": "mssql",
"NAME": "<your-database>",
"HOST": "<your-server>",
"PORT": "1433",
"OPTIONS": {
"driver": "ODBC Driver 18 for SQL Server",
"isolation_level": "READ UNCOMMITTED",
},
},
}
Routeer vervolgens query's naar de read_uncommitted alias:
# Read with NOLOCK-equivalent behavior
products = Product.objects.using("read_uncommitted").filter(active=True)
# Writes still go through the default connection
Product.objects.create(name="Widget", price=9.99)
Optie 2: onbewerkte SQL gebruiken met NOLOCK
Voor gerichte query's op specifieke tabellen gebruikt u onbewerkte SQL met de NOLOCK tabelhint:
from django.db import connection
with connection.cursor() as cursor:
cursor.execute("SELECT id, name, price FROM myapp_product WITH (NOLOCK) WHERE active = %s", [1])
rows = cursor.fetchall()
Waarschuwing
Zowel READ UNCOMMITTED als NOLOCK vuile leesbewerkingen toestaan, wat betekent dat query's gegevens van niet-doorgevoerde transacties kunnen retourneren. Gebruik deze technieken alleen voor rapportage- of analysequery's waarbij geen absolute consistentie is vereist.
Optie 3: Gebruik in plaats daarvan SNAPSHOT-isolatie
SNAPSHOT isolatie biedt consistente leesbewerkingen zonder blokkeren en zonder vuile leesbewerkingen. Het is het aanbevolen alternatief voor NOLOCK voor de meeste workloads:
DATABASES = {
"default": {
"ENGINE": "mssql",
"NAME": "<your-database>",
"HOST": "<your-server>",
"PORT": "1433",
"OPTIONS": {
"driver": "ODBC Driver 18 for SQL Server",
"isolation_level": "SNAPSHOT",
},
},
}
SNAPSHOT vereist configuratie op databaseniveau. Zie SNAPSHOT-isolatie inschakelen.
Vergrendeling op rijniveau met select_for_update()
Django's select_for_update() wordt volledig ondersteund door de mssql-django back-end. SQL Server implementeert dit met behulp van tabelhints in plaats van de FOR UPDATE component die door andere databases wordt gebruikt.
Basaal gebruik
from django.db import transaction
with transaction.atomic():
product = Product.objects.select_for_update().get(pk=1)
product.stock -= 1
product.save()
De back-end genereert: SELECT ... FROM [myapp_product] WITH (ROWLOCK, UPDLOCK) WHERE ...
NOWAIT en SKIP LOCKED
De nowait parameters en skip_locked parameters worden beide ondersteund:
from django.db import transaction
# Raise DatabaseError immediately if the row is already locked
with transaction.atomic():
product = Product.objects.select_for_update(nowait=True).get(pk=1)
# Skip rows that are locked by other transactions
with transaction.atomic():
available = Product.objects.select_for_update(skip_locked=True).filter(
reserved=False
)[:10]
| Parameter | Hint voor SQL Server-tabel |
|---|---|
| Default | WITH (ROWLOCK, UPDLOCK) |
nowait=True |
WITH (NOWAIT, ROWLOCK, UPDLOCK) |
skip_locked=True |
WITH (ROWLOCK, UPDLOCK, READPAST) |
Note
select_for_update() moet in een transaction.atomic() blok worden gebruikt. Django genereert een fout als u deze buiten een transactie aanroept.
Verschillen met PostgreSQL
- De
ofparameter (select_for_update(of=(...))) wordt niet ondersteund. De back-end geeftNotSupportedErrorterug als u die doorgeeft. - SQL Server gebruikt hints op tabelniveau (
UPDLOCK) in plaats van componenten op rijniveauFOR UPDATE. Bij een hoog conflict kan escalatie van vergrendeling ertoe leiden dat meer rijen of pagina's worden vergrendeld dan waarop u zich richt. Gebruik hetSNAPSHOTisolatieniveau als u niet-blokkerende leesbewerkingen naast vergrendelde schrijfbewerkingen nodig hebt.