Redis - 11. Cluster (Cluster)

1. Problemas existentes

A capacidade de um único redis é limitada, como expandir? Continuar adicionando memória e hardware?
O volume de gravação simultânea de um único redis é muito grande e há um gargalo de desempenho. Como resolver isso?
Os clusters são fornecidos no redis 3.0 para resolver esses problemas.

2. O que é um cluster

O cluster redis é a expansão horizontal do redis, ou seja, inicia N nós redis, distribui e armazena todos os dados nesses N nós, e cada nó armazena 1/N do total de dados.

Conforme mostrado na figura abaixo: um cluster redis composto por 3 mestres e 3 escravos, cada mestre assume um terço dos dados solicitados e gravados pelo cliente, quando o mestre desliga, o escravo substitui automaticamente o mestre para atingir alta disponibilidade.

3. Como configurar o cluster?

3.1. Requisitos: configurar 3 clusters master 3 slave

Em seguida, vamos configurar um cluster com 3 mestres e 3 escravos.Um escravo é travado abaixo de cada mestre.Após o mestre desligar, o escravo será promovido a mestre.

Por conveniência, simulamos em uma máquina, o ip da minha máquina é: 192.168.200.129, 6 nós diferentes são diferenciados por porta, as informações de configuração são as seguintes

3.2. Crie um diretório de trabalho de caso: cluster

Execute o seguinte comando para criar o diretório /opt/cluster.Todas as operações desta vez são executadas no diretório do cluster.

# 方便演示,停止所有的redis
ps -ef | grep redis | awk -F" " '{print $2;}' | xargs kill -9
mkdir /opt/cluster
cd /opt/cluster/

3.3. Copie redis.conf para o diretório do cluster

redis.conf é o arquivo de configuração padrão do redis

cp /opt/redis-6.2.1/redis.conf /opt/cluster/

3.4. Crie o arquivo de configuração do master1: redis-6379.conf

Crie um arquivo redis-6379.conf no diretório /opt/cluster, o conteúdo é o seguinte, observe que 192.168.200.129 é o ip da máquina de teste, você precisa substituí-lo pelo seu próprio

include /opt/cluster/redis.conf
daemonize yes
bind 192.168.200.129
dir /opt/cluster/

port 6379
dbfilename dump_6379.rdb
pidfile /var/run/redis_6379.pid
logfile "./6379.log"

# 开启集群设置
cluster-enabled yes
# 设置节点配置文件
cluster-config-file node-6379.conf
# 设置节点失联时间,超过该时间(毫秒),集群自动进行主从切换
cluster-node-timeout 15000

3.5. Crie o arquivo de configuração do master2: redis-6380.conf

Crie um arquivo redis-6380.conf no diretório /opt/cluster, o conteúdo é o seguinte, semelhante ao mestre acima, apenas substitua 6379 por 6380

include /opt/cluster/redis.conf
daemonize yes
bind 192.168.200.129
dir /opt/cluster/

port 6380
dbfilename dump_6380.rdb
pidfile /var/run/redis_6380.pid
logfile "./6380.log"

# 开启集群设置
cluster-enabled yes
# 设置节点配置文件
cluster-config-file node-6380.conf
# 设置节点失联时间,超过该时间(毫秒),集群自动进行主从切换
cluster-node-timeout 15000

3.6. Crie um arquivo de configuração master3: redis-6381.conf

Crie um arquivo redis-6381.conf no diretório /opt/cluster com o seguinte conteúdo

include /opt/cluster/redis.conf
daemonize yes
bind 192.168.200.129
dir /opt/cluster/

port 6381
dbfilename dump_6381.rdb
pidfile /var/run/redis_6381.pid
logfile "./6381.log"

# 开启集群设置
cluster-enabled yes
# 设置节点配置文件
cluster-config-file node-6381.conf
# 设置节点失联时间,超过该时间(毫秒),集群自动进行主从切换
cluster-node-timeout 15000

3.7. Crie um arquivo de configuração para slave1: redis-6389.conf

Crie um arquivo redis-6389.conf no diretório /opt/cluster com o seguinte conteúdo

include /opt/cluster/redis.conf
daemonize yes
bind 192.168.200.129
dir /opt/cluster/

port 6389
dbfilename dump_6389.rdb
pidfile /var/run/redis_6389.pid
logfile "./6389.log"

# 开启集群设置
cluster-enabled yes
# 设置节点配置文件
cluster-config-file node-6389.conf
# 设置节点失联时间,超过该时间(毫秒),集群自动进行主从切换
cluster-node-timeout 15000

3.8. Crie um arquivo de configuração para slave2: redis-6390.conf

Crie um arquivo redis-6390.conf no diretório /opt/cluster com o seguinte conteúdo

include /opt/cluster/redis.conf
daemonize yes
bind 192.168.200.129
dir /opt/cluster/

port 6390
dbfilename dump_6390.rdb
pidfile /var/run/redis_6390.pid
logfile "./6390.log"

# 开启集群设置
cluster-enabled yes
# 设置节点配置文件
cluster-config-file node-6390.conf
# 设置节点失联时间,超过该时间(毫秒),集群自动进行主从切换
cluster-node-timeout 15000

3.9. Crie um arquivo de configuração para slave3: redis-6391.conf

Crie um arquivo redis-6391.conf no diretório /opt/cluster com o seguinte conteúdo

include /opt/cluster/redis.conf
daemonize yes
bind 192.168.200.129
dir /opt/cluster/

port 6391
dbfilename dump_6391.rdb
pidfile /var/run/redis_6391.pid
logfile "./6391.log"

# 开启集群设置
cluster-enabled yes
# 设置节点配置文件
cluster-config-file node-6391.conf
# 设置节点失联时间,超过该时间(毫秒),集群自动进行主从切换
cluster-node-timeout 15000

3.10, iniciar mestre, escravo1, escravo2

# 方便演示,停止所有的redis
ps -ef | grep redis | awk -F" " '{print $2;}' | xargs kill -9
# 下面启动6个redis
redis-server /opt/cluster/redis-6379.conf
redis-server /opt/cluster/redis-6380.conf
redis-server /opt/cluster/redis-6381.conf
redis-server /opt/cluster/redis-6389.conf
redis-server /opt/cluster/redis-6390.conf
redis-server /opt/cluster/redis-6391.conf

3.11. Verifique o status de inicialização de 6 redis

ps -ef | grep redis

3.12. Certifique-se de que o arquivo node-xxxx.conf foi gerado normalmente

Posteriormente, mesclaremos 6 instâncias em um cluster. Antes de combinar, precisamos garantir que, após o início das 6 instâncias redis, os arquivos nodes-xxxx.conf sejam gerados normalmente, da seguinte maneira, o diretório /opt/cluster foi realmente gerado com sucesso

3.13. Sintetize 6 nós em um cluster

Execute o seguinte comando para combinar 6 redis

/opt/redis-6.2.1/src/redis-cli --cluster create --cluster-replicas 1192.168.200.129:6379 192.168.200.129:6380 192.168.200.129:6381 192.168.200.129:6389 192.168.200.129:6390 192.168.200.129:6391
  • O comando combinado será seguido pela lista ip:port de todos os nós, e os múltiplos são separados por espaços. Observe que o ip não deve escrever 127.0.0.1, mas o ip real
  • --cluster-replicas 1: Indica que o cluster está configurado da forma mais simples, ou seja, cada mestre é equipado com 1 escravo, e 6 nós formam 3 mestres e 3 escravos

 O processo de execução é o seguinte, durante o qual determinaremos se o método de alocação é o mesmo, digite: sim e aguarde alguns segundos, a integração do cluster foi bem-sucedida

[root@hspEdu01 src]# redis-cli --cluster create --cluster-replicas 1 192.168.200.129:6379 192.168.200.129:6380 192.168.200.129:6381 192.168.200.129:6389 192.168.200.129:6390 192.168.200.129:6391
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 192.168.200.129:6390 to 192.168.200.129:6379
Adding replica 192.168.200.129:6391 to 192.168.200.129:6380
Adding replica 192.168.200.129:6389 to 192.168.200.129:6381
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: ccf3abb791e026380ad3ad2a166aa788df738437 192.168.200.129:6379
slots:[0-5460] (5461 slots) master
M: 3c372392d5a91dad64a6febadfe9524ea2cbd8c0 192.168.200.129:6380
slots:[5461-10922] (5462 slots) master
M: 2c905be9c975be367bd66c962167beca1ef66af3 192.168.200.129:6381
slots:[10923-16383] (5461 slots) master
S: 4a0f860081b969162767aac26801994de54d80a5 192.168.200.129:6389
replicates ccf3abb791e026380ad3ad2a166aa788df738437
S: 62c9f37a362459c212e8af6dd744b6562f5fe6a7 192.168.200.129:6390
replicates 3c372392d5a91dad64a6febadfe9524ea2cbd8c0
S: a2f89efc09681520f9d9502707b18e1f46a40b90 192.168.200.129:6391
replicates 2c905be9c975be367bd66c962167beca1ef66af3
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
.
>>> Performing Cluster Check (using node 192.168.200.129:6379)
M: ccf3abb791e026380ad3ad2a166aa788df738437 192.168.200.129:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: 2c905be9c975be367bd66c962167beca1ef66af3 192.168.200.129:6381
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 62c9f37a362459c212e8af6dd744b6562f5fe6a7 192.168.200.129:6390
slots: (0 slots) slave
replicates 3c372392d5a91dad64a6febadfe9524ea2cbd8c0
M: 3c372392d5a91dad64a6febadfe9524ea2cbd8c0 192.168.200.129:6380
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 4a0f860081b969162767aac26801994de54d80a5 192.168.200.129:6389
slots: (0 slots) slave
replicates ccf3abb791e026380ad3ad2a166aa788df738437
S: a2f89efc09681520f9d9502707b18e1f46a40b90 192.168.200.129:6391
slots: (0 slots) slave
replicates 2c905be9c975be367bd66c962167beca1ef66af3
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

3.14. Conecte-se aos nós do cluster e visualize as informações do cluster: nós do cluster

Você precisa usar o comando redis-cli -c para se conectar a qualquer um dos 6 nós no cluster. Observe que é um pouco diferente dos parâmetros de conexão anteriores. Há um parâmetro -c adicional após o comando redis-cli, o que significa que o cluster é usado para conectar. Mais tarde, use clusternodes para visualizar as informações do nó do cluster, como segue

192.168.200.129:6379> cluster nodes
2c905be9c975be367bd66c962167beca1ef66af3 192.168.200.129:6381@16381 master - 0
1650194157604 3 connected 10923-16383
62c9f37a362459c212e8af6dd744b6562f5fe6a7 192.168.200.129:6390@16390 slave
3c372392d5a91dad64a6febadfe9524ea2cbd8c0 0 1650194158611 2 connected
3c372392d5a91dad64a6febadfe9524ea2cbd8c0 192.168.200.129:6380@16380 master - 0
1650194158000 2 connected 5461-10922
4a0f860081b969162767aac26801994de54d80a5 192.168.200.129:6389@16389 slave
ccf3abb791e026380ad3ad2a166aa788df738437 0 1650194156000 1 connected
ccf3abb791e026380ad3ad2a166aa788df738437 192.168.200.129:6379@16379
myself,master - 0 1650194157000 1 connected 0-5460
a2f89efc09681520f9d9502707b18e1f46a40b90 192.168.200.129:6391@16391 slave
2c905be9c975be367bd66c962167beca1ef66af3 0 1650194159617 3 connected
192.168.200.129:6379>

Conforme mostrado na figura abaixo, explique os resultados dos nós do cluster. Primeiro, observe as notas em vermelho. Cada nó no cluster gerará um ID, e essas informações do ID serão gravadas no arquivo node-xxxx.conf. Por quê? gerar uma identificação? ?

Porque o ip e a porta do nó podem mudar, mas o ID do nó não mudará, e outros nós podem reconhecer cada nó através do ID de outros nós.

3.15. Verifique as operações de leitura e gravação dos dados do cluster

Da seguinte forma, conectamos o nó 6379 e, em seguida, executamos uma operação definida, o efeito é o seguinte, a gravação é bem-sucedida

[root@hspEdu01 cluster]# redis-cli -c -h 192.168.200.129 -p 6379
192.168.200.129:6379> set name ready
-> Redirected to slot [5798] located at 192.168.200.129:6380
OK
192.168.200.129:6380>

Você deve ter notado que obviamente estamos operando no 6379, mas a requisição é encaminhada para processamento no nó 6380. Aqui está o conhecimento do slot do qual falaremos mais tarde, vamos olhar para trás primeiro.

4. Como o cluster redis aloca esses 6 nós?

Um cluster tem pelo menos 3 nós mestres, porque a eleição de um novo mestre requer o consentimento de mais da metade dos nós mestres do cluster para ser eleito com sucesso. Se houver apenas dois nós mestres e um deles travar, as condições para eleger um novo mestre não pode ser cumprido.

A opção --cluster-replicas 1 indica que queremos criar um nó escravo para cada nó mestre no cluster.

O princípio de alocação tenta garantir que cada biblioteca mestre seja executada em um ip diferente e que cada biblioteca mestre e escrava não estejam no mesmo ip, de modo a obter alta disponibilidade.

5. O que são slots (slots)

Conforme mostrado na figura abaixo, vamos dar uma olhada em algumas saídas de informações durante o processo de fusão do cluster

 O cluster Redis divide internamente 16384 slots (slots). Ao mesclar, cada slot será mapeado para um mestre. Por exemplo, a relação entre os três mestres e slots acima é a seguinte:

nó mestre redis Faixa de slots
master1 (porta: 6379) [0-5460], a posição do slot começa em 0 e 0 significa o primeiro slot
master2 (porta: 6380) [5460-10922]
master3 (porta: 6381) [10923-16383]
escravo1,escravo2,escravo3 Não há slot para o nó escravo e o escravo é usado como substituto do mestre

E cada chave no banco de dados pertence a um dos slots 16384. Ao ler e gravar dados por meio da chave, o redis precisa calcular os slots correspondentes à chave de acordo com a chave e, em seguida, encontrar os slots correspondentes de acordo com o relacionamento de mapeamento entre os slots e o mestre. O nó redis, os dados correspondentes à chave estão neste nó.

Use a fórmula CRC16(key)%16384 no cluster para calcular a qual slot a chave pertence

6. Insira os valores no cluster

Toda vez que você inserir e consultar o valor da chave no redis-cli, o redis calculará o slot correspondente à chave. Se não for o slot do nó redis atual, o redis relatará um erro e informará o endereço e a porta da instância redis para ir. O efeito é o seguinte, conectamos A instância 6379 é usada para operar k1. O nó descobre que o slot de k1 está acima de 6381 e retorna uma mensagem de erro. O que devo fazer?

[root@hspEdu01 cluster]# redis-cli -h 192.168.200.129 -p 6379
192.168.200.129:6379> set k1 v1
(error) MOVED 12706 192.168.200.129:6381

Usar o cliente redis-cli para fornecer o parâmetro -c pode resolver esse problema, o que significa que ele é executado em um modo de cluster. Quando o nó atual não puder manipular o comando ao executar o comando, ele redirecionará automaticamente a solicitação para o destino nó. O efeito é o seguinte, ele é redirecionado para 6381

[root@hspEdu01 cluster]# redis-cli -c -h 192.168.200.129 -p 6379
192.168.200.129:6379> set k1 v1
-> Redirected to slot [12706] located at 192.168.200.129:6381
OK
192.168.200.129:6381>

Da mesma forma, a execução de get será redirecionada, o efeito é o seguinte

[root@hspEdu01 cluster]# redis-cli -c -h 192.168.200.129 -p 6379
192.168.200.129:6379> get k1
-> Redirected to slot [12706] located at 192.168.200.129:6381
"v1"
192.168.200.129:6381>

Se não estiver em um slot, as operações multi-chave, como mget e mset, não podem ser usadas. O efeito é o seguinte

192.168.200.129:6381> mset k1 v1 k2 v2
(error) CROSSSLOT Keys in request don't hash to the same slot
192.168.200.129:6381> mget k1 k2
(error) CROSSSLOT Keys in request don't hash to the same slot

Você pode usar {} para definir o conceito de um grupo, para que o mesmo valor de chave em {} na chave possa ser colocado em um slot e o efeito seja o seguinte

192.168.200.129:6381> mset k1{g1} v1 k2{g1} v2 k3{g1} v3
OK
192.168.200.129:6381> mget k1{g1} k2{g1} k3{g1}
1) "v1"
2) "v2"
3) "v3"

7. Alguns comandos relacionados ao slot

  • cluster keyslot <key>: Calcula o slot correspondente à chave
  • cluster coutkeysinslot <slot>: Obtenha o número de chaves no slot slot
  • cluster getkeysinslot <slot> <count> retorna as chaves nos slots de contagem
192.168.200.129:6381> cluster keyslot k1{g1}
(integer) 13519
192.168.200.129:6381> cluster countkeysinslot 13519
(integer) 3
192.168.200.129:6381> cluster getkeysinslot 13519 3
1) "k1{g1}"
2) "k2{g1}"
3) "k3{g1}"

8. Recuperação de falhas

Se o nó mestre ficar offline, o nó escravo pode ser promovido a nó mestre? Nota: Aguarde 15 segundos.
Vamos tentar, da seguinte maneira, conectar-se ao master1 e, em seguida, parar o master1

[root@hspEdu01 cluster]# redis-cli -c -h 192.168.200.129 -p 6379
192.168.200.129:6379> shutdown
not connected>

Execute o seguinte comando para conectar-se ao master1 e ver as informações dos nós do cluster

redis-cli -c -h 192.168.200.129 -p 6380
cluster nodes

A saída é a seguinte, você pode ver que slave1 (6389) realmente se tornou o mestre e seu mestre original: master1 (6379) está offline

Em seguida, vamos iniciar 6379 novamente e, em seguida, ver como o cluster se parece. O comando é o seguinte

[root@hspEdu01 cluster]# redis-server /opt/cluster/redis-6379.conf
[root@hspEdu01 cluster]# redis-cli -c -h 192.168.200.129 -p 6379
192.168.200.129:6379> cluster nodes

Os resultados da execução são os seguintes, 6379 tornou-se um escravo e está suspenso em 6389

 Se o mestre e o escravo de um determinado slot estiverem inativos, o serviço redis pode continuar?

Neste momento, depende do valor do parâmetro cluster-require-full-coverage

  • sim (valor padrão): todo o cluster é incapaz de fornecer serviços
  • no: Todos os dados do slot do tempo de inatividade não podem ser usados ​​e os outros slots são normais

9. SpringBoot integra cluster redis

9.1. Apresente a configuração maven do redis

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

9.2. Configure as informações do cluster redis em application.properties

# 集群节点(host:port),多个之间用逗号隔开
spring.redis.cluster.nodes=192.168.200.129:6379,192.168.200.129:6380,192.168.200.129:6381,192.168.200.129:6389,192.168.200.129:6390,192.168.200.129:6391
# 连接超时时间(毫秒)
spring.redis.timeout=60000

9.3. Use a classe de ferramenta RedisTemplate para operar redis

O RedisTemplate é usado no springboot para operar o redis, e esse objeto precisa ser injetado no nosso bean. O código é o seguinte:

@Autowired
private RedisTemplate<String, String> redisTemplate;

// 用下面5个对象来操作对应的类型
this.redisTemplate.opsForValue(); //提供了操作string类型的所有方法
this.redisTemplate.opsForList(); // 提供了操作list类型的所有方法
this.redisTemplate.opsForSet(); //提供了操作set的所有方法
this.redisTemplate.opsForHash(); //提供了操作hash表的所有方法
this.redisTemplate.opsForZSet(); //提供了操作zset的所有方法

9.4, código de amostra do RedisTemplate

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @className RedisController
 * @date 2022/6/21
 **/
@RestController
@RequestMapping("/redis")
public class RedisController {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * string测试
     * @return
     */
    @RequestMapping("/stringTest")
    public String stringTest() {
        this.redisTemplate.delete("name");
        this.redisTemplate.opsForValue().set("name", "路人");
        String name = this.redisTemplate.opsForValue().get("name");
        return name;
    }

    /**
     * list测试
     * @return
     */
    @RequestMapping("/listTest")
    public List<String> listTest() {
        this.redisTemplate.delete("names");
        this.redisTemplate.opsForList().rightPushAll("names", "刘德华", "张学友", "郭富城", "黎明");
        List<String> courses = this.redisTemplate.opsForList().range("names", 0, -1);
        return courses;
    }

    /**
     * set类型测试
     * @return
     */
    @RequestMapping("setTest")
    public Set<String> setTest() {
        this.redisTemplate.delete("courses");
        this.redisTemplate.opsForSet().add("courses", "java", "spring", "springboot");
        Set<String> courses = this.redisTemplate.opsForSet().members("courses");
        return courses;
    }

    /**
     * hash类型测试
     * @return
     */
    @RequestMapping("hashTest")
    public Map<Object, Object> hashTest() {
        this.redisTemplate.delete("userMap");
        Map<String, String> map = new HashMap<>();
        map.put("name", "路人");
        map.put("age", "30");
        this.redisTemplate.opsForHash().putAll("userMap", map);
        Map<Object, Object> userMap = this.redisTemplate.opsForHash().entries("userMap");
        return userMap;
    }

    /**
     * zset类型测试
     * @return
     */
    @RequestMapping("zsetTest")
    public Set<String> zsetTest() {
        this.redisTemplate.delete("languages");
        this.redisTemplate.opsForZSet().add("languages", "java", 100d);
        this.redisTemplate.opsForZSet().add("languages", "c", 95d);
        this.redisTemplate.opsForZSet().add("languages", "php", 70);
        Set<String> languages = this.redisTemplate.opsForZSet().range("languages", 0, -1);
        return languages;
    }

    /**
     * 查看redis机器信息
     * @return
     */
    @RequestMapping(value = "/info", produces = MediaType.TEXT_PLAIN_VALUE)
    public String info() {
        return this.redisTemplate.execute((RedisCallback<String>) connection ->
                String.valueOf(connection.execute("info")));
    }

}

Acho que você gosta

Origin blog.csdn.net/qq_34272760/article/details/125397953
Recomendado
Clasificación