Redis集群高可用
2020-08-14 12:51 ☆野生架构师☆ 阅读(289) 评论(0) 编辑 收藏 举报简介
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在很多项目中都在使用,建议研究下,单机、群集、备份、数据类型等。