Banco de dados em produção costuma dar problema em horários ruins: a aplicação trava porque uma consulta segurou um lock, o disco enche de madrugada, alguém rodou um UPDATE sem WHERE. Nessas horas, saber o comando certo de cabeça (ou saber onde procurar) faz toda a diferença.

Este artigo reúne situações reais, organizadas por tema, com os comandos para PostgreSQL, MySQL, SQL Server, MongoDB e Redis. No final há uma tabela completa para consulta rápida. Para entender os conceitos por trás (transações, índices, isolamento, NoSQL), leia o guia completo de banco de dados.

Alterando dados sem medo

Conferir antes de rodar um UPDATE ou DELETE

SQL
-- 1. Veja exatamente o que será afetado
SELECT id, status FROM pedidos WHERE status = 'pendente' AND criado_em < '2026-01-01';
 
-- 2. Rode dentro de uma transação
BEGIN;
UPDATE pedidos SET status = 'expirado'
WHERE status = 'pendente' AND criado_em < '2026-01-01';
-- o cliente mostra: UPDATE 42  → era o número esperado?
 
-- 3. Confirme ou desfaça
COMMIT;   -- ou ROLLBACK;

No SQL Server, BEGIN TRANSACTION e SELECT @@ROWCOUNT logo após o comando.

Rodei um UPDATE sem WHERE

Se ainda está dentro da transação: ROLLBACK; e respire. Se já confirmou (ou estava em autocommit), o caminho é restaurar a partir de backup:

  • PostgreSQL: restaure o último backup numa base separada e copie de volta os dados afetados; com arquivamento de WAL configurado, dá para voltar a um instante exato (point-in-time recovery).
  • MySQL: restaure o backup e reaplique o binary log até um pouco antes do comando (mysqlbinlog --stop-datetime).
  • SQL Server: com modelo de recuperação FULL e backups de log, RESTORE ... WITH STOPAT = '2026-09-24 14:32:00' restaura até o momento anterior.

Prevenção: no MySQL, SET SQL_SAFE_UPDATES = 1; recusa UPDATE/DELETE sem WHERE pela chave.

Apagar em lotes para não travar a tabela

SQL
-- PostgreSQL: apaga de 10 mil em 10 mil
DELETE FROM logs
WHERE id IN (SELECT id FROM logs WHERE criado_em < now() - INTERVAL '90 days' LIMIT 10000);
 
-- MySQL
DELETE FROM logs WHERE criado_em < NOW() - INTERVAL 90 DAY LIMIT 10000;
 
-- SQL Server
DELETE TOP (10000) FROM logs WHERE criado_em < DATEADD(DAY, -90, GETDATE());

Repita até não sobrar linhas. Um DELETE de milhões de linhas numa única transação segura locks, cresce o log e pode derrubar a replicação.

Dados duplicados

Encontrar duplicados

SQL
SELECT email, COUNT(*) AS vezes
FROM clientes
GROUP BY email
HAVING COUNT(*) > 1
ORDER BY vezes DESC;

Apagar duplicados mantendo o registro mais antigo

SQL
-- PostgreSQL e SQL Server (e MySQL 8 com CTE em DELETE via JOIN)
WITH ranqueados AS (
    SELECT id, ROW_NUMBER() OVER (PARTITION BY email ORDER BY id) AS n
    FROM clientes
)
DELETE FROM clientes WHERE id IN (SELECT id FROM ranqueados WHERE n > 1);

Depois, impeça que volte a acontecer: ALTER TABLE clientes ADD CONSTRAINT clientes_email_unico UNIQUE (email);.

A aplicação travou: locks e sessões

Quem está rodando o quê agora

SQL
-- PostgreSQL
SELECT pid, usename, state, now() - query_start AS duracao, left(query, 80) AS consulta
FROM pg_stat_activity
WHERE state <> 'idle'
ORDER BY duracao DESC;
 
-- MySQL
SHOW FULL PROCESSLIST;
 
-- SQL Server
SELECT session_id, status, blocking_session_id, wait_type, total_elapsed_time
FROM sys.dm_exec_requests
WHERE session_id > 50;

Quem está bloqueando quem

SQL
-- PostgreSQL
SELECT pid, pg_blocking_pids(pid) AS bloqueado_por, left(query, 80)
FROM pg_stat_activity
WHERE cardinality(pg_blocking_pids(pid)) > 0;
 
-- SQL Server
SELECT session_id, blocking_session_id, wait_type, wait_time
FROM sys.dm_exec_requests
WHERE blocking_session_id <> 0;
 
-- MySQL 8
SELECT * FROM sys.innodb_lock_waits;

Encerrar a consulta ou a sessão problemática

SQL
-- PostgreSQL
SELECT pg_cancel_backend(12345);      -- cancela só a consulta (mais gentil)
SELECT pg_terminate_backend(12345);   -- derruba a conexão
 
-- MySQL
KILL QUERY 123;                       -- cancela a consulta
KILL 123;                             -- derruba a conexão
 
-- SQL Server
KILL 57;

Uma transação aberta e esquecida (idle in transaction no PostgreSQL) é a causa mais comum de locks eternos. No PostgreSQL, idle_in_transaction_session_timeout encerra essas sessões automaticamente.

Limitar quanto tempo uma consulta pode rodar

SQL
SET statement_timeout = '30s';          -- PostgreSQL, na sessão
SET SESSION max_execution_time = 30000; -- MySQL, em ms (só SELECT)

O banco ficou lento

Ver o plano de execução

SQL
EXPLAIN ANALYZE SELECT * FROM pedidos WHERE cliente_id = 42;   -- PostgreSQL
EXPLAIN ANALYZE SELECT * FROM pedidos WHERE cliente_id = 42;   -- MySQL 8.0.18+
SET STATISTICS IO, TIME ON;                                     -- SQL Server (e ative o plano no SSMS)

Seq Scan (PostgreSQL), type: ALL (MySQL) ou Table Scan (SQL Server) numa tabela grande indicam falta de índice.

As consultas mais pesadas

SQL
-- PostgreSQL (precisa da extensão pg_stat_statements)
SELECT calls, round(total_exec_time) AS ms_total, round(mean_exec_time) AS ms_media, left(query, 80)
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;
 
-- SQL Server
SELECT TOP 10 qs.total_elapsed_time / qs.execution_count AS media, qs.execution_count,
       SUBSTRING(st.text, 1, 100) AS consulta
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st
ORDER BY media DESC;

No MySQL, ative o slow query log: SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 1;.

Criar índice sem travar a tabela

SQL
CREATE INDEX CONCURRENTLY idx_pedidos_cliente ON pedidos (cliente_id);   -- PostgreSQL
ALTER TABLE pedidos ADD INDEX idx_cliente (cliente_id), ALGORITHM=INPLACE, LOCK=NONE;   -- MySQL
CREATE INDEX idx_cliente ON pedidos (cliente_id) WITH (ONLINE = ON);     -- SQL Server (edições que suportam)

Estatísticas desatualizadas

SQL
ANALYZE pedidos;              -- PostgreSQL
ANALYZE TABLE pedidos;        -- MySQL
UPDATE STATISTICS pedidos;    -- SQL Server

O otimizador escolhe o plano com base em estatísticas. Depois de uma carga grande de dados, elas podem estar velhas e gerar planos ruins.

Índices que ninguém usa (e só atrasam as escritas)

SQL
-- PostgreSQL
SELECT relname AS tabela, indexrelname AS indice, idx_scan
FROM pg_stat_user_indexes
WHERE idx_scan = 0
ORDER BY pg_relation_size(indexrelid) DESC;

No SQL Server, compare user_seeks + user_scans com user_updates em sys.dm_db_index_usage_stats.

Espaço em disco

Tamanho dos bancos e das maiores tabelas

SQL
-- PostgreSQL
SELECT datname, pg_size_pretty(pg_database_size(datname)) FROM pg_database;
SELECT relname, pg_size_pretty(pg_total_relation_size(relid)) AS total
FROM pg_statio_user_tables ORDER BY pg_total_relation_size(relid) DESC LIMIT 10;
 
-- MySQL
SELECT table_name, ROUND((data_length + index_length) / 1024 / 1024) AS mb
FROM information_schema.tables
WHERE table_schema = 'loja'
ORDER BY mb DESC LIMIT 10;
 
-- SQL Server
EXEC sp_spaceused;
EXEC sp_spaceused 'pedidos';

O log de transações do SQL Server não para de crescer

SQL
DBCC SQLPERF(LOGSPACE);                                          -- quanto do log está em uso
SELECT name, recovery_model_desc, log_reuse_wait_desc FROM sys.databases;

Se o modelo é FULL e log_reuse_wait_desc mostra LOG_BACKUP, faltam backups de log: agende BACKUP LOG regularmente. Encolher o arquivo sem resolver a causa só adia o problema.

PostgreSQL: tabela inchada depois de muitos UPDATE/DELETE

SQL
VACUUM (VERBOSE, ANALYZE) pedidos;   -- limpa versões mortas e reaproveita espaço

O VACUUM FULL devolve espaço ao sistema operacional, mas trava a tabela inteira durante a execução; prefira ferramentas como pg_repack em produção.

Backups e restores de emergência

Bash
# PostgreSQL
pg_dump -U postgres -d loja -F c -f loja_$(date +%F).dump
pg_restore -U postgres -d loja_restaurada loja_2026-09-24.dump
pg_dump -U postgres -d loja -t pedidos -F c -f pedidos.dump     # uma tabela só
 
# MySQL
mysqldump -u root -p --single-transaction --routines loja > loja.sql
mysql -u root -p loja_restaurada < loja.sql
SQL
-- SQL Server
BACKUP DATABASE Loja TO DISK = 'C:\bk\loja.bak' WITH COPY_ONLY, COMPRESSION;
RESTORE VERIFYONLY FROM DISK = 'C:\bk\loja.bak';
RESTORE DATABASE Loja_Teste FROM DISK = 'C:\bk\loja.bak'
WITH MOVE 'Loja' TO 'C:\data\Loja_Teste.mdf', MOVE 'Loja_log' TO 'C:\data\Loja_Teste.ldf';

COPY_ONLY faz um backup avulso sem bagunçar a sequência de backups diferenciais. Os nomes lógicos usados no MOVE saem de RESTORE FILELISTONLY.

Exportar uma consulta para CSV

SQL
\copy (SELECT * FROM pedidos WHERE status = 'pago') TO 'pagos.csv' CSV HEADER   -- psql
Bash
mysql -u root -p -e "SELECT * FROM loja.pedidos" --batch > pedidos.tsv          # MySQL (separado por tab)

Usuários e acesso

Criar um usuário só de leitura

SQL
-- PostgreSQL
CREATE ROLE relatorios LOGIN PASSWORD 'troque-esta-senha';
GRANT CONNECT ON DATABASE loja TO relatorios;
GRANT USAGE ON SCHEMA public TO relatorios;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO relatorios;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO relatorios;
 
-- MySQL
CREATE USER 'relatorios'@'%' IDENTIFIED BY 'troque-esta-senha';
GRANT SELECT ON loja.* TO 'relatorios'@'%';
 
-- SQL Server
CREATE LOGIN relatorios WITH PASSWORD = 'Troque-Esta-Senha1';
CREATE USER relatorios FOR LOGIN relatorios;
ALTER ROLE db_datareader ADD MEMBER relatorios;

Usuário órfão depois de restaurar no SQL Server

SQL
ALTER USER app WITH LOGIN = app;

Religa o usuário do banco ao login da instância (substitui o antigo sp_change_users_login).

"Too many connections"

SQL
SHOW max_connections;                                        -- PostgreSQL
SELECT count(*) FROM pg_stat_activity;
SHOW VARIABLES LIKE 'max_connections';                       -- MySQL
SHOW STATUS LIKE 'Threads_connected';

A solução raramente é aumentar o limite: use um pool de conexões na aplicação (ou PgBouncer no PostgreSQL) e procure conexões vazando.

MongoDB em apuros

O que está rodando e o que está lento

JavaScript
db.currentOp({ secs_running: { $gt: 5 } })     // operações há mais de 5 s
db.killOp(12345)                                // encerra uma operação
db.setProfilingLevel(1, { slowms: 100 })        // registra consultas acima de 100 ms
db.system.profile.find().sort({ ts: -1 }).limit(5)

Consulta sem índice

JavaScript
db.pedidos.find({ clienteId: 42 }).explain("executionStats")
// "COLLSCAN" no plano = varreu a coleção inteira
db.pedidos.createIndex({ clienteId: 1, criadoEm: -1 })

Encontrar e remover duplicados

JavaScript
db.clientes.aggregate([
  { $group: { _id: "$email", ids: { $push: "$_id" }, total: { $sum: 1 } } },
  { $match: { total: { $gt: 1 } } }
])

Depois, remova os extras e crie db.clientes.createIndex({ email: 1 }, { unique: true }).

Tamanho e backup

JavaScript
db.stats()                            // tamanho do banco
db.pedidos.stats().size               // tamanho da coleção em bytes
Bash
mongodump --uri="mongodb://localhost:27017/loja" --out=backup/
mongorestore --uri="mongodb://localhost:27017" backup/
mongoexport --db=loja --collection=pedidos --type=csv --fields=_id,total --out=pedidos.csv

Redis em apuros

Está vivo? Quanto de memória usa?

Bash
redis-cli PING                    # PONG = ok
redis-cli INFO memory             # used_memory_human e maxmemory
redis-cli INFO stats              # hits, misses, conexões
redis-cli DBSIZE                  # quantidade de chaves

Achar as maiores chaves sem travar o servidor

Bash
redis-cli --bigkeys               # amostra as maiores chaves por tipo
redis-cli --scan --pattern "sessao:*" | head
redis-cli MEMORY USAGE nome:da:chave

Nunca use KEYS * em produção: ele percorre tudo numa única thread e bloqueia o Redis.

Comandos lentos e clientes conectados

Bash
redis-cli SLOWLOG GET 10          # os 10 comandos mais lentos recentes
redis-cli CLIENT LIST             # quem está conectado
redis-cli CLIENT KILL ID 42       # derruba um cliente

Apagar muitas chaves por padrão

Bash
redis-cli --scan --pattern "cache:produtos:*" | xargs -L 100 redis-cli UNLINK

UNLINK libera a memória em segundo plano, sem bloquear como o DEL em chaves grandes.

A memória acabou

Defina limite e política de despejo, para que o Redis remova chaves antigas em vez de recusar escritas:

Bash
redis-cli CONFIG SET maxmemory 512mb
redis-cli CONFIG SET maxmemory-policy allkeys-lru

Para cache, allkeys-lru é o padrão sensato. Se o Redis guarda dados que não podem sumir, prefira volatile-lru (só remove chaves com expiração) e mais memória.

Tabela completa: bancos de dados em emergências

Cheat sheet

SQL e NoSQL: comandos que salvam sua vida

PostgreSQL, MySQL, SQL Server, MongoDB e Redis.

Alterações seguras

Antes de mexer em produção

ComandoDescrição
BEGIN; ... ROLLBACK;Testa a alteração e desfaz
SELECT ... WHERE (mesmo filtro)Confere as linhas antes
SET SQL_SAFE_UPDATES = 1;MySQL recusa UPDATE/DELETE sem WHERE
DELETE ... LIMIT 10000Apaga em lotes (MySQL)
DELETE TOP (10000) FROM t WHERE ...Apaga em lotes (SQL Server)
SELECT @@ROWCOUNTLinhas afetadas (SQL Server)

Sessões e locks

Quando a aplicação trava

ComandoDescrição
SELECT * FROM pg_stat_activitySessões (PostgreSQL)
SELECT pg_blocking_pids(pid)Quem bloqueia (PostgreSQL)
SELECT pg_cancel_backend(pid)Cancela consulta (PostgreSQL)
SELECT pg_terminate_backend(pid)Derruba sessão (PostgreSQL)
SHOW FULL PROCESSLISTSessões (MySQL)
KILL QUERY 123 / KILL 123Cancela / derruba (MySQL)
SELECT * FROM sys.dm_exec_requestsRequisições (SQL Server)
KILL 57Derruba sessão (SQL Server)
SET statement_timeout = '30s'Limite de tempo (PostgreSQL)

Lentidão

Encontre e corrija o gargalo

ComandoDescrição
EXPLAIN ANALYZE SELECT ...Plano real (PostgreSQL, MySQL 8)
SET STATISTICS IO, TIME ONEstatísticas (SQL Server)
SELECT ... FROM pg_stat_statementsConsultas mais pesadas
CREATE INDEX CONCURRENTLY ...Índice sem travar (PostgreSQL)
... WITH (ONLINE = ON)Índice online (SQL Server)
ANALYZE t / UPDATE STATISTICS tAtualiza estatísticas
SET GLOBAL slow_query_log = 'ON'Log de lentas (MySQL)
SELECT ... FROM pg_stat_user_indexesÍndices sem uso (PostgreSQL)

Espaço em disco

Quem está ocupando o disco

ComandoDescrição
pg_size_pretty(pg_database_size('db'))Tamanho do banco (PostgreSQL)
pg_total_relation_size('t')Tamanho da tabela (PostgreSQL)
information_schema.tablesTamanhos (MySQL)
EXEC sp_spaceusedTamanhos (SQL Server)
DBCC SQLPERF(LOGSPACE)Uso do log (SQL Server)
log_reuse_wait_descPor que o log não esvazia
VACUUM (ANALYZE) tRecupera espaço (PostgreSQL)

Backup e restore

Tenha sempre um caminho de volta

ComandoDescrição
pg_dump -F c -d db -f db.dumpBackup (PostgreSQL)
pg_restore -d nova db.dumpRestore (PostgreSQL)
mysqldump --single-transaction db > db.sqlBackup (MySQL)
mysql db < db.sqlRestore (MySQL)
BACKUP DATABASE db TO DISK = '...' WITH COPY_ONLYBackup avulso (SQL Server)
RESTORE VERIFYONLY FROM DISK = '...'Confere o backup
RESTORE ... WITH STOPAT = '...'Volta a um instante
\copy (SELECT ...) TO 'f.csv' CSV HEADERExporta CSV (psql)

Acesso

Usuários e conexões

ComandoDescrição
GRANT SELECT ON ALL TABLES IN SCHEMA public TO rLeitura (PostgreSQL)
GRANT SELECT ON db.* TO 'u'@'%'Leitura (MySQL)
ALTER ROLE db_datareader ADD MEMBER uLeitura (SQL Server)
ALTER USER u WITH LOGIN = uCorrige usuário órfão (SQL Server)
SHOW max_connectionsLimite de conexões (PostgreSQL)
SHOW STATUS LIKE 'Threads_connected'Conexões abertas (MySQL)

MongoDB

No mongosh e no terminal

ComandoDescrição
db.currentOp({ secs_running: { $gt: 5 } })Operações longas
db.killOp(opid)Encerra operação
db.setProfilingLevel(1, { slowms: 100 })Registra consultas lentas
.explain("executionStats")Plano (procure COLLSCAN)
db.c.createIndex({ campo: 1 })Cria índice
db.stats()Tamanho do banco
mongodump / mongorestoreBackup / restore
mongoexport --type=csvExporta CSV

Redis

No redis-cli

ComandoDescrição
PINGTesta se está vivo
INFO memoryUso de memória
redis-cli --bigkeysMaiores chaves
MEMORY USAGE chaveMemória de uma chave
SLOWLOG GET 10Comandos lentos
CLIENT LIST / CLIENT KILL ID nClientes conectados
--scan --pattern "x:*"Lista chaves sem travar
UNLINK chaveApaga sem bloquear
CONFIG SET maxmemory-policy allkeys-lruPolítica de despejo

Referências