Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Tento článek vysvětluje, jak nakonfigurovat úrovně zpracování transakcí a izolace pro aplikace Django pomocí back-endu mssql-django s SQL Server.
Výchozí chování
Ve výchozím nastavení django funguje v režimu automatickéhocommitu. Každý databázový dotaz běží ve své vlastní transakci a je potvrzen okamžitě. Toto chování můžete změnit pomocí AUTOCOMMIT nastavení nebo rozhraní API pro správu transakcí Django.
Nastavení AUTOCOMMIT
Nastavte v konfiguraci databáze AUTOCOMMIT na False, aby se vypnul režim automatického potvrzování:
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
Vypnutí automatického potvrzování znamená, že transakce musíte explicitně potvrdit nebo vrátit zpět. Většina aplikací Django ponechá funkci autocommit povolenou a používá transaction.atomic() pro konkrétní operace.
Použijte transaction.atomic()
Zabalte databázové operace transaction.atomic() , aby se zajistilo, že se provádějí v jedné transakci:
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()
Pokud dojde k jakékoli výjimce atomic() uvnitř bloku, celá transakce se vrátí zpět.
Vnořené transakce
Django podporuje vnořené atomic() bloky pomocí savepointů SQL Serveru:
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
Úrovně izolace transakcí
Nakonfigurujte úroveň izolace transakcí pomocí isolation_level možnosti v konfiguraci databáze:
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",
},
},
}
Podporované úrovně izolace
| Úroveň izolace | Description |
|---|---|
READ UNCOMMITTED |
Umožňuje špinavé čtení. Nejnižší izolace, nejvyšší souběžnost. |
READ COMMITTED |
SQL Server výchozí. Zabraňuje špinavým čtením. |
REPEATABLE READ |
Zabraňuje špinavému a neopakovatelnému čtení. |
SNAPSHOT |
Používá verzování řádků pro konzistentní čtení bez blokování. Vyžaduje, aby byla povolená izolace snímků na úrovni databáze. |
SERIALIZABLE |
Nejvyšší izolace. Zabraňuje fantomovým čtením. |
Povolení izolace SNAPSHOT
Pokud chcete použít SNAPSHOT izolaci, povolte ji nejprve v databázi:
ALTER DATABASE [<your-database>]
SET ALLOW_SNAPSHOT_ISOLATION ON;
Potom ho nakonfigurujte v 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",
},
},
}
Použijte dekorátor @transaction.atomic
Použití transakcí na celé funkce zobrazení:
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})
Čtení dat bez blokování (ekvivalent NOLOCK)
Běžným požadavkem je spouštět dotazy na SQL Server s hintem NOLOCK nebo s izolací READ UNCOMMITTED, aby se zabránilo blokování při práci s vytíženými tabulkami. ORM Djanga negeneruje hinty tabulek, ale máte dvě možnosti.
Možnost 1: Nastavení READ UNCOMMITTED pro každé připojení
Nastavte úroveň izolace na READ UNCOMMITTED ve vyhrazeném aliasu databáze jen pro čtení, aby se použila na všechny dotazy v tomto připojení:
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",
},
},
}
Potom směrujte dotazy na 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)
Možnost 2: Použití nezpracovaných SQL s NOLOCK
U cílových dotazů na konkrétní tabulky použijte nezpracovaný SQL s nápovědou k NOLOCK tabulce:
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()
Caution
READ UNCOMMITTED i NOLOCK umožňují špinavá čtení, což znamená, že dotazy mohou vracet data z nepotvrzených transakcí. Tyto techniky použijte pouze pro dotazy pro vytváření sestav nebo analýzy, u kterých není vyžadována absolutní konzistence.
Možnost 3: Místo toho použijte izolaci SNAPSHOT.
SNAPSHOT izolace poskytuje konzistentní čtení bez blokování a bez zašpiněných čtení. Je to doporučená alternativa NOLOCK pro většinu úloh:
DATABASES = {
"default": {
"ENGINE": "mssql",
"NAME": "<your-database>",
"HOST": "<your-server>",
"PORT": "1433",
"OPTIONS": {
"driver": "ODBC Driver 18 for SQL Server",
"isolation_level": "SNAPSHOT",
},
},
}
SNAPSHOT vyžaduje konfiguraci na úrovni databáze. Viz Povolit izolaci SNAPSHOT.
Uzamčení na úrovni řádků pomocí select_for_update()
Djangův select_for_update() je plně podporován backendem mssql-django. SQL Server to implementuje pomocí hintů tabulky místo klauzule FOR UPDATE, kterou používají jiné databáze.
Základní použití
from django.db import transaction
with transaction.atomic():
product = Product.objects.select_for_update().get(pk=1)
product.stock -= 1
product.save()
Back-end vygeneruje: SELECT ... FROM [myapp_product] WITH (ROWLOCK, UPDLOCK) WHERE ...
NOWAIT a SKIP LOCKED
Parametry nowait a skip_locked jsou podporovány:
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 | Nápověda k tabulce SQL Serveru |
|---|---|
| Default | WITH (ROWLOCK, UPDLOCK) |
nowait=True |
WITH (NOWAIT, ROWLOCK, UPDLOCK) |
skip_locked=True |
WITH (ROWLOCK, UPDLOCK, READPAST) |
Note
select_for_update() musí být použit uvnitř transaction.atomic() bloku. Django vyvolá chybu, pokud je zavoláte mimo transakci.
Rozdíly oproti PostgreSQL
- Parametr
of(select_for_update(of=(...))) není podporován. Backend vyvoláNotSupportedError, pokud jej předáte. - SQL Server místo klauzulí na úrovni
UPDLOCKřádků používá nápovědy na úrovni tabulky (FOR UPDATE). Při vysoké míře kolizí může eskalace zámků způsobit uzamčení více řádků nebo stránek, než jste zamýšleli.SNAPSHOTÚroveň izolace použijte, pokud potřebujete neblokující čtení spolu s uzamčenými zápisy.