🔄 CAPÍTULO 03: TRANSAÇÕES ACID, NUVEM E CONCORRÊNCIA
🎯 Objetivos de Aprendizagem
Ao final deste capítulo (estimativa: 2 horas de estudo autoguiado), você será capaz de:
- 🔹 Dominar os 4 pilares transacionais ACID: Atomicidade (Tudo ou Nada), Consistência (Regras Válidas), Isolamento (Sem Interferência) e Durabilidade (Gravado em Disco).
- 🔹 Analisar os 4 Níveis de Isolamento ANSI SQL: Read Uncommitted, Read Committed, Repeatable Read e Serializable.
- 🔹 Compreender o mecanismo de Write-Ahead Logging (WAL) e recuperação de falhas em SGBDs.
- 🔹 Implementar transações seguras em Python utilizando
session.begin(),session.commit()esession.rollback().
No coração de sistemas críticos (Bancos, Hospitais, Logística), existe o conceito de Transação. Uma transação não é apenas um comando SQL solto, mas uma Unidade Lógica de Trabalho indestrutível. 🛡️🧩
🏢 O Cenário Prático (Seu Desafio)
Você atua na TecProExpress. Imagine que o sistema debita R$ 1.500 do saldo de um cliente para pagar um frete internacional, mas antes do sistema creditar esse valor na conta da TecProExpress, o servidor de banco de dados sofre uma queda de energia.
"Seu desafio é arquitetar o banco de dados para garantir que, caso o fluxo inteiro não se complete, o dinheiro volte imediatamente para a conta do cliente (Rollback). Nenhum centavo pode desaparecer do sistema."
🧠 Fundamentos: O Escudo de Confiança (ACID)
Para garantir que o seu banco nunca entre em um estado corrupto, o SGBD aplica as propriedades ACID:
- ⚛️ Atomicidade (Tudo ou Nada): Se uma transação tem 10 passos e o passo 9 falha, os 8 anteriores são desfeitos automaticamente.
- ⚖️ Consistência (Regras): A transação leva o banco de um estado válido para outro estado válido (ex: o saldo nunca ficará negativo se houver um
CHECK >= 0). - 🔒 Isolamento (Invisibilidade): Se o operador A e o operador B estiverem alterando o estoque ao mesmo tempo, um não enxerga os dados parciais do outro.
- 💾 Durabilidade (Permanência): Após o comando
COMMIT, o dado está salvo no disco permanentemente. Nem mesmo um apagão remove o dado.
📊 Fluxo da Transação (TCL)
flowchart TD
START["▶️ BEGIN TRANSACTION"] --> OP1["📝 UPDATE: Debitar Saldo Cliente"]
OP1 --> OP2["📝 UPDATE: Creditar Conta TecProExpress"]
OP2 --> DEC{"❓ Sucesso Total?"}
DEC -- SIM --> COM["✅ COMMIT: Salva no Disco"]
DEC -- NÃO --> ROL["❌ ROLLBACK: Desfaz Tudo"]
COM --> END["🏁 Fim da Transação"]
ROL --> END
🔍 Detalhamento do Fluxo:
- BEGIN TRANSACTION: Avisa ao banco que uma sequência de operações críticas começou.
- COMMIT: A confirmação de que tudo deu certo.
- ROLLBACK: O botão de "Pânico" ou "Ctrl+Z" que cancela todas as ações pendentes.
📖 Exemplo Guiado: Transações Seguras
Vamos simular o cenário da TecProExpress no banco de dados. Seguindo nossa regra de excelência: primeiro DDL, depois DML.
🛠️ Código do Exemplo
-- PASSO 1: DDL (Criar a conta corrente)
CREATE TABLE conta_corrente (
id INT PRIMARY KEY,
titular VARCHAR(100),
saldo DECIMAL(10,2) CHECK (saldo >= 0)
);
-- PASSO 2: DML (Carga Inicial)
INSERT INTO conta_corrente VALUES (1, 'Cliente João', 2000.00);
INSERT INTO conta_corrente VALUES (2, 'TecProExpress', 0.00);
-- PASSO 3: O Fluxo Transacional (TCL + DML)
START TRANSACTION;
-- Retira do Cliente
UPDATE conta_corrente SET saldo = saldo - 1500.00 WHERE id = 1;
-- Credita na Empresa
UPDATE conta_corrente SET saldo = saldo + 1500.00 WHERE id = 2;
COMMIT;
🔍 Detalhamento do Código:
START TRANSACTION: Inicia a unidade lógica.UPDATE: Altera os dados. O valor só será efetivado no banco de dados quando oCOMMITfor lido e executado pelo sistema.
☁️ A Era da Nuvem (Cloud Databases)
Atualmente, não instalamos mais servidores físicos em salas geladas. Utilizamos a Cloud Computing para hospedar nossos SGBDs.
📊 Modelos de Nuvem
flowchart LR
U1["👤 Usuário Final"]
U2["🛠️ Dev / DBA"]
U3["⚙️ SysAdmin / Infra"]
subgraph Cloud ["Modelos de Serviços Cloud"]
UC1(("SaaS: App Pronto"))
UC2(("PaaS: DBaaS"))
UC3(("IaaS: Infra Bruta"))
end
U1 --> UC1
U2 --> UC2
U3 --> UC3
🔍 Detalhamento dos Modelos:
- IaaS (Infraestrutura como Serviço): Você aluga a máquina e tem que instalar o banco. Controle total, mas requer gestão manual.
- PaaS (Plataforma como Serviço - Foco do DBA): O provedor (AWS, Azure) te entrega o banco pronto para conectar (DBaaS). Eles cuidam da atualização e do hardware.
- SaaS (Software como Serviço): O usuário apenas usa o sistema (Ex: Netflix, Gmail).
🛠️ Prática Obrigatória: O Botão de Pânico
Cenário: Simular um erro transacional para a TecProExpress.
- Crie a tabela e os inserts da Prática Guiada acima.
- Inicie uma nova transação (
START TRANSACTION). - Tente transferir
3000.00do Cliente João (que só tem 500 restantes). O banco dará um erro devido aoCHECK. - Execute
ROLLBACK;.
🏁 Resultado Esperado
Ao executar um SELECT * FROM conta_corrente, o saldo do Cliente João não deve ter sido negativado, comprovando que a transação bloqueou o estado inconsistente.
💻 Ponte Prática: Do SQL Manual ao SQLAlchemy 2.0 ORM
Como o Python e o SQLAlchemy 2.0 implementam o Controle Transacional ACID de forma blindada contra falhas?
🔴 1. A Abordagem com SQL Manual (Controle Transacional Manual)
No modelo manual, o desenvolvedor precisa lembrar de disparar rollback() no bloco except. Se esquecer, a conexão fica em estado inconsistente ou bloqueia o banco:
# ❌ ABORDAGEM COM SQL MANUAL: Risco de esquecer o rollback ou prender locks
import sqlite3
conn = sqlite3.connect("banco_legado.db")
cursor = conn.cursor()
try:
cursor.execute("UPDATE contas SET saldo = saldo - 1500 WHERE id = 1;")
cursor.execute("UPDATE contas SET saldo = saldo + 1500 WHERE id = 2;")
conn.commit() # Se falhar antes daqui, o que acontece?
except Exception as err:
conn.rollback() # Fácil de esquecer!
finally:
conn.close()
🟢 2. A Abordagem com SQLAlchemy 2.0 (Gerenciador de Contexto session.begin())
Com o SQLAlchemy 2.0, usamos o gerenciador de contexto with session.begin():. A Atomicidade (A do ACID) é nativa: se qualquer linha do bloco levantar uma exceção, o rollback é executado instantaneamente:
# ✅ ABORDAGEM MODERNA COM SQLALCHEMY 2.0: Atomicidade e Rollback Automático
from sqlalchemy import create_engine, String, Float, CheckConstraint, select
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, Session
class Base(DeclarativeBase):
pass
class ContaCorrenteModel(Base):
__tablename__ = "contas_correntes"
__table_args__ = (
CheckConstraint("saldo >= 0.0", name="chk_saldo_positivo"),
)
id: Mapped[int] = mapped_column(primary_key=True)
titular: Mapped[str] = mapped_column(String(100), nullable=False)
saldo: Mapped[float] = mapped_column(Float, nullable=False)
def __repr__(self) -> str:
return f"Conta({self.titular}) -> Saldo: R$ {self.saldo:.2f}"
🛠️ Mini-Projeto 03 (BD): Transferência Transacional Segura (ACID)
Objetivo: Simular um serviço de transferência financeira PIX entre contas e comprovar o rollback automático quando uma regra de negócio for violada.
📋 Pré-requisitos e Instalação
No terminal do seu ambiente virtual (PowerShell ou Bash), instale a biblioteca necessária:
pip install sqlalchemy
💻 Código Completo e Autocontido (miniprojeto_03_transacoes.py)
Crie o arquivo miniprojeto_03_transacoes.py e insira o código abaixo integralmente:
"""
Mini-Projeto 03: Transferência Transacional Segura (ACID)
Curso: GTI - Banco de Dados Relacionais e Engenharia de Software
Stack: Python 3.11+ | SQLAlchemy 2.0 | SQLite
"""
import os
from sqlalchemy import create_engine, String, Float, CheckConstraint, select
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, Session
# 1. Definição Declarativa do Schema
class Base(DeclarativeBase):
pass
class ContaCorrenteModel(Base):
__tablename__ = "contas_correntes"
__table_args__ = (
CheckConstraint("saldo >= 0.0", name="chk_saldo_positivo"),
)
id: Mapped[int] = mapped_column(primary_key=True)
titular: Mapped[str] = mapped_column(String(100), nullable=False)
saldo: Mapped[float] = mapped_column(Float, nullable=False)
def __repr__(self) -> str:
return f"Conta({self.titular}) -> Saldo: R$ {self.saldo:.2f}"
# 2. Serviço com Controle Transacional ACID
class ServicoTransferencia:
@staticmethod
def transferir(engine, id_origem: int, id_destino: int, valor: float) -> bool:
with Session(engine) as session:
try:
# with session.begin() abre a transação e faz COMMIT automático se tudo der certo
with session.begin():
conta_origem = session.get(ContaCorrenteModel, id_origem)
conta_destino = session.get(ContaCorrenteModel, id_destino)
if not conta_origem or not conta_destino:
raise ValueError("Uma das contas informadas não existe!")
if conta_origem.saldo < valor:
raise ValueError(f"Saldo insuficiente na conta de {conta_origem.titular}!")
print(f"💸 Debitando R$ {valor:.2f} de {conta_origem.titular}...")
conta_origem.saldo -= valor
print(f"💰 Creditando R$ {valor:.2f} em {conta_destino.titular}...")
conta_destino.saldo += valor
print("✅ [TCL] Transação confirmada com sucesso (COMMIT)!")
return True
except Exception as err:
print(f"❌ [TCL] FALHA NA TRANSAÇÃO: {err}")
print("🛡️ [ROLLBACK] Todas as operações foram revertidas pelo SGBD!")
return False
# 3. Ponto de Entrada Executável
if __name__ == "__main__":
DB_FILE = "tecpro_banco.db"
# Reset preventivo para garantir idempotência em testes repetidos
if os.path.exists(DB_FILE):
os.remove(DB_FILE)
engine = create_engine(f"sqlite:///{DB_FILE}", echo=False)
Base.metadata.create_all(bind=engine)
# Carga inicial de teste
with Session(engine) as session:
c1 = ContaCorrenteModel(id=1, titular="Cliente João", saldo=2000.0)
c2 = ContaCorrenteModel(id=2, titular="TecProExpress Cargas", saldo=0.0)
session.merge(c1)
session.merge(c2)
session.commit()
print("🔐 SIMULAÇÃO DE TRANSAÇÕES ACID (TECPROEXPRESS):")
print("=" * 60)
# 1. Transferência Válida (R$ 1.500,00)
ServicoTransferencia.transferir(engine, id_origem=1, id_destino=2, valor=1500.0)
# 2. Transferência Inválida (Tentativa de transferir R$ 3.000,00 com saldo de apenas R$ 500,00)
print("-" * 60)
ServicoTransferencia.transferir(engine, id_origem=1, id_destino=2, valor=3000.0)
# Consulta dos saldos finais:
print("-" * 60)
with Session(engine) as session:
for conta in session.scalars(select(ContaCorrenteModel)).all():
print(f" 📊 {conta}")
print("=" * 60)
🚀 Como Executar
Execute o script no terminal:
python miniprojeto_03_transacoes.py
🖥️ Saída Esperada no Console
🔐 SIMULAÇÃO DE TRANSAÇÕES ACID (TECPROEXPRESS):
============================================================
💸 Debitando R$ 1500.00 de Cliente João...
💰 Creditando R$ 1500.00 em TecProExpress Cargas...
✅ [TCL] Transação confirmada com sucesso (COMMIT)!
------------------------------------------------------------
💸 Debitando R$ 3000.00 de Cliente João...
❌ [TCL] FALHA NA TRANSAÇÃO: Saldo insuficiente na conta de Cliente João!
🛡️ [ROLLBACK] Todas as operações foram revertidas pelo SGBD!
------------------------------------------------------------
📊 Conta(Cliente João) -> Saldo: R$ 500.00
📊 Conta(TecProExpress Cargas) -> Saldo: R$ 1500.00
============================================================
💡 Checkpoint de Lógica
Reflexão Profissional: Qual é a grande vantagem da nuvem em termos de transações financeiras? O DBaaS (Database as a Service) moderno oferece backups automáticos diários. Se um Rollback falhar por erro catastrófico, o provedor da nuvem pode restaurar o banco exatamente para 1 minuto antes do acidente. 🧠🛡️
🧪 Quiz de Fixação e Autoavaliação — Capítulo 03
🧪 Quiz de Fixação e Autoavaliação — Capítulo 03
1. Em uma transferência bancária de R$ 500, o sistema debita da Conta A, mas o servidor desliga antes de creditar na Conta B. Qual princípio ACID garante que o débito seja cancelado (desfeito) ao reiniciar?
- A) Durabilidade
- B) Atomicidade (Atomicity)
- C) Isolamento
- D) Autodescrição
💡 Ver Resposta e Justificativa
Resposta Correta: B
Justificativa: Atomicidade garante que a transação é indivisível: ou todas as operações têm sucesso (COMMIT) ou nenhuma é aplicada (ROLLBACK total).
2. O que é o fenômeno da 'Leitura Suja' (Dirty Read) em bancos de dados relacionais?
- A) Quando o monitor do computador está empoeirado.
- B) Quando uma transação lê dados que foram alterados por outra transação que AINDA NÃO fez commit (e que pode sofrer rollback em seguida).
- C) Quando uma consulta SELECT demora mais de 10 segundos.
- D) Quando a tabela não possui chave primária.
💡 Ver Resposta e Justificativa
Resposta Correta: B
Justificativa: Leituras sujas acontecem no nível de isolamento 'Read Uncommitted', lendo dados 'fantasmas' que podem nunca se concretizar no banco.
3. Qual é o papel do log de escrita prévia (Write-Ahead Logging - WAL) no PostgreSQL e SQLite?
- A) Registrar o histórico de acessos dos usuários para fins de RH.
- B) Garantir a Durabilidade: gravar as mudanças no arquivo de log sequencial em disco ANTES de alterar as páginas de dados na memória RAM, permitindo recuperação instantânea após quedas de energia.
- C) Calcular a fatura mensal do serviço em nuvem.
- D) Apagar tabelas antigas.
💡 Ver Resposta e Justificativa
Resposta Correta: B
Justificativa: O WAL garante que, mesmo se o servidor for desligado da tomada, o SGBD reconstrói todas as transações comitadas ao reiniciar lendo o log sequencial.
Coloque este conhecimento em prática agora mesmo executando o roteiro autoguiado:
👉 ATIVIDADE 12: TRANSAÇÕES E ACID