Configuração Avançada de Redis: Replicação, Sentinel, Cluster e Cache

Visão Geral da Replicação Master-Slave no Redis

O Redis oferece dois mecanismos principais de replicação: sincronização completa (full sync) e sincronização incremental (incremental sync). Compreender esses dois processos é fundamental para implementar soluções robustas de alta disponibilidade.

Sincronização Completa (Full Replication)

Quando um Slave conecta-se ao Master pela primeira vez, ou quando a conexão é perdida e não é possível usar replicação incremental, o Redis executa uma sincronização completa. O processo ocorre da seguinte maneira:

  1. O Master recebe o comando PSYNC (ou SYNC em versões anetriores à 2.8) do Slave
  2. O Master realiza um fork do processo atual e executa BGSAVE em segundo plano
  3. Durante a geração do arquivo RDB, novas operações são armazenadas em um buffer de replicação
  4. Após a conclusão do BGSAVE, o arquivo RDB é transmitido ao Slave
  5. O Slave remove dados locais existentes e carrega o RDB em memória
  6. Por fim, o Slave aplica as operações armazenadas no buffer de replicação

Sincronização Incremental (Partial Replication)

Após a sincronização completa inicial, o Redis mantém a consistência através de replicação incremental. O Slave envia ao Master seu offset atual (equivalente à posição no binlog do MySQL), e o Master transmite apenas as operações a partir daquele ponto, incluindo dados acumulados no buffer de replicação.

Configuração de Replicação Master-Slave via Arquivo de Configuração

Ambiente de Teste


# Verificando versão do Redis
$ redis-cli -v
redis-cli 7.2.0

Configuração do Nó Slave


# Verificando dados atuais no Slave
$ redis-cli -a senha123
127.0.0.1:6379> KEYS *
(empty array)

# Editando configuração do Slave
$ vim /apps/redis/etc/redis.conf

# Definindo IP e porta do Master
replicaof 192.168.119.171 6379

# Configurando autenticação com o Master
masterauth senha123

# Reiniciando o serviço
$ systemctl restart redis-server.service

Verificação do Status de Replicação


$ redis-cli -a senha123
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:192.168.119.171
master_port:6379
master_link_status:up
master_last_io_seconds_ago:7
master_sync_in_progress:0
slave_read_repl_offset:42
slave_repl_offset:42
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:57d8d33fe921658a8763b1afddb763be41493547
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:42
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:536870912
repl_backlog_first_byte_offset:1
repl_backlog_histlen:42

# Verificando sincronização de dados
127.0.0.1:6379> KEYS *
  1) "key87"
  2) "key88"
...
 98) "key12"
 99) "key70"
100) "key32"

Implementação de Alta Disponibilidade com Redis Sentinel

O Redis Sentinel é a solução oficial para garantir alta disponibilidade, monitorando a saúde do cluster master-slave e automatizando o processo de failover quando necessário.

Preparação do Ambiente

Verificação do Master


127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.119.161,port=6379,state=online,offset=23366,lag=0
slave1:ip=192.168.119.162,port=6379,state=online,offset=23366,lag=0
master_failover_state:no-failover
master_replid:37ef554e18a0776bd1f52d5694c6a53fad96f753
master_repl_offset:23366
repl_backlog_active:1
repl_backlog_size:536870912
repl_backlog_histlen:23366

Verificação dos Slaves


127.0.0.1:6379> info replication
# Replication
role:slave
master_host:192.168.119.171
master_port:6379
master_link_status:up
master_last_io_seconds_ago:4
master_sync_in_progress:0
slave_read_repl_offset:23450
slave_repl_offset:23450
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0

Configuração do Sentinel

Criação do Arquivo de Configuração


# Copiando modelo de configuração
$ cp /usr/local/src/redis-7.2.0/sentinel.conf /apps/redis/etc/

# Editando configurações
$ vim /apps/redis/etc/sentinel.conf
$ grep -Ev "^#|^$" sentinel.conf

protected-mode no
port 26379
daemonize yes
pidfile /apps/redis/run/redis-sentinel.pid
loglevel notice
logfile "/apps/redis/logs/redis-sentinel.log"
dir /apps/redis/data

# Monitorando o master com quorum 2
sentinel monitor meugrupo 192.168.119.171 6379 2
sentinel auth-pass meugrupo senha123
sentinel down-after-milliseconds meugrupo 10000

acllog-max-len 128
sentinel parallel-syncs meugrupo 1
sentinel failover-timeout meugrupo 180000
sentinel deny-scripts-reconfig yes
SENTINEL resolve-hostnames no
SENTINEL announce-hostnames no
SENTINEL master-reboot-down-after-period meugrupo 0

Distribuição da Configuração


# Enviando configuração para os demais nós
$ scp sentinel.conf root@192.168.119.161:/apps/redis/etc/
$ scp sentinel.conf root@192.168.119.162:/apps/redis/etc/

Inicialização dos Sentinels


# Iniciando serviço em cada nó
$ redis-sentinel /apps/redis/etc/sentinel.conf

Análise dos Logs do Sentinel


# Log do nó Master
$ tail -f /apps/redis/logs/redis-sentinel.log
2648:X 08 Mar 2024 14:40:59.570 * Sentinel ID is 821c3649dc7695ce1049e7250d3620365bc4a081
2648:X 08 Mar 2024 14:40:59.570 # +monitor master meugrupo 192.168.119.171 6379 quorum 2
2648:X 08 Mar 2024 14:40:59.580 * +slave slave 192.168.119.161:6379 192.168.119.161 6379 @ meugrupo 192.168.119.171 6379
2648:X 08 Mar 2024 14:40:59.582 * Sentinel new configuration saved on disk
2648:X 08 Mar 2024 14:40:59.582 * +slave slave 192.168.119.162:6379 192.168.119.162 6379 @ meugrupo 192.168.119.171 6379
2648:X 08 Mar 2024 14:40:59.593 * Sentinel new configuration saved on disk
2648:X 08 Mar 2024 14:41:01.590 * +sentinel sentinel 8daef2c63f976764a3e16815fccc1abe8595e84b 192.168.119.162 26379 @ meugrupo 192.168.119.171 6379
2648:X 08 Mar 2024 14:41:01.603 * +sentinel sentinel 9213f768242779d8ec6ffb97ab233103d5bdf09c 192.168.119.161 26379 @ meugrupo 192.168.119.171 6379

Verificação do Status do Cluster Sentinel


# Consultando informações do cluster
$ redis-cli -h 192.168.119.171 -p 26379
192.168.119.171:26379> info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_tilt_since_seconds:-1
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=meugrupo,status=ok,address=192.168.119.171:6379,slaves=2,sentinels=3

Teste de Failover Automático

Simulação de Falha


# Encerrando o serviço do Master
$ systemctl stop redis-server.service

Verificação do Novo Master


# Verificando promoção do Slave 161
$ redis-cli -a senha123
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.119.162,port=6379,state=online,offset=737954,lag=0
master_failover_state:no-failover
master_replid:1c0a4d080609e759baa61f8d2c4ddb84532412a3
master_repl_offset:737954

# Verificando reatribuição do Slave 162
$ redis-cli -a senha123
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:192.168.119.161
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_read_repl_offset:761743
slave_repl_offset:761743
slave_priority:100
slave_read_only:1

Recuperação do Nó Original


# Reiniciando o nó que falhou
$ systemctl restart redis-server.service

# Verificando nova configuração
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:192.168.119.161
master_port:6379
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_read_repl_offset:1338829
slave_repl_offset:1338829

# Testando replicação de dados
$ redis-cli -a senha123
127.0.0.1:6379> set nome Maria
OK

# Verificando sincronização no nó recuperado
$ redis-cli -a senha123
127.0.0.1:6379> GET nome
"Maria"

Mecanismo de Funcionamento do Sentinel

O Redis Sentinel implementa alta disponibilidade através de múltiplos componentes:

  • Monitoramento: Processos independentes que verificam a disponibilidade dos nós Redis através de comandos PING
  • Arquitetura Distribuída: Múltiplos Sentinels formam um cluster para eliminar ponto único de falha
  • Detecção de Falhas: Utiliza conceitos de "subjective down" e "objective down" baseados em parâmetros de quorum
  • Eleição de Líder: Em caso de falha do master, os Sentinels elegem um líder para coordenar o failover
  • Propagação de Configuração: Notifica clientes sobre mudanças através de canais pub/sub
  • Persistência de Configuração: Mantém estado do cluster em arquivos de configuração

Implementação do Redis Cluster

Embora o Sentinel resolva problemas de alta disponibilidade, não resolve a limitação de escrita em instância única. O Redis Cluster, introduzido na versão 3.0, oferece escalabilidade horizontal através de arquitetura distribuída sem ponto central de controle.

Características Principais do Redis Cluster


1. Todos os nós conectados diretamente (mecanismo PING)
2. Falha declarada quando maioria dos nós detecta problema
3. Cliente conecta diretamente aos nós sem proxy
4. Dados distribuídos em 16384 slots (0-16383)
5. Cálculo de slot: CRC16(chave) mod 16384
6. Capacidade total = soma da memória dos nós master
7. Performance = soma da capacidade de escrita dos masters

Preparação do Ambiente


# Verificando sistema
$ cat /etc/issue
Ubuntu 18.04.6 LTS \n \l

Versão Redis: 7.2.0

分配的节点


192.168.119.161  # Master
192.168.119.162  # Master  
192.168.119.163  # Master
192.168.119.164  # Slave
192.168.119.165  # Slave
192.168.119.166  # Slave
192.168.119.167  # Reservado
192.168.119.168  # Reservado

Habilitando Cluster no Redis


# Instalando Redis 7.2.0
$ tar xvf redis7InstallScript.tar.gz
$ cd redis7InstallScript/
$ bash redis-7.2.0_install.sh

# Habilitando modo cluster
$ vim /apps/redis/etc/redis.conf
cluster-enabled yes
cluster-config-file nodes-6379.conf
masterauth senha123
requirepass senha123

# Reiniciando serviço
$ systemctl restart redis-server.service

# Verificando ativação
$ redis-cli -a senha123
127.0.0.1:6379> info cluster
# Cluster
cluster_enabled:1

Comandos de Gerenciamento do Cluster


$ redis-cli --cluster help
Cluster Manager Commands:
  create         host1:port1 ... hostN:portN
                 --cluster-replicas <arg>
  check          <host:port>
  info           <host:port>
  fix            <host:port>
  reshard        <host:port>
  rebalance      <host:port>
  add-node       new_host:port existing_host:port
  del-node       host:port node_id
  call           host:port command
  set-timeout    host:port milliseconds
  import         host:port
  backup         host:port backup_directory

Criação do Cluster


# Inicializando cluster com 3 masters e 1 replica cada
$ redis-cli -a senha123 --cluster create 192.168.119.161:6379 192.168.119.162:6379 192.168.119.163:6379 192.168.119.164:6379 192.168.119.165:6379 192.168.119.166:6379 --cluster-replicas 1

Verificação do Status do Cluster


$ redis-cli -a senha123

# Informações de replicação
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.119.165,port=6379,state=online,offset=714,lag=1
master_failover_state:no-failover

# Estado do cluster
127.0.0.1:6379> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3

# Nós do cluster
127.0.0.1:6379> cluster nodes
fdee09e80ceda3bb7fc11c8eedf3833ac09931ec 192.168.119.165:6379@16379 slave 5fcc191932ea0660f0b9a636d34e22aa926824d2 0 1710233373696 1 connected
b7d52d2dd34812c34b37fa8ad08ac83cf22cf40f 192.168.119.163:6379@16379 master - 0 1710233372684 3 connected 10923-16383
da2b2b032b96e25a97bc41e99d12ff0f56009c52 192.168.119.162:6379@16379 master - 0 1710233371000 2 connected 5461-10922
4873b095054dc04fc0ae0975dfae2df2d1756d9d 192.168.119.166:6379@16379 slave da2b2b032b96e25a97bc41e99d12ff0f56009c52 0 1710233373000 2 connected
83db779dbfc60c79983fbe22427f870f2add700c 192.168.119.164:6379@16379 slave b7d52d2dd34812c34b37fa8ad08ac83cf22cf40f 0 1710233371663 3 connected
5fcc191932ea0660f0b9a636d34e22aa926824d2 192.168.119.161:6379@16379 myself,master - 0 1710233370000 1 connected 0-5460

Distribuição de Dados no Cluster


# Verificando distribuição de chaves
$ redis-cli -a senha123 --cluster call 192.168.119.161:6379 DBSIZE
>>> Calling DBSIZE
192.168.119.161:6379: 32
192.168.119.162:6379: 32
192.168.119.163:6379: 36
192.168.119.166:6379: 32
192.168.119.165:6379: 32
192.168.119.164:6379: 36

Migração de Dados com Redis-Shake

O redis-shake é uma ferramanta poderosa para migração de dados entre diferentes instâncias Redis, suportando múltiplos modos de lietura e sincronização.

Modos de Operação


rdb_reader: Lê e parseia arquivos RDB locais para restauração
sync_reader: Sincronização online entre instâncias (suporta PSYNC)
scan_reader: Exporta chaves via SCAN e importa via RESTORE

Preparação do Ambiente


# Limpando dados existentes no cluster
$ redis-cli -a senha123 --cluster call 192.168.119.161:6379 flushall
$ redis-cli -a senha123 --no-auth-warning --cluster call 192.168.119.161:6379 dbsize
>>> Calling dbsize
192.168.119.161:6379: 0
192.168.119.162:6379: 0
192.168.119.163:6379: 0

# Verificando serviço de origem
$ systemctl status redis-server.service
● redis-server.service - Redis data structure server
   Loaded: loaded (/lib/systemd/system/redis-server.service)
   Active: active (running) since Wed 2024-03-13 01:38:08 UTC

# Gerando dados para migração
$ cat redis-client.sh 
#!/bin/bash

for i in $(seq 1 100000); do
  redis-cli -h 127.0.0.1 -a senha123 --no-auth-warning set chave-${i} valor-${i} &
done

echo "Cem mil chaves criadas!"

$ bash redis-client.sh

Execução da Migração


# Baixando ferramenta de migração
$ cd /usr/local/src/
$ wget https://github.com/tair-opensource/RedisShake/releases/download/v4.0.5/redis-shake-linux-amd64.tar.gz
$ tar xvf redis-shake-linux-amd64.tar.gz 

# Editando configuração
$ vim shake.toml

# Executando sincronização
$ ./redis-shake shake.toml

# Verificando progresso
$ redis-cli -a senha123
127.0.0.1:6379> set nome Teste
OK

# Monitorando sincronização
2024-03-13 07:03:22 INF read_count=[100001], read_ops=[0.00], write_count=[100001], write_ops=[0.00], syncing aof, diff=[0]

Validação dos Dados Migrados


# Verificando distribuição final
$ redis-cli -a senha123 --no-auth-warning --cluster call 192.168.119.161:6379 dbsize
>>> Calling dbsize
192.168.119.161:6379: 33365
192.168.119.162:6379: 33280
192.168.119.166:6379: 33280
192.168.119.163:6379: 33356
192.168.119.164:6379: 33356
192.168.119.165:6379: 33365

Sistema de Cache com PHP, MySQL e Redis

A implementação de cache com Redis dramatically reduz a carga em bancos de dados relacionais, melhorando significativamente o tempo de resposta de aplicações web.

Infraestrutura Necessária

Instalação do MySQL


# Configurando container MySQL
$ mkdir -p /data/mysql
$ nerdctl run -d -p 3306:3306 --name mysql-container-test \
  -v /data/mysql:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD="minhasenha123" \
  registry.cn-hangzhou.aliyuncs.com/zhangshijie/mysql:5.7.36

# Conectando e criando banco
$ mysql -uroot -h 192.168.119.171 -pminhasenha123
mysql> CREATE database loja_teste;
mysql> USE loja_teste;

# Criando tabela de produtos
mysql> CREATE TABLE produtos (
    -> id_produto BIGINT PRIMARY KEY AUTO_INCREMENT,
    -> nome_produto VARCHAR(50),
    -> preco DOUBLE
    -> ) Engine = InnoDB;

# Inserindo dados iniciais
mysql> INSERT INTO produtos(nome_produto, preco) VALUES ('Servidores Privados','5.00');
mysql> INSERT INTO produtos(nome_produto, preco) VALUES ('Bancos Gerenciados', '15.00');
mysql> INSERT INTO produtos(nome_produto, preco) VALUES ('Armazenamento Bloco', '10.00');
mysql> INSERT INTO produtos(nome_produto, preco) VALUES ('Kubernetes Gerenciado','60.00');
mysql> INSERT INTO produtos(nome_produto, preco) VALUES ('Balanceador Carga', '10.00');

mysql> SELECT * FROM produtos;
+------------+-------------------------+-------+
| id_produto | nome_produto            | preco |
+------------+-------------------------+-------+
|          1 | Servidores Privados     |     5 |
|          2 | Bancos Gerenciados      |    15 |
|          3 | Armazenamento Bloco     |    10 |
|          4 | Kubernetes Gerenciado   |    60 |
|          5 | Balanceador Carga       |    10 |
+------------+-------------------------+-------+

Instalação do Redis


# Instalando Redis
$ tar xf redis7InstallScript.tar.gz -C /usr/local/src
$ cd /usr/local/src/redis7InstallScript/
$ bash redis-7.2.0_install.sh

Configuração do Servidor Web


# Verificando sistema
$ lsb_release -a
Distributor ID: Ubuntu
Description: Ubuntu 20.04.6 LTS

# Instalando Apache e PHP
$ apt update
$ apt -y install apache2 php-fpm libapache2-mod-php php7.4-mysql php7.4-redis

Teste de Conexão Redis


# Arquivo de teste
$ cat /var/www/html/teste_redis.php 
<?php
  $redis = new Redis();
  $redis->connect('192.168.119.171', 6379);
  $redis->auth('senha123');
  echo $redis->get("nome");
?>

Implementação de Cache MySQL-Redis


# Sistema de cache completo
$ cat /var/www/html/cache-loja.php 
<?php
  $redis = new Redis();
  $redis->connect('192.168.119.171', 6379);
  $redis->auth('senha123');

  $chaveCache = 'PRODUTOS';

  if (!$redis->get($chaveCache)) {
    $origem = 'Cache Miss: dados do MySQL';
    $nome_banco = 'loja_teste';
    $usuario_banco = 'root';
    $senha_banco = 'minhasenha123';
    $host_mysql = '192.168.119.171';

    $pdo = new PDO('mysql:host=' . $host_mysql . '; dbname=' . $nome_banco, $usuario_banco, $senha_banco);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    $sql = "SELECT * FROM produtos";
    $stmt = $pdo->prepare($sql);
    $stmt->execute();

    while ($linha = $stmt->fetch(PDO::FETCH_ASSOC)) {
      $produtos[] = $linha;
    }

    $redis->set($chaveCache, serialize($produtos));
    $redis->expire($chaveCache, 60);
  } else {
    $origem = 'Cache Hit: dados do Redis';
    $produtos = unserialize($redis->get($chaveCache));
  }

  echo $origem . ': <br>';
  print_r($produtos);
?>

Verificação do Cache


# Primeira requisição (Cache Miss)
# O sistema busca dados do MySQL e armazena no Redis

# Segunda requisição (Cache Hit)
# O sistema retorna dados diretamente do Redis

# Inspecionando dados no Redis
$ redis-cli -a senha123
127.0.0.1:6379> KEYS *
1) "nome"
2) "PRODUTOS"

127.0.0.1:6379> TTL PRODUTOS
(integer) 46

127.0.0.1:6379> get PRODUTOS
"a:5:{i:0;a:3:{s:10:\"id_produto\";s:1:\"1\";s:12:\"nome_produto\";s:18:\"Servidores Privados\";s:5:\"preco\";s:1:\"5\";}i:1;a:3:{s:10:\"id_produto\";s:1:\"2\";s:12:\"nome_produto\";s:18:\"Bancos Gerenciados\";s:5:\"preco\";s:2:\"15\";}i:2;a:3:{s:10:\"id_produto\";s:1:\"3\";s:12:\"nome_produto\";s:19:\"Armazenamento Bloco\";s:5:\"preco\";s:2:\"10\";}i:3;a:3:{s:10:\"id_produto\";s:1:\"4\";s:12:\"nome_produto\";s:21:\"Kubernetes Gerenciado\";s:5:\"preco\";s:2:\"60\";}i:4;a:3:{s:10:\"id_produto\";s:1:\"5\";s:12:\"nome_produto\";s:16:\"Balanceador Carga\";s:5:\"preco\";s:2:\"10\";}}"

Resultados da Implementação

A integração entre MySQL e Redis demonstra benefícios significativos:

  • Primeira requisição: dados buscam no MySQL e são armazenados em cache
  • Requisições subsequentes: dados retornam diretamente do Redis
  • Tempo de expiração configurável (60 segundos no exemplo)
  • Serialização automática de objetos PHP para armazenamento

Tags: Redis replication sentinel cluster high-availability

Publicado em 5-30 18:41 por Thomas