Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Este artigo explica como configurar os níveis de tratamento e isolamento de transações para aplicativos Django usando o mssql-django back-end com SQL Server.
Comportamento padrão
Por padrão, o Django opera no modo de confirmação automática. Cada consulta de banco de dados é executada em sua própria transação e é confirmada imediatamente. Você pode alterar esse comportamento usando a configuração ou a AUTOCOMMIT API de gerenciamento de transações do Django.
Configuração de AUTOCOMMIT
Defina AUTOCOMMIT como False na configuração do seu banco de dados para desativar o modo de confirmação automática:
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
Desabilitar a confirmação automática significa que você deve confirmar ou reverter transações explicitamente. A maioria das aplicações Django mantém o autocommit ativado e usa transaction.atomic() para operações específicas.
Use transaction.atomic()
Encapsule as operações de transaction.atomic() banco de dados para garantir que elas sejam executadas em uma única transação:
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()
Se ocorrer alguma exceção dentro de um atomic() bloco, toda a transação será revertida.
Transações aninhadas
O Django dá suporte a blocos aninhados atomic() por meio dos pontos de salvamento do 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
Níveis de isolamento de transação
Configure o nível de isolamento da transação usando a opção isolation_level na configuração do banco de dados:
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",
},
},
}
Níveis de isolamento suportados
| Nível de isolamento | Descrição |
|---|---|
READ UNCOMMITTED |
Permite leituras sujas. Isolamento mais baixo, maior simultaneidade. |
READ COMMITTED |
Padrão do SQL Server. Impede leituras sujas. |
REPEATABLE READ |
Impede leituras sujas e não repetíveis. |
SNAPSHOT |
Usa o controle de versão de linhas para leituras consistentes sem bloqueios. Requer que o isolamento de instantâneo no nível do banco de dados seja habilitado. |
SERIALIZABLE |
Isolamento mais alto. Impede leituras fantasma. |
Habilitar o isolamento de instantâneo
Para usar o isolamento SNAPSHOT, ative-o primeiro no banco de dados:
ALTER DATABASE [<your-database>]
SET ALLOW_SNAPSHOT_ISOLATION ON;
Em seguida, configure-o em 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",
},
},
}
Use o decorador @transaction.atomic
Aplicar transações a funções de exibição inteiras:
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})
Ler dados sem bloqueio (equivalente a NOLOCK)
Uma solicitação comum é consultar o SQL Server com a dica NOLOCK ou o nível de isolamento READ UNCOMMITTED para evitar bloqueios em tabelas com muita atividade. O ORM do Django não gera sugestões de tabela, mas você tem duas opções.
Opção 1: Definir READ UNCOMMITTED por conexão
Defina o nível de isolamento como READ UNCOMMITTED em um alias dedicado de banco de dados somente leitura, para que ele seja aplicado a todas as consultas nessa conexão:
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",
},
},
}
Em seguida, encaminhe consultas para o alias 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)
Opção 2: Usar SQL bruto com NOLOCK
Para consultas direcionadas a tabelas específicas, use SQL puro com a dica de tabela NOLOCK:
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()
Cuidado
Ambas READ UNCOMMITTED e NOLOCK permitem leituras sujas, o que significa que as consultas podem retornar dados de transações não confirmadas. Use essas técnicas apenas para consultas de relatório ou análise em que a consistência absoluta não é necessária.
Opção 3: Usar o isolamento SNAPSHOT em vez disso
SNAPSHOT O isolamento fornece leituras consistentes sem bloqueio e sem leituras sujas. É a alternativa recomendada para NOLOCK na maioria das cargas de trabalho:
DATABASES = {
"default": {
"ENGINE": "mssql",
"NAME": "<your-database>",
"HOST": "<your-server>",
"PORT": "1433",
"OPTIONS": {
"driver": "ODBC Driver 18 for SQL Server",
"isolation_level": "SNAPSHOT",
},
},
}
SNAPSHOT requer a configuração no nível do banco de dados. Consulte Habilitar isolamento SNAPSHOT.
Bloqueio em nível de linha com select_for_update()
O select_for_update() do Django tem suporte completo do backend mssql-django. O SQL Server implementa isso usando dicas de tabela em vez da cláusula FOR UPDATE usada por outros bancos de dados.
Uso Básico
from django.db import transaction
with transaction.atomic():
product = Product.objects.select_for_update().get(pk=1)
product.stock -= 1
product.save()
O back-end gera: SELECT ... FROM [myapp_product] WITH (ROWLOCK, UPDLOCK) WHERE ...
NOWAIT e SKIP LOCKED
Há suporte para os parâmetros nowait e skip_locked:
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]
| Parâmetro | Hint de tabela do SQL Server |
|---|---|
| Default | WITH (ROWLOCK, UPDLOCK) |
nowait=True |
WITH (NOWAIT, ROWLOCK, UPDLOCK) |
skip_locked=True |
WITH (ROWLOCK, UPDLOCK, READPAST) |
Note
select_for_update() deve ser usado dentro de um transaction.atomic() bloco. O Django gera um erro se você chamá-lo fora de uma transação.
Diferenças do PostgreSQL
- O
ofparâmetro (select_for_update(of=(...))) não tem suporte. O backend geraNotSupportedErrorse você o passar. - O SQL Server usa instruções em nível de tabela (
UPDLOCK) em vez de cláusulasFOR UPDATEem nível de linha. Sob alta contenção, o escalonamento de bloqueios pode fazer com que mais linhas ou páginas sejam bloqueadas do que você pretendia bloquear. Use oSNAPSHOTnível de isolamento se precisar de leituras sem bloqueio junto com gravações bloqueadas.