Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Bu makalede, SQL Server ile arka ucu kullanarak mssql-django Django uygulamaları için işlem işleme ve yalıtım düzeylerini yapılandırma işlemi açıklanmaktadır.
Varsayılan davranış
Varsayılan olarak, Django otomatik komut modunda çalışır. Her veritabanı sorgusu ayrı bir işlem içinde çalışır ve hemen onaylanır. Ayarı veya Django'nun işlem yönetimi API'sini AUTOCOMMIT kullanarak bu davranışı değiştirebilirsiniz.
AUTOCOMMIT ayarı
Otomatik onay modunu devre dışı bırakmak için, veritabanı yapılandırmanızda AUTOCOMMIT değerini False olarak ayarlayı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
Otomatik komutu devre dışı bırakmak, işlemleri açıkça işlemeniz veya geri almanız gerektiği anlamına gelir. Çoğu Django uygulaması otomatik işlemeyi etkin bırakır ve belirli işlemler için transaction.atomic() kullanır.
transaction.atomic() kullanma
Veritabanı işlemlerini, tek bir işlem içinde yürütülmelerini sağlamak için transaction.atomic() içine alın:
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()
atomic() bloğu içinde herhangi bir istisna oluşursa, işlemin tamamı geri alınır.
İç içe geçmiş işlemler
Django, SQL Server'ın kaydetme noktaları aracılığıyla iç içe atomic() bloklarını destekler:
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
İşlem yalıtım düzeyleri
Veritabanı yapılandırmanızdaki seçeneği kullanarak isolation_level işlem yalıtım düzeyini yapılandırın:
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",
},
},
}
Desteklenen yalıtım düzeyleri
| Yalıtım düzeyi | Açıklama |
|---|---|
READ UNCOMMITTED |
Kirli okumalara izin verir. En düşük yalıtım, en yüksek eşzamanlılık. |
READ COMMITTED |
Varsayılan SQL Server. Kirli okumaları önler. |
REPEATABLE READ |
Kirli ve tekrarlanamayan okumaları önler. |
SNAPSHOT |
Engellemeden tutarlı okumalar için satır sürümleme kullanır. Veritabanı düzeyinde anlık görüntü yalıtımının etkinleştirilmesini gerektirir. |
SERIALIZABLE |
En yüksek yalıtım. Hayalet okumaları engeller. |
SNAPSHOT yalıtımını etkinleştir
SNAPSHOT yalıtımını kullanmak için önce bunu veritabanında etkinleştirin:
ALTER DATABASE [<your-database>]
SET ALLOW_SNAPSHOT_ISOLATION ON;
Ardından bunu settings.py içinde yapılandırın:
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",
},
},
}
@transaction.atomic dekoratörü kullanın
Tüm görünüm işlevlerine işlem uygulama:
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})
Engelleme olmadan verileri okuma (NOLOCK eşdeğeri)
Yaygın isteklerden biri, yoğun tablolarda bloklamayı önlemek için SQL Server’ı NOLOCK ipucuyla veya READ UNCOMMITTED yalıtım düzeyiyle sorgulamaktır. Django'nun ORM'sı tablo ipuçları oluşturmaz, ancak iki seçeneğiniz vardır.
Seçenek 1: Bağlantı başına READ UNCOMMITTED ayarlama
Bu bağlantıdaki tüm sorgulara uygulanması için, yalıtım düzeyini salt okunur özel bir veritabanı takma adında READ UNCOMMITTED olarak ayarlayın:
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",
},
},
}
Ardından sorguları diğer adla yönlendirin read_uncommitted :
# 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)
Seçenek 2: NOLOCK ile ham SQL kullanma
Belirli tablolardaki hedeflenen sorgular için ham SQL'i NOLOCK tablo ipucuyla birlikte kullanın:
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 Hem hem de NOLOCK kirli okumalara izin verir, bu da sorguların kaydedilmemiş işlemlerden veri döndürebileceği anlamına gelir. Bu teknikleri yalnızca mutlak tutarlılığın gerekli olmadığı raporlama veya analiz sorguları için kullanın.
3. Seçenek: Bunun yerine SNAPSHOT yalıtımını kullanın
SNAPSHOT yalıtım, engelleme olmadan ve kirli okumalar olmadan tutarlı okumalar sağlar. Çoğu iş yükü için NOLOCK yerine önerilen alternatiftir:
DATABASES = {
"default": {
"ENGINE": "mssql",
"NAME": "<your-database>",
"HOST": "<your-server>",
"PORT": "1433",
"OPTIONS": {
"driver": "ODBC Driver 18 for SQL Server",
"isolation_level": "SNAPSHOT",
},
},
}
SNAPSHOT veritabanı düzeyinde yapılandırma gerektirir. Bkz. SNAPSHOT yalıtımını etkinleştirme.
select_for_update() ile satır düzeyi kilitleme
Django’nun select_for_update() özelliği, mssql-django arka ucu tarafından tam olarak desteklenmektedir. SQL Server bunu diğer veritabanları tarafından kullanılan yan tümce yerine FOR UPDATE tablo ipuçlarını kullanarak uygular.
Temel kullanım
from django.db import transaction
with transaction.atomic():
product = Product.objects.select_for_update().get(pk=1)
product.stock -= 1
product.save()
Arka uç aşağıdakileri oluşturur: SELECT ... FROM [myapp_product] WITH (ROWLOCK, UPDLOCK) WHERE ...
NOWAIT ve SKIP LOCKED
nowait ve skip_locked parametrelerinin her ikisi de desteklenir:
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]
| Parametre | SQL Server tablo ipucu |
|---|---|
| Varsayılan | WITH (ROWLOCK, UPDLOCK) |
nowait=True |
WITH (NOWAIT, ROWLOCK, UPDLOCK) |
skip_locked=True |
WITH (ROWLOCK, UPDLOCK, READPAST) |
Note
select_for_update() bir transaction.atomic() bloğun içinde kullanılmalıdır. Django, işlemi bir işlemin dışında çağırırsanız bir hata oluşturur.
PostgreSQL'den farklar
-
ofparametresi (select_for_update(of=(...))) desteklenmez. Bunu iletirseniz, arka uçNotSupportedErrorfırlatır. - SQL Server satır düzeyi yan tümceleri yerine tablo düzeyinde
UPDLOCKipuçları (FOR UPDATE) kullanır. Yoğun çekişme durumunda, kilit yükseltmesi hedeflediğinizden daha fazla satırın veya sayfanın kilitlenmesine neden olabilir. Kilitli yazma işlemleriyle birlikte engellenmeyen okumalara ihtiyacınız varsaSNAPSHOTyalıtım düzeyini kullanın.