谈谈Redis的各种集群方案、及优缺点对比

 

在服务开发中,单机都会存在单点故障的问题,即服务部署在一台服务器上,一旦服务器宕机服务就不可用,所以为了让服务高可用,分布式服务就出现了,将同一服务部署到多台机器上,即使其中几台服务器宕机,只要有一台服务器可用服务就可用。

Redis 也是一样,为了解决单机故障引入了主从模式,但主从模式存在一个问题:master 节点故障后服务,需要人为的手动将 slave 节点切换成为 master 节点后服务才恢复。Redis 为解决这一问题又引入了哨兵模式,哨兵模式能在 master 节点故障后能自动将 slave 节点提升成 master 节点,不需要人工干预操作就能恢复服务可用。但是主从模式、哨兵模式都没有达到真正的数据 sharding 存储,每个 Redis 实例中存储的都是全量数据,所以 Redis cluster 就诞生了,实现了真正的数据分片存储。但是由于 Redis cluster 发布得比较晚(2015 年才发布正式版 ),各大厂等不及了,陆陆续续开发了自己的 Redis 数据分片集群模式,比如:Twemproxy、Codis 等。

主从模式

Redis 单节点虽然有通过 RDB 和 AOF 持久化机制能将数据持久化到硬盘上,但数据是存储在一台服务器上的,如果服务器出现硬盘故障等问题,会导致数据不可用,而且读写无法分离,读写都在同一台服务器上,请求量大时会出现 I/O 瓶颈。

为了避免单点故障 和 读写不分离,Redis 提供了复制(replication)功能实现 master 数据库中的数据更新后,会自动将更新的数据同步到其他 slave 数据库上。

https://mmbiz.qpic.cn/mmbiz_png/TLH3CicPVibrfhU2QWkMalo5QEpst3KlJEJw9ab9NC1icMfm8rjAowdHNsd9jXnJY4VGWp5HRlmNcUare0GRr8drw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1

如上 Redis 主从结构特点:一个 master 可以有多个 slave 节点;slave 节点可以有 slave 节点,从节点是级联结构。

主从模式优缺点

-「优点:」 主从结构具有读写分离,提高效率、数据备份,提供多个副本等优点。 -「不足:」 最大的不足就是主从模式不具备自动容错和恢复功能,主节点故障,集群则无法进行工作,可用性比较低,从节点升主节点需要人工手动干预。

普通主从

普通的主从模式,当主数据库崩溃时,需要手动切换从数据库成为主数据库:

  1. 在从数据库中使用 SLAVE NO ONE 命令将从数据库提升成主数据继续服务。
  2. 启动之前崩溃的主数据库,然后使用 SLAVEOF 命令将其设置成新的主数据库的从数据库,即可同步数据。

哨兵模式

上面介绍的主从同步/复制的模式,当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用,这时候就需要哨兵模式登场了。

哨兵模式是从 Redis 的 2.6 版本开始提供的,但是当时这个版本的模式是不稳定的,直到 Redis 的 2.8 版本以后,这个哨兵模式才稳定下来。

哨兵模式核心还是主从复制,只不过在相对于主从模式在主节点宕机导致不可写的情况下,多了一个竞选机制:从所有的从节点中竞选出新的主节点。竞选机制的实现,是依赖于在系统中启动一个 sentinel 进程。

Redis 哨兵模式:

https://mmbiz.qpic.cn/mmbiz_png/TLH3CicPVibrfhU2QWkMalo5QEpst3KlJE1dkbvRBHicwskgWAvYicSKz8t0q9LPc5ts41adicOBNTvVE81bBEnYKvw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1

如上图,哨兵本身也有单点故障的问题,所以在一个一主多从的 Redis 系统中,可以使用多个哨兵进行监控,哨兵不仅会监控主数据库和从数据库,哨兵之间也会相互监控。每一个哨兵都是一个独立的进程,作为进程,它会独立运行。

多哨兵互相监控:

https://mmbiz.qpic.cn/mmbiz_png/TLH3CicPVibrfhU2QWkMalo5QEpst3KlJEPzn3EQLk5H5Kc1OdgictcCnd8ItmtwOU9iacAng27u9FicUrUumMFvKYA/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1

哨兵模式的作用

监控所有服务器是否正常运行:通过发送命令返回监控服务器的运行状态,处理监控主服务器、从服务器外,哨兵之间也相互监控。

故障切换:当哨兵监测到 master 宕机,会自动将 slave 切换成 master,然后通过「发布订阅模式」通知其他的从服务器,修改配置文件,让它们切换 master。同时那台有问题的旧主也会变为新主的从,也就是说当旧的主即使恢复时,并不会恢复原来的主身份,而是作为新主的一个从。

单哨兵模式

Redis 提供了哨兵的命令,哨兵事一个独立的进程,作为进程,它会独立运行,其原理事通过哨兵进程发送命令,得到 Redis 服务器响应,从而监控运行的多个 Redis 实例。

多哨兵模式

单个哨兵进程对 Redis 进行监控可能会因为哨兵进程宕掉导致失败,为此我们可以使用多个哨兵进行监控,各哨兵之间还会进行监控,这样就形成了多哨兵模式。哨兵集群至少要三个哨兵才能保证健壮性。

Redis Sentinel 是一个分布式架构,其中包含若干个 Sentinel 节点和 Redis 数据节点,每个 Sentinel 节点会对数据节点和其余哨兵节点进行监控

哨兵实现原理

哨兵在启动进程时,会读取配置文件的内容,通过如下的配置找出需要监控的主数据库:


# master-name 是主数据库的名字
# ip 和 port 是当前主数据库地址和端口号
# quorum 表示在执行故障切换操作前,需要多少哨兵节点同意。
sentinel monitor master-name ip port quorum

这里之所以只需要连接主节点,是因为通过主节点的 info 命令,可以获取从节点信息,进而可以和从节点也建立连接,同时也能通过主节点的 info 信息知道新增从节点的信息。

一个哨兵节点可以监控多个主节点,但是并不提倡这么做,因为当哨兵节点崩溃时,同时有多个集群切换会发生故障。哨兵启动后,会与主数据库建立两条连接。

订阅主数据库 _sentinel_:hello 频道以获取同样监控该数据库的哨兵节点信息定期向主数据库发送 info 命令,获取主数据库本身的信息。

跟主数据库建立连接后会定时执行以下三个操作:

  • 每隔 10s 向 master 和 slave 发送 info 命令。作用是获取当前数据库信息,比如发现新增从节点时,会建立连接,并加入到监控列表中,当主从数据库的角色发生变化进行信息更新。
  • 每隔 2s 向主数据里和从数据库的 _sentinel_:hello 频道发送自己的信息。作用是将自己的监控数据和哨兵分享。每个哨兵会订阅数据库的 _sentinel:hello 频道,当其他哨兵收到消息后,会判断该哨兵是不是新的哨兵,如果是则将其加入哨兵列表,并建立连接。
  • 每隔 1s 向所有主从节点和所有哨兵节点发送 ping 命令,作用是监控节点是否存活。

主观下线和客观下线

哨兵节点发送 ping 命令时,当超过一定时间(down-after-millisecond)后,如果节点未回复,则哨兵认为主观下线。主观下线表示当前哨兵认为该节点已经下面,如果该节点为主数据库,哨兵会进一步判断是够需要对其进行故障切换,这时候就要发送命令(SENTINEL is-master-down-by-addr)询问其他哨兵节点是否认为该主节点是主观下线,当达到指定数量(quorum)时,哨兵就会认为是客观下线。

当主节点客观下线时就需要进行主从切换,主从切换的步骤为:

  • 选出领头哨兵。
  • 领头哨兵所有的 slave 选出优先级最高的从数据库。优先级可以通过 slave-priority 选项设置。
  • 如果优先级相同,则从复制的命令偏移量越大(即复制同步数据越多,数据越新),越优先。
  • 如果以上条件都一样,则选择 run ID 较小的从数据库。

选出一个从数据库后,哨兵发送 slave no one 命令升级为主数据库,并发送 slaveof 命令将其他从节点的主数据库设置为新的主数据库。

哨兵模式优缺点

优点

哨兵模式是基于主从模式的,解决可主从模式中 master 故障不可以自动切换故障的问题。

不足/问题

  • (1)是一种中心化的集群实现方案:始终只有一个 Redis 主机来接收和处理写请求,写操作受单机瓶颈影响。
  • (2)集群里所有节点保存的都是全量数据,浪费内存空间,没有真正实现分布式存储。数据量过大时,主从同步严重影响 master 的性能。
  • (3)Redis 主机宕机后,哨兵模式正在投票选举的情况之外,因为投票选举结束之前,谁也不知道主机和从机是谁,此时 Redis 也会开启保护机制,禁止写操作,直到选举出了新的 Redis 主机。

主从模式或哨兵模式每个节点存储的数据都是全量的数据,数据量过大时,就需要对存储的数据进行分片后存储到多个 Redis 实例上。此时就要用到 Redis Sharding 技术。

哨兵模式部署

一个实例:

sentinal1 : 26379
sentinal2 : 26380
sentinal3 : 26381
master : 6379
slave1: 6381
slave2: 6381

1, 部署 Redis 数据节点

1.1 redis-6379.conf

port 6379
daemonize yes
logfile "6379.log"
dbfilename "dump-6379.rdb"
dir "/opt/soft/redis/data"

启动:

redis-server redis-6379.conf
redis-cli -h 127.0.0.1 -p 6379 ping
PONG

1.2 redis-6380.conf

port 6380
daemonize yes
logfile "6380.log"
dbfilename "dump-6380.rdb"
dir "/opt/soft/redis/data"
slaveof 127.0.0.1 6379

1.3 redis-6381.conf

port 6381
daemonize yes
logfile "6381.log"
dbfilename "dump-6381.rdb"
dir "/opt/soft/redis/data"
slaveof 127.0.0.1 6379

启动:

redis-server redis-6380.conf
redis-server redis-6381.conf

redis-cli -h 127.0.0.1 -p 6380 ping
redis-cli -h 127.0.0.1 -p 6381 ping

确认主从关系, 主节点的视角, 他有两个从节点。

redis-cli -h 127.0.0.1 -p 6379 info replication

从节点的视角, 他有一个主节点。

redis-cli -h 172.0.0.1 -p 6380 info replication

1.4 redis-sentinel-26379.conf

port 26379
daemonize yes
logfile "26379.log"
dbfilename "dump-26379.rdb"
dir "/opt/soft/redis/data"

## sentinel_1 节点需要监控 127.0.0.1:6379 这个主节点
## 2 代表判断失败至少需要 2 个 Sentinel 节点同意。
## mymaster 是主节点的别名。
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000

启动:

redis-sentinel redis-sentinel-26379.conf
redis-sentinel redis-sentinel-26380.conf
redis-sentinel redis-sentinel-26381.conf
## 或者
redis-server redis-sentinel-26379.conf --sentinel

确认

Sentinel 节点本质上是一个特殊的 Redis 节点, 所以也可以通过 info 命令来查询它的相关信息。

redis-cli -h 127.0.0.1 -p 26739 info Sentinel

配置优化

# Sentinel 会顶起监控主节点
# quorum 代表要判定主节点最终不可达所需票数
# 实际上 Sentinel 会对所有节点进行监控, 
# 因为 Sentinel 会从主节点中获取从节点和其余 Sentinel 的信息
sentinel monitor <master-name> <ip> <port> <quorum>

# 每个 Sentinel 节点都要通过定期发送 ping 命令来判断 Redis 数据节点
# 和其余 Sentinel 节点是否可达, 
# 如果超过了 down-after-milliseconds 配置的时间且没有
# 有效回复, 则判定节点不可达, 
# <times> (单位:毫秒)
# 对 Sentinel 、主节点、从节点的失败判定同时有效
sentinel down-after-milliseconds <master-name> <times>

# 限制在一次故障转移后, 每次向主节点发起复制操作的从几点个数
sentinel parallel-syncs <master-name> <nums>


sentinel failover-timeout <master-name> times>
sentinel auth-pass <master-name> <password>

# 故障转移期间, 当一些警告级别的 Sentinel 事件发生, 会出发对应路径的脚本
sentinel notification-script <master-name> <script-path>

# 故障转移结束后, 触发脚本:
# 参数;
# <master-name> <role> (leader, observer)
#   <state> <from-ip> 
#   <from-port> <to-ip> <to-port>
sentinel client-reconfig-script <master-name> <script-path>

脚本格式:

#!/bin/sh
# 获取所有参数
msg=$*
# 报警脚本接口, 将 msg 作为参数
# 脚本需可执行权限, 最大执行时间不能超过 60 秒。
# 如果 exit 1 结束, 稍后将重试
# 如果 exit 2 或以上, 不会重试。
exit 0

和普通 Redis 数据节点一样, Sentinel 节点也支持动态设置参数

sentinel set <param> <value>

支持的参数:

quorum:
    sentinel set mymaster quorum 2
down-after-milliseconds
    sentinel set down-after-milliseconds 30000
failover-timeout
    sentinel set failover-timeout 360000
parallel-syncs
    sentinel set mymaster parallel-syncs 2
notification-script
    sentinel set mymaster notification-script /opt.ns.sh
client-reconfig-script
    sentinel set mymaster lcient-reconfig-script /opt/my.sh
auth-pass
    sentinel set mymaster auth-pass masterPassword
  • sentinel set 命令只对当前 Sentinel 节点有效。
  • 命令执行成功会立即刷新配置文件,和数据节点需要执行 config rewrite 刷新不同
  • 建议所有 Sentinel 节点配置尽可能一直。
  • Sentinel 对外不支持 config命令。

Redis 6 安装

1, 下载: http://download.redis.io/releases/redis-6.0.6.tar.gz

cd /data/redis/src
tar xzf redis-6.0.6.tar.gz
cd redis-6.0.6
## 如果不先执行这一步,则在编译时会遇到如下这样的错误:
## zmalloc.h:50:10: 致命错误:jemalloc/jemalloc.h:没有那个文件或目录
make persist-settings
make
make install
## 默认安装到目录“/usr/local”,如果需要安装到其它目录,可指定 make 的参数“PREFIX”,如:
make install PREFIX=/usr/local/redis-6.0.6

错误解决

In file included from adlist.c:34:0:
zmalloc.h:50:31: fatal error: jemalloc/jemalloc.h: No such file or directory
 #include <jemalloc/jemalloc.h>

针对这个错误,我们可以在README.md 文件中看到解释。

---------

Selecting a non-default memory allocator when building Redis is done by setting
the `MALLOC` environment variable. Redis is compiled and linked against libc
malloc by default, with the exception of jemalloc being the default on Linux
systems. This default was picked because jemalloc has proven to have fewer
fragmentation problems than libc malloc.

To force compiling against libc malloc, use:

    % make MALLOC=libc

To compile against jemalloc on Mac OS X systems, use:

    % make MALLOC=jemalloc

Verbose build
-------------

网上大部分解决办法都是错误的,如下文:

(错误解决办法)

make MALLOC=libc

正确解决办法(针对2.2以上的版本) 清理上次编译残留文件,重新编译

make distclean  && make

导致出现这个错误的原因

错误的本质是我们在开始执行 make 时遇到了错误(大部分是由于gcc未安装),然后我们安装好了 gcc 后,我们再执行 make ,这时就出现了 jemalloc/jemalloc.h: No such file or directory。这是因为上次的编译失败,有残留的文件,我们需要清理下,然后重新编译就可以了。

网上的解决办法是有什么错误吗?

网上的解决办法虽然最后也是可以成功安装好 redis ,但是是有一些隐患的,首先我们要知道redis 需要使用内存分配器的, make MALLOC=jemalloc 就是指定内存分配器为 jemalloc ,make MALLOC=libc 就是指定内存分配器为 libc ,这个是有安全隐患的,jemalloc 内存分配器在实践中处理内存碎片是要比libc 好的,而且在README.md 文档也说明到了,jemalloc内存分配器也是包含在源码包里面的,可以在deps 目录下看到 jemalloc 目录。

以上参考: https://www.cnblogs.com/oxspirt/p/11392437.html

server.h:1051:5: 错误:expected specifier-qualifier-list before ‘_Atomic’
server.c:1032:31: 错误:‘struct redisServer’没有名为‘logfile’的成员

编译 redis-6.0,要求 C11 编译器.

## 安装gcc套装
yum install cpp
yum install binutils
yum install glibc
yum install glibc-kernheaders
yum install glibc-common
yum install glibc-devel
yum install gcc
yum install make
## 或者
yum grouplist
yum groupinstall 'Development Tools'

## 升级 gcc
yum -y install centos-release-scl
yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils
scl enable devtoolset-9 bash

## GCC 安装

yum -y install gcc automake autoconf libtool make

参考链接: https://blog.csdn.net/tarenadjq/article/details/108647006

EOF


Power by TeXt.