MongoDB: Replicação: Arquitetura de alta disponibilidade
Última atualização: 2026-08-26
Os conjuntos de réplicas são a base da alta disponibilidade do MongoDB — dominá-los permite que você crie clusters de produção com 99,99% de disponibilidade.
1. O que você vai aprender
- Conceito de conjunto de réplicas (primário / secundário / árbitro)
- Mecanismo de eleição (baseado no Raft)
- Sincronização de dados (sincronização inicial / acompanhamento do oplog)
- Separação entre leitura e gravação (readPreference)
- Implantação de um conjunto de réplicas de 3 nós com o Docker
- Failover e recuperação
2. Arquitetura do Conjunto de Réplicas
Visão geral do conceito: Um Conjunto de Réplicas é a base da arquitetura de alta disponibilidade do MongoDB. Ele consiste em vários processos mongod — 1 nó primário + N nós secundários + um nó árbitro opcional. O nó primário aceita todas as operações de gravação, que são replicadas de forma assíncrona para os nós secundários por meio do oplog, garantindo assim a redundância dos dados e o failover automático.
Como funciona: O Primário registra todas as operações de gravação no oplog (uma coleção com tamanho fixo e limite máximo), enquanto o Secundário obtém e reproduz continuamente as operações de gravação, acompanhando o oplog para manter a consistência dos dados com o Primário. Quando o Primário falha, os nós restantes elegem um novo Primário usando um protocolo de eleição (baseado no Raft), e o aplicativo se reconecta automaticamente, alcançando 99,99% de disponibilidade.
O Protocolo Raft e o Mecanismo de Eleição: O processo de eleição do MongoDB baseia-se em uma variante do protocolo Raft, cuja regra fundamental é a “regra da maioria” — somente um candidato que receba votos de mais da metade dos nós pode se tornar o Primário. Isso garante que haja, no máximo, um Primário a qualquer momento (para evitar o “split-brain”) e que o novo Primário possua os dados mais completos.
graph TB
App[Applications] --> P[Primary<br/>Master Node<br/>Accept all posts]
P -.Copy oplog.-> S1[Secondary 1<br/>From node<br/>Readable+Backup]
P -.Copy oplog.-> S2[Secondary 2<br/>From node<br/>Readable+Backup]
A[Arbiter<br/>Arbitration Node<br/>Vote Only]
subgraph "Data Flow"
W[Write] --> P
P -->|oplog| S1
P -->|oplog| S2
end
subgraph "Election"
P -->|Heartbeat| S1
P -->|Heartbeat| S2
P -->|Heartbeat| A
end
style P fill:#d4edda
style S1 fill:#cce5ff
style S2 fill:#cce5ff
style A fill:#fff3cd
Comparação das funções dos nós:
| Nó | Responsabilidades | Armazena dados | Legível | Participa de eleições | Prioridade |
|---|---|---|---|---|---|
| Primário | Aceita todas as operações de gravação | ✅ | ✅ | ✅ | Padrão 1 (pode ser aumentado) |
| Secundário | Copiar dados primários | ✅ | ✅ (requer configuração) | ✅ | Padrão 1 |
| Árbitro | Participa apenas da votação eleitoral | ❌ | ❌ | ✅ | 0 (não pode ser eleito) |
Conjuntos de réplicas x nós únicos:
| Dimensão | Nó único | Conjunto de réplicas |
|---|---|---|
| Disponibilidade | Ponto único de falha | 99,99% (failover automático) |
| Segurança de dados | Falha no disco significa perda de dados | Redundância com múltiplas cópias |
| Expansão de leitura | Nenhuma | Balanceamento de carga de leitura secundária |
| Suporte a transações | ❌ | ✅ (Requer um conjunto de réplicas) |
| Complexidade operacional | Baixa | Média |
3. Inicie o conjunto de réplicas
Explicação do conceito: Iniciar um conjunto de réplicas envolve duas etapas: 1) Iniciar o processo mongod com o parâmetro --replSet; 2) Executar rs.initiate() em mongosh para inicializar a configuração do conjunto de réplicas. Durante a inicialização, especifique os endereços de todos os membros, e o MongoDB elegerá automaticamente um Primário.
Como funciona: rs.initiate() grava a configuração do conjunto de réplicas no banco de dados local de cada nó, acionando o protocolo de eleição. O primeiro nó a ser inicializado geralmente se torna o Primário; os demais nós sincronizam automaticamente a configuração e começam a acompanhar o oplog.
# === Start the first node(Raid Mode)===
mongod --replSet rs0 --port 27017 --dbpath /data/db1 --bind_ip localhost
# === mongosh Initialization ===
mongosh
> rs.initiate({
_id: 'rs0',
members: [
{ _id: 0, host: 'localhost:27017' },
{ _id: 1, host: 'localhost:27018' },
{ _id: 2, host: 'localhost:27019' }
]
});
Parâmetros de configuração de inicialização:
| Parâmetro | Descrição | Exemplo |
|---|---|---|
_id |
Nome do conjunto de instâncias; deve corresponder a --replSet |
'rs0' |
members |
Lista de membros | [{_id: 0, host: '...'}] |
members._id |
ID de membro (único) | 0, 1, 2 |
members.host |
Endereço do membro | 'localhost:27017' |
members.priority |
Prioridade eleitoral (0 = inelegível) | Padrão 1 |
settings.electionTimeoutMillis |
Tempo limite do heartbeat | Padrão: 10.000 ms |
4. Adicionar/remover nós
Explicação do conceito: Os conjuntos de réplicas permitem adicionar e remover nós em tempo real, sem a necessidade de interrupção do serviço. Após a adição de um nó, o novo nó realiza automaticamente uma sincronização inicial (sincronização completa) a partir do nó primário e, em seguida, passa a acompanhar o oplog (sincronização incremental).
Processo de sincronização de dados:
sequenceDiagram
participant New as New Node
participant P as Primary
participant Oplog as oplog
New->>P: Request to Join a Raid Group
P-->>New: Confirm + Current Configuration
Note over New,P: Phase 1: Initial Sync(Full Synchronization)
New->>P: Request Full Data
P-->>New: All Aggregated Data + Index
Note over New,P: Phase 2: Oplog Tailing(Incremental Synchronization)
loop Continuous
New->>Oplog: Get the latest oplog Entry
Oplog-->>New: Incremental Operations
New->>New: Replay oplog Operation
end
Note over New: Once synchronization is complete, you can participate in the election.
| Operação | Comando | Descrição |
|---|---|---|
| Adicionar nó de dados | rs.add('host:port') |
Sincronização inicial automática |
| Adicionar nó de arbitragem | rs.addArb('host:port') |
Apenas para votação, não armazenar dados |
| Remover nó | rs.remove('host:port') |
Rebaixamento automático do nó |
| Ver status | rs.status() |
Status e integridade de todos os nós |
| Exibir configuração | rs.conf() |
Detalhes da configuração do conjunto de réplicas |
// === Add a New Node ===
rs.add('localhost:27020');
// === Add Arbiter(Do not save data)===
rs.addArb('localhost:27021');
// === Remove Node ===
rs.remove('localhost:27020');
// === View Replica Set Status ===
rs.status();
Análise dos pontos-chave:
- Durante a sincronização inicial, os novos nós não participam de eleições nem de serviços de leitura; assim que a sincronização for concluída, eles se tornam automaticamente nós secundários.
- A sincronização inicial de grandes conjuntos de dados leva muito tempo (100 GB podem levar várias horas), por isso recomenda-se adicioná-los fora dos horários de pico.
- O Arbiter não armazena dados; ele é utilizado exclusivamente para garantir que um número ímpar de nós participe da votação, e sua prioridade é 0.
5. Mecanismo de eleição (baseado no Raft)
Explicação do conceito: O mecanismo de eleição é fundamental para a alta disponibilidade de um conjunto de réplicas — quando o Primário falha, o Secundário inicia automaticamente uma eleição para selecionar um novo Primário. A eleição do MongoDB baseia-se em uma variante do protocolo Raft, cujas principais garantias são “no máximo um Primário a qualquer momento” (para evitar o fenômeno de “split-brain”) e “o novo Primário possui os dados mais completos”.
Processo de eleição da Raft:
- Detecção de falha: O secundário não recebeu um sinal de vida do primário dentro do tempo limite de eleição (timeout) (padrão: 10 segundos)
- Iniciar uma eleição: O Secundário disponível com a prioridade mais alta torna-se candidato, e seu mandato é incrementado.
- Solicitar um voto: O candidato envia uma solicitação de voto a todos os nós
- Regras de votação: Cada nó emite apenas um voto por mandato, para o candidato com os dados mais atualizados.
- Eleição por maioria: O candidato que receber mais da metade dos votos (> N/2) torna-se o novo Primário.
- Reconexão do aplicativo: O driver detecta automaticamente o novo primário e realiza a troca de forma transparente
sequenceDiagram
participant S1 as Secondary 1<br/>(Candidates)
participant S2 as Secondary 2
participant A as Arbiter
Note over S1: Primary Heartbeat Timeout 10s<br/>Call for an Election
S1->>S1: Auto-increment term = 2<br/>Vote for yourself
S1->>S2: RequestVote(term=2, lastOplogTime)
S1->>A: RequestVote(term=2, lastOplogTime)
S2->>S1: GrantVote ✅<br/>(The data is recent enough)
A->>S1: GrantVote ✅
Note over S1: receive a majority of the votes (2/3)<br/>Become a New Primary
S1->>S2: Heartbeat(term=2, role=PRIMARY)
S1->>A: Heartbeat(term=2, role=PRIMARY)
Note over S1,S2: The election is over,App Auto-Reconnect
Principais parâmetros eleitorais:
| Regras eleitorais | Descrição | Valor padrão |
|---|---|---|
| Gatilho | O primário está inacessível há mais tempo do que o tempo de espera da eleição | 10 segundos |
| Candidato | Todos os secundários + Árbitro | — |
| Voto | É necessário obter a maioria dos votos (> metade) para se tornar o candidato principal | — |
| Prioridade | Os nós com maior prioridade são eleitos primeiro | 1 |
| Integridade dos dados | Os eleitores votam apenas em candidatos com oplogs atualizados | — |
| Tempo de espera entre eleições | Evitar eleições frequentes | 30 segundos |
graph LR
A[Primary Failure] --> B[Secondary Testing]
B --> C{receive a majority of the votes?}
C -->|Yes| D[Promoted to Primary]
C -->|No| E[Waiting]
D --> F[App Reconnection]
style D fill:#d4edda
| Regras eleitorais | Explicação |
|---|---|
| Gatilho | O primário está inacessível há mais tempo do que o tempo de espera da eleição |
| Candidato | Todo o Ensino Médio + Árbitro |
| Voto | É necessário obter a maioria dos votos (> metade) para se tornar o candidato principal |
| Prioridade | Os nós com maior prioridade são eleitos primeiro |
Por que é necessário um número ímpar de nós: Um sistema com 3 nós pode tolerar 1 falha (votação por maioria de 2/3); um sistema com 4 nós também pode tolerar 1 falha (votação por maioria de 3/4); e um sistema com 5 nós pode tolerar 2 falhas (votação por maioria de 3/5). Um número par de nós não aumenta a tolerância a falhas, mas aumenta os custos; portanto, recomenda-se um número ímpar de nós.
▶ Exemplo 1: Configuração da prioridade de seleção
// TechCorp:Give priority to the more powerful server Primary
rs.reconfig({
_id: 'rs0',
members: [
{ _id: 0, host: 'mongo1:27017', priority: 3 }, // The Strongest Server,Elected by a landslide
{ _id: 1, host: 'mongo2:27017', priority: 2 }, // Second choice
{ _id: 2, host: 'mongo3:27017', priority: 1 }, // Lowest Priority
{ _id: 3, host: 'mongo4:27017', priority: 0 } // Never Elected(Cold Standby)
]
});
// priority: 0 That node will never be elected Primary,Suitable for use as a cold standby or reporting server
// Adjustment priority This will trigger a re-election(If the current Primary No longer the best option)
6. Separação das operações de leitura e gravação
Explicação do conceito: Os conjuntos de réplicas oferecem suporte inerente à separação entre leitura e gravação — as operações de gravação são encaminhadas para o primário, enquanto as operações de leitura podem ser distribuídas para o secundário. Ao configurar a política de roteamento de leitura por meio do readPreference, é possível reduzir significativamente a carga no primário em cenários com grande volume de leituras.
Como funciona: O driver Mongoose/Mongo utiliza o parâmetro readPreference para determinar a qual nó uma operação de leitura é enviada. primary garante consistência forte, enquanto secondary reduz a carga no nó primário, mas introduz latência na replicação.
Detalhes da preferência de leitura:
| Padrão | Comportamento | Consistência | Latência | Casos de uso |
|---|---|---|---|---|
primary |
Nó primário somente leitura | Máximo | Mínimo | Transações, dados críticos |
primaryPreferred |
Primário primeiro; se indisponível, recorrer ao secundário | Forte | Baixo | Cenários gerais |
secondary |
Nó secundário somente leitura | Fraco | Alto | Relatórios/Análises |
secondaryPreferred |
Use este primeiro; se não estiver disponível, use o principal | Mais fraco | Menor | Com muitas leituras e poucas gravações |
nearest |
Menor latência de rede | Desconhecido | Menor | Distribuído geograficamente |
Equilíbrio entre consistência e desempenho:
graph LR
A[primary<br/>Strong consistency<br/>High latency] --> B[primaryPreferred<br/>Strong consensus]
B --> C[secondaryPreferred<br/>Weak consensus]
C --> D[secondary<br/>Weak consistency<br/>Low latency]
D --> E[nearest<br/>Not sure<br/>Lowest Latency]
style A fill:#d4edda
style D fill:#fff3cd
// === Default:All read and write operations take place in Primary ===
const user = await User.findById(userId);
// === Read from a child node(Reduce Primary Pressure)===
const products = await Product.find().read('secondary');
// === Read Preference Options ===
await Product.find().read('primary'); // Master node only
await Product.find().read('primaryPreferred'); // Preferred Primary Node
await Product.find().read('secondary'); // From the node alone
await Product.find().read('secondaryPreferred'); // Priority Node
await Product.find().read('nearest'); // Recent Online Trends
Análise dos pontos-chave:
- A leitura a partir de um nó pode envolver um atraso na replicação (normalmente < 1 segundo, embora possa ser maior em caso de problemas de rede).
- No modo
secondary, se todos os nós secundários estiverem indisponíveis, a consulta retornará um erro. - Para cenários em que a consistência não é uma preocupação importante, como relatórios e criação de índices de pesquisa, recomendamos
secondaryousecondaryPreferred.
7. Implantação do Docker em 3 nós
Explicação do conceito: O Docker Compose é a maneira mais prática de desenvolver e testar conjuntos de réplicas localmente. Uma configuração de três nós (1 primário + 2 secundários) é a configuração mínima recomendada para produção, oferecendo alta disponibilidade e failover automático.
Arquitetura de implantação:
graph TB
subgraph "Docker Compose"
M1[mongo1:27017<br/>Primary/Secondary]
M2[mongo2:27017<br/>Primary/Secondary]
M3[mongo3:27017<br/>Primary/Secondary]
end
App[Applications] --> M1
App --> M2
App --> M3
M1 <--> M2
M2 <--> M3
M1 <--> M3
style M1 fill:#d4edda
style M2 fill:#cce5ff
style M3 fill:#cce5ff
# docker-compose.yml
version: '3.8'
services:
mongo1:
image: mongo:7.0
command: mongod --replSet rs0 --port 27017
ports:
- "27017:27017"
mongo2:
image: mongo:7.0
command: mongod --replSet rs0 --port 27017
ports:
- "27018:27017"
mongo3:
image: mongo:7.0
command: mongod --replSet rs0 --port 27017
ports:
- "27019:27017"
// === Initialize the replica set ===
rs.initiate({
_id: 'rs0',
members: [
{ _id: 0, host: 'mongo1:27017' },
{ _id: 1, host: 'mongo2:27017' },
{ _id: 2, host: 'mongo3:27017' }
]
});
Etapas de implantação:
| Etapa | Comando | Descrição |
|---|---|---|
| 1 | docker-compose up -d |
Iniciar 3 contêineres |
| 2 | mongosh --port 27017 |
Conectar-se ao primeiro nó |
| 3 | rs.initiate({...}) |
Inicializar conjunto de réplicas |
| 4 | rs.status() |
Status da verificação |
| 5 | rs.conf() |
Ver configurações |
8. Failover
Explicação do conceito: O failover é a funcionalidade mais crítica de um conjunto de réplicas — quando o nó primário falha, os nós restantes elegem automaticamente um novo nó primário, e a aplicação se reconecta de forma transparente. Todo o processo geralmente é concluído em 10 a 30 segundos; durante esse período, as gravações ficam indisponíveis, mas as leituras podem ser redirecionadas para o nó secundário.
Processo de failover:
sequenceDiagram
participant App as Applications
participant P as Primary (mongo1)
participant S1 as Secondary (mongo2)
participant S2 as Secondary (mongo3)
Note over P: mongo1 Failure
P-xApp: Heartbeat Disconnected
P-xS1: Heartbeat Disconnected
P-xS2: Heartbeat Disconnected
Note over S1,S2: 10Not received yetPrimaryHeartbeat
S1->>S2: Call for an Election
S2->>S1: Vote in favor
S1->>S1: receive a majority of the votes(2/3)
Note over S1: mongo2 Become a New Primary
App->>S1: Automatic Reconnection → Writing has returned to normal
App->>S2: Continue reading(secondaryPreferred)
Note over App,S1: Total downtime: 10-30 seconds
| Cenário de falha | Impacto | Tempo de recuperação |
|---|---|---|
| Falha primária | Interrupção na gravação de 10 a 30 segundos | Recuperação automática por eleição |
| Interrupção secundária | Sem impacto | Sincronização automática após a reinicialização |
| A maioria dos nós está inativa | O conjunto de réplicas está em modo somente leitura | É necessário restaurar a maioria dos nós |
| Partição de rede | Não gravável no lado minoritário | Mesclar automaticamente após a recuperação da rede |
# === Primary Downtime Simulation ===
# kill mongo1 Container
docker stop mongo1
# === Automatic Election(30 Completed in seconds)===
# mongo2 or mongo3 Automatically promoted to Primary
# === App Auto-Reconnect(mongoose Layout)===
mongoose.connect('mongodb://mongo1,mongo2,mongo3/shopdb?replicaSet=rs0');
Solução de problemas na camada de aplicação:
| Configuração | Descrição | Valor recomendado |
|---|---|---|
| String de conexão | Listar todos os nós | mongodb://m1,m2,m3/db?replicaSet=rs0 |
| Repetir gravação | Repetir automaticamente em caso de erros de rede | retryWrites=true |
| Preferências de leitura | Degradação da leitura durante falhas | secondaryPreferred |
| Tempo limite de conexão | Duração do tempo limite de conexão | connectTimeoutMS=5000 |
| Tempo limite para seleção do servidor | Tempo limite para seleção do nó | serverSelectionTimeoutMS=30000 |
9. Melhores práticas para conjuntos de réplicas
Visão geral do conceito: As melhores práticas para conjuntos de réplicas abrangem aspectos como o número de nós, configurações de segurança, monitoramento e alertas. Seguir essas práticas pode ajudar a evitar falhas comuns em conjuntos de réplicas em ambientes de produção.
Práticas-chave:
| Prática | Descrição | Motivo |
|---|---|---|
| Configuração de 3 nós | 1 primário + 2 secundários | Configuração mínima tolerante a falhas, que suporta a falha de 1 nó |
| Número ímpar de nós | A eleição requer maioria dos votos: 3/5/7 | Um número par de nós não aumenta a tolerância a falhas |
| writeConcern: maioria | Gravação confirmada na maioria dos nós | Sem perda de dados (mesmo que o Primário falhe) |
| readPreference primaryPreferred | Leitura padrão no nó primário | Garante a consistência; se o nó primário estiver indisponível, um nó secundário é promovido |
| Monitore a janela do oplog | A renovação do oplog pode causar perda de dados | Mantenha a janela com duração superior a 24 horas |
| Configuração razoável de prioridade | O servidor mais potente tem prioridade maior | Após a recuperação de uma falha, o servidor mais potente é reeleito |
| Use o Arbiter com cautela | Use-o apenas quando o custo for um fator importante | O Arbiter não armazena dados e não pode ser restaurado |
Comparação do writeConcern:
| writeConcern | Segurança dos dados | Latência de gravação | Casos de uso |
|---|---|---|---|
w: 1 |
Apenas confirmação primária | Mais rápida | Registros, dados não críticos |
w: majority |
Confirmado pela maioria dos nós | Médio | Dados comerciais gerais |
w: majority, j: true |
Maior número de confirmações + registro gravado no disco | Mais lento | Finanças, dados críticos |
Lista de verificação para monitoramento:
| Item de monitoramento | Comando | Valor de integridade | Limite de alerta |
|---|---|---|---|
| Status do conjunto de réplicas | rs.status() |
1 PRIMÁRIO + N SECUNDÁRIOS | Sem PRIMÁRIO |
| Atraso na cópia | rs.status().members[n].optimeDate |
< 1 segundo | > 10 segundos |
| janela do oplog | rs.printReplicationInfo() |
> 72 horas | < 24 horas |
| Número de conexões | db.serverStatus().connections |
< 1.000 | > 8.000 |
| Número de eleições | rs.status().electionMetrics |
Poucas | Eleições frequentes |
▶ Exemplo: Node.js Application com Read Preference e Failover (Difficulty ⭐⭐)
// Scene: ShopHub application connecting to replica set with automatic failover
const mongoose = require('mongoose');
// Connection string with replica set members
const uri = 'mongodb://mongo1:27017,mongo2:27017,mongo3:27017/shophub?' +
'replicaSet=rs0&' +
'readPreference=primaryPreferred&' +
'w=majority&' +
'maxPoolSize=50';
// Connect with retry logic
async function connectWithRetry() {
try {
await mongoose.connect(uri, {
serverSelectionTimeoutMS: 5000,
heartbeatFrequencyMS: 10000,
retryWrites: true,
retryReads: true
});
console.log('Connected to replica set');
// Monitor replica set status
const admin = mongoose.connection.db.admin();
const status = await admin.command({ replSetGetStatus: 1 });
console.log('Replica Set Status:');
status.members.forEach(m => {
console.log(` ${m.name}: ${m.stateStr} (health: ${m.health})`);
});
// Current primary
const primary = status.members.find(m => m.stateStr === 'PRIMARY');
console.log(`Current Primary: ${primary?.name}`);
} catch (err) {
console.error('Connection failed:', err.message);
console.log('Retrying in 5 seconds...');
setTimeout(connectWithRetry, 5000);
}
}
// Handle connection events
mongoose.connection.on('disconnected', () => {
console.log('Disconnected from MongoDB');
});
mongoose.connection.on('reconnected', () => {
console.log('Reconnected to MongoDB');
});
mongoose.connection.on('error', (err) => {
console.error('MongoDB connection error:', err);
});
connectWithRetry();
Saída:
TEXT 📖 Somente leituraConnected to replicum set Replicum Set Status: mongo1:27017: PRIMARY (health: 1) mongo2:27017: SECONDARY (health: 1) mongo3:27017: SECONDARY (health: 1) Current Primary: mongo1:27017
▶ Exemplo: Implantação no Docker de um conjunto de réplicas de 3 nós + Teste de failover
# === 1. docker-compose.yml ===
# version: '3.8'
# services:
# mongo1:
# image: mongo:7.0
# command: mongod --replSet rs0 --bind_ip_all --port 27017
# ports: ["27017:27017"]
# networks: [mongo-net]
#
# mongo2:
# image: mongo:7.0
# command: mongod --replSet rs0 --bind_ip_all --port 27017
# ports: ["27018:27017"]
# networks: [mongo-net]
#
# mongo3:
# image: mongo:7.0
# command: mongod --replSet rs0 --bind_ip_all --port 27017
# ports: ["27019:27017"]
# networks: [mongo-net]
#
# networks:
# mongo-net:
# driver: bridge
# Start 3 Node
docker-compose up -d
# === 2. Initialize the replica set ===
mongosh --port 27017 --eval '
rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "localhost:27017" },
{ _id: 1, host: "localhost:27018" },
{ _id: 2, host: "localhost:27019" }
]
});
'
# === 3. Verify the replica set status ===
mongosh --port 27017 --eval 'rs.status();'
# Output:
# {
# set: 'rs0',
# members: [
# { name: 'localhost:27017', stateStr: 'PRIMARY' },
# { name: 'localhost:27018', stateStr: 'SECONDARY' },
# { name: 'localhost:27019', stateStr: 'SECONDARY' }
# ]
# }
# === 4. Writing Test Data(Automatically copy to Secondary)===
mongosh --port 27017 --eval '
db.products.insertOne({ sku: "PHONE-001", title: "Smartphone X", price: 599 });
'
# === 5. Verify Data Replication ===
mongosh --port 27018 --eval '
db.setSecondaryOk(); // Read Permitted Secondary
db.products.findOne({ sku: "PHONE-001" });
'
# Return to the same document,Proof that the copy was successful
# === 6. Failover Testing ===
# Stop Primary
docker stop mongo1
# View on mongo2
mongosh --port 27018 --eval 'rs.status();'
# Output:mongo2 Automatically promoted to PRIMARY(30 Completed in seconds)
# === 7. App Auto-Reconnect(mongoose)===
// mongoose The chain automatically discovers all nodes
const mongoose = require('mongoose');
await mongoose.connect(
'mongodb://localhost:27017,localhost:27018,localhost:27019/shopdb?replicaSet=rs0'
);
// Even if Primary fails, App automatically connects to new Primary
const products = await Product.find(); // Automatic Retry,No errors
// === 8. Read-Write Separation Configuration ===
// Write operation complete Primary
await Product.create({ sku: 'PHONE-002', title: 'Phone 2' });
// Read operations can proceed Secondary(Reduce Primary Pressure)
const products = await Product.find().read('secondaryPreferred');
// === 9. Restore a Failed Node ===
docker start mongo1
# mongo1 Automatically set to Secondary Join a Raid Group,Start Over Primary Synchronize Data
Resultado: Um conjunto de réplicas com três nós oferece alta disponibilidade; em caso de falha do servidor primário, ocorre uma transição automática em até 30 segundos, sem impacto na aplicação.
❓ Perguntas Frequentes
P: Qual é o número mínimo de nós em um conjunto de réplicas? R: 1 nó (apenas em ambiente de desenvolvimento); em produção, no mínimo 3 nós (1 primário + 2 secundários).
P: Qual é a finalidade de um nó Arbiter? R: Ele participa apenas da votação na eleição (não armazena dados). Permite que haja um número ímpar de nós sem aumentar a redundância de dados.
P: Um conjunto de réplicas pode ser escalonado horizontalmente? R: Um conjunto de réplicas oferece alta disponibilidade (HA); o escalonamento horizontal é obtido por meio de um cluster fragmentado (Sharding).
P: É possível fazer gravações nos nós secundários? R: Tecnicamente, sim, mas isso causaria conflitos de dados; por isso, essa função está desativada em ambientes de produção.
📖 Resumo
- Conjunto de réplicas: Primário + Secundário + Árbitro
- Mecanismo de eleição: baseado no Raft; requer maioria de votos
- Sincronização de dados: sincronização inicial + acompanhamento do oplog
- Separação entre leitura e gravação: readPreference
- Implantação do Docker em 3 nós
- Failover: Eleição automática + reconexão do aplicativo
📝 Exercícios
- Questão básica (⭐): Implemente um conjunto de réplicas de 3 nós usando o Docker Compose.
- Pergunta básica (⭐): Inicialize o conjunto de réplicas e verifique seu status (rs.status()).
- Exercício avançado (⭐⭐): Teste a separação entre leitura e gravação (readPreference: secondary).
- Exercício avançado (⭐⭐): Simule uma falha no sistema primário e observe o processo de eleição automática.
- Desafio (⭐⭐⭐): Implantar um conjunto de réplicas completo (3 nós + monitoramento do conjunto de réplicas + testes de recuperação de falhas).