zoukankan      html  css  js  c++  java
  • Redis集群高可用

    简介

      Redis 是一个开源的使用 ANSI C 语言编写、遵守 BSD 协议、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API的非关系型数据库。

      Redis作为基于键值对的NoSQL数据库,具有高性能、丰富的数据结构、持久化、高可用、分布式等特性,同时Redis本身非常稳定,已经得到业界的广泛认可和使用。  

      Github 源码:https://github.com/antirez/redis

      Redis 官网:https://redis.io/

      目前很多项目都在使用它,我的项目基本都在用,另外6.0版本开始支持多线程,大伙可以研究下。

      Redis支持的数据类型?   

        1、String字符串:

          格式: set key value

          string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。

          string类型是Redis最基本的数据类型,一个键最大能存储512MB。 

        2、Hash(哈希)

          格式: hmset name  key1 value1 key2 value2

          Redis hash 是一个键值(key=>value)对集合。

          Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。 

        3、List(列表)

          Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)

          格式: lpush  name  value

          在 key 对应 list 的头部添加字符串元素

          格式: rpush  name  value

          在 key 对应 list 的尾部添加字符串元素

          格式: lrem name  index

          key 对应 list 中删除 count 个和 value 相同的元素

          格式: llen name  

          返回 key 对应 list 的长度 

        4、Set(集合)

          格式: sadd  name  value

          Redis的Set是string类型的无序集合。

          集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。 

        5、zset(sorted set:有序集合)

          格式: zadd  name score value

          Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。

          不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。

          zset的成员是唯一的,但分数(score)却可以重复。

      在Redis中,有两种持久化方式

        1、RDB:

          rdb是Redis DataBase缩写

          功能核心函数rdbSave(生成RDB文件)和rdbLoad(从文件加载内存)两个函数

        2、AOF:

          Aof是Append-only file缩写      

          每当执行服务器(定时)任务或者函数时flushAppendOnlyFile 函数都会被调用, 这个函数执行以下两个工作

          AOF写入保存:

            WRITE:根据条件,将 aof_buf 中的缓存写入到 AOF 文件

            SAVE:根据条件,调用 fsync 或 fdatasync 函数,将 AOF 文件保存到磁盘中。  

        存储:

            内容是redis通讯协议(RESP )格式的命令文本存储。

        比较:

          1、aof文件比rdb更新频率高,优先使用aof还原数据。

          2、aof比rdb更安全也更大

          3、rdb性能比aof好

          4、如果两个都配了优先加载AOF

    集群方案

        1、主从复制

          同Mysql主从复制的原因一样,Redis虽然读取写入的速度都特别快,但是也会产生读压力特别大的情况。为了分担读压力,Redis支持主从复制,读写分离。一个Master可以有多个Slaves。

          优点

            1、数据备份

            2、读写分离,提高服务器性能

          缺点

            1、不能自动故障恢复,RedisHA系统(需要开发)

            2、无法实现动态扩容

        2、哨兵机制      

          Redis Sentinel是社区版本推出的原生高可用解决方案,其部署架构主要包括两部分:Redis Sentinel集群和Redis数据集群。

          其中Redis Sentinel集群是由若干Sentinel节点组成的分布式集群,可以实现故障发现、故障自动转移、配置中心和客户端通知。Redis Sentinel的节点数量要满足2n+1(n>=1)的奇数个。 

         

          优点

            1、自动化故障恢复

          缺点

            1、Redis 数据节点中 slave 节点作为备份节点不提供服务

            2、无法实现动态扩容

        3、Cluster      

          Redis Cluster是社区版推出的Redis分布式集群解决方案,主要解决Redis分布式方面的需求,比如,当遇到单机内存,并发和流量等瓶颈的时候,Redis Cluster能起到很好的负载均衡的目的。

          Redis Cluster着眼于提高并发量。

          群集至少需要3主3从,且每个实例使用不同的配置文件。

          在redis-cluster架构中,redis-master节点一般用于接收读写,而redis-slave节点则一般只用于备份, 其与对应的master拥有相同的slot集合,若某个redis-master意外失效,则再将其对应的slave进行升级为临时redis-master。

          在redis的官方文档中,对redis-cluster架构上,有这样的说明:在cluster架构下,默认的,一般redis-master用于接收读写,而redis-slave则用于备份,当有请求是在向slave发起时,会直接重定向到对应key所在的master来处理。 但如果不介意读取的是redis-cluster中有可能过期的数据并且对写请求不感兴趣时,则亦可通过readonly命令,将slave设置成可读,然后通过slave获取相关的key,达到读写分离。具体可以参阅redis官方文档等相关内容

          优点

            1、解决分布式负载均衡的问题。具体解决方案是分片/虚拟槽slot。

            2、可实现动态扩容

            3、P2P模式,无中心化

          缺点

            1、为了性能提升,客户端需要缓存路由表信息

            2、Slave在集群中充当“冷备”,不能缓解读压力

    环境

      此次搭建一个6节点的Redis集群,包括3个主节点和3个从节点。

    部署

      之前我在通过docker安装redis集群的时候,一步一步的来安装,但是docker虽然简化了一些繁琐的步骤,但不可避免的还是有一些步骤需要自己手动写命令,太老火了,所以我就在网上找了一个shell脚本,通过一个命令即可完成redis集群的部署。

    #!/bin/bash
    
    # 创建redis挂载目录
    echo "step 1 -> 创建redis安装位置------"
    mkdir -p /data/redis-cluster
    cd /data/redis-cluster
    
    echo "step 2 -> 创建redis-cluster.tmpl模板------"
    cat <<'EOF'> redis-cluster.tmpl
    port ${PORT}
    protected-mode no
    cluster-enabled yes
    cluster-config-file nodes.conf
    cluster-node-timeout 5000
    cluster-announce-ip ${CLUSTER_ANNOUNCE_IP}
    cluster-announce-port ${PORT}
    cluster-announce-bus-port 1${PORT}
    EOF
    
    #for port in `seq 6391 6396`; do 
    #firewall-cmd --zone=public --add-port=${port}/tcp --permanent
    #done
    
    echo "step 3 -> 创建redis数据配置挂载目录------"
    
    CLUSTER_ANNOUNCE_IP=192.168.1.30
    echo ${CLUSTER_ANNOUNCE_IP}
    
    for port in `seq 6391 6396`; do 
    mkdir -p ./${port}/conf 
    && PORT=${port} CLUSTER_ANNOUNCE_IP=${CLUSTER_ANNOUNCE_IP}  envsubst < ./redis-cluster.tmpl > ./${port}/conf/redis.conf 
    && mkdir -p ./${port}/data; 
    done
    
    echo "step 4 -> 创建redis docker-compose.yaml 模板------"
    cat <<EOF > docker-compose.yaml
    version: '3'
    services:
      redis-6391:
        image: redis:latest
        container_name: redis-6391
        command: redis-server /usr/local/etc/redis/redis.conf --appendonly yes
        privileged: true
        restart: always
        volumes:
          - /data/redis-cluster/6391/data:/data
          - /data/redis-cluster/6391/conf/redis.conf:/usr/local/etc/redis/redis.conf
        ports:
          - 6391:6391
          - 16391:16391
      redis-6392:
        image: redis:latest
        container_name: redis-6392
        command: redis-server /usr/local/etc/redis/redis.conf --appendonly yes
        privileged: true
        restart: always
        volumes:
          - /data/redis-cluster/6392/data:/data
          - /data/redis-cluster/6392/conf/redis.conf:/usr/local/etc/redis/redis.conf
        ports:
          - 6392:6392
          - 16392:16392
      redis-6393:
        image: redis:latest
        container_name: redis-6393
        command: redis-server /usr/local/etc/redis/redis.conf --appendonly yes
        privileged: true
        restart: always
        volumes:
          - /data/redis-cluster/6393/data:/data
          - /data/redis-cluster/6393/conf/redis.conf:/usr/local/etc/redis/redis.conf
        ports:
          - 6393:6393
          - 16393:16393
      redis-6394:
        image: redis:latest
        container_name: redis-6394
        command: redis-server /usr/local/etc/redis/redis.conf --appendonly yes
        privileged: true
        restart: always
        volumes:
          - /data/redis-cluster/6394/data:/data
          - /data/redis-cluster/6394/conf/redis.conf:/usr/local/etc/redis/redis.conf
        ports:
          - 6394:6394
          - 16394:16394
      redis-6395:
        image: redis:latest
        container_name: redis-6395
        command: redis-server /usr/local/etc/redis/redis.conf --appendonly yes
        privileged: true
        restart: always
        volumes:
          - /data/redis-cluster/6395/data:/data
          - /data/redis-cluster/6395/conf/redis.conf:/usr/local/etc/redis/redis.conf
        ports:
          - 6395:6395
          - 16395:16395
      redis-6396:
        image: redis:latest
        container_name: redis-6396
        command: redis-server /usr/local/etc/redis/redis.conf --appendonly yes
        privileged: true
        restart: always
        volumes:
          - /data/redis-cluster/6396/data:/data
          - /data/redis-cluster/6396/conf/redis.conf:/usr/local/etc/redis/redis.conf
        ports:
          - 6396:6396
          - 16396:16396
    EOF
    
    echo "docker-compose redis 生成成功!"
    
    echo "step 5 -> 运行docker-compose 部署启动redis容器------"
    # 运行docker-compose启动redis容器
    docker-compose -f docker-compose.yaml up -d
    
    exist=$(docker inspect --format '{{.State.Running}}' redis-6391)
    
    if [[${exist}!='true']];
    then
        sleep 3000
    else
        echo 'redis容器启动成功!'
        IP_RESULT=""
        CONTAINER_IP=""
        for port in `seq 6391 6396`;
        do
        #CONTAINER_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' redis-${port})
        IP_RESULT=${IP_RESULT}${CLUSTER_ANNOUNCE_IP}":"${port}" "
        done
    fi
    echo "获取redis容器ip和端口号:" ${IP_RESULT}
    
    echo "step 6 -> redis 执行集群指令------"
    docker run --rm -it inem0o/redis-trib create --replicas 1 ${IP_RESULT}

    注:CLUSTER_ANNOUNCE_IP:要换成你的公网ip,就是你可以访问到的ip,同时执行脚本前,先开放6391~6396 以及16391~16396端口

    测试

      1、进入某个节点

        docker exec -it redis-6391 redis-cli -p 6391 -c

      2、输入测试命令

        查看集群信息 cluster info

        查看集群状态 cluster nodes

        查看slots分片 cluster slots

    容灾

      1、停掉主节点redis-6391,看节点redis-6394是否会接替它的位置,由slave变成master节点。

        docker stop redis-6391

        docker exec -it redis-6392 redis-cli -p 6392 -c

        cluster nodes 查看是否切换成功

      2、启动6391,它将自动切换为slave节点。

        docker start redis-6391

        docker exec -it redis-6392 redis-cli -p 6392 -c

        cluster nodes 查看是否切换成功

      注:我今天重新搭建了环境测试了下是没问题的。

    管理

      1、创建

        docker-compose up -d

      2、销毁

        docker-compose down 

      3、查看

        docker-compose ps  

      4、启动

        docker-compose start  

      5、停止

        docker-compose stop

    总结

      redis在很多项目中都在使用,建议研究下,单机、群集、备份、数据类型等。

  • 相关阅读:
    可持久化线段树学习笔记
    GDI+学习之路
    tcpdump——分析tcp关闭4次过程
    nasm过程调用
    ios学习:NSURLConnection 和 Json数据解析
    ios学习:文件简单读写
    JSONP原理及其简单封装
    JSP使用JSTL
    JDBC
    Apache无法正常启动的原因
  • 原文地址:https://www.cnblogs.com/shumtn/p/13501572.html
Copyright © 2011-2022 走看看