【0】常用架构种类
(0.1)单机Redis
(0.2)单纯的Redis主从复制
(0.3)哨兵Sentinel+Redis主从复制集群(实现高可用自动故障转移)
(0.4)Redis Cluster 分布式数据库集群
(0.5)第三方中间件+Redis 主从复制
【1】Redis 主从复制
一般情况下,这种架构,主节点负责写数据,从节点负责读数据,主节点定期吧数据同步到从节点保证数据一致性。
常用的Redis主从拓扑:
(1)一主一从 (2)一主多从 (3)级联复制
【1.1】一主一从
主要是主库写,从库读。且主库不开启持久化,从库开启AOF即可。
【1.2】一主多从
实现读写分离,针对读较多的场景,主写从读,读有多个节点来分担,考虑到主库压力不建议从库超过3个,且受网络影响较大。
【1.3】级联复制
A->B->C/D,A复制到B,B复制C和D
【1.4】Redis主从复制过程
(1)基本过程
从库启动后:保存主节点信息;主从建立连接;从发送ping命令;验证权限;同步数据集;持续复制;
(2)主从复制的缺点
【若主节点出现问题,则不能提供服务,需人工配置】
【主从复制主节点的写能力单机,能力有限】
【单机节点的存储能力有限】
【主从故障如何故障转移的问题】
1)主节点(master)0故障,从节点 slvae-1端执行 slaveof no one 后变成新主节点;
2)其他的节点成为新主节点的从节点,并从新节点辅助数据
3)需人工操作,无法实现高可用
当 master 关闭持久化时,复制的安全性
在使用 Redis 复制功能时的设置中,强烈建议在 master 和在 slave 中启用持久化。当不可能启用时,例如由于非常慢的磁盘性能而导致的延迟问题,应该配置实例来避免重置后自动重启。
为了更好地理解为什么关闭了持久化并配置了自动重启的 master 是危险的,检查以下故障模式,这些故障模式中数据会从 master 和所有 slave 中被删除:
我们设置节点 A 为 master 并关闭它的持久化设置,节点 B 和 C 从 节点 A 复制数据。
节点 A 崩溃,但是他有一些自动重启的系统可以重启进程。但是由于持久化被关闭了,节点重启后其数据集合为空。
节点 B 和 节点 C 会从节点 A 复制数据,但是节点 A 的数据集是空的,因此复制的结果是它们会销毁自身之前的数据副本。
【2】哨兵机制
(Sentinel)的 Redis主从复制集群
核心参考:https://www.cnblogs.com/bingshu/p/9776610.html
参考:https://www.jianshu.com/p/d6d2325a5ec7
拓扑,三台哨兵+主从复制集群(三台哨兵是为了实现哨兵之间的高可用)
哨兵机制的出现是为了解决主从复制的缺点
【2.1】哨兵机制的高可用原理
当主节点出现故障时,由 Redis Sentinel 自动完成故障发现和故障转移,并通知应用方,实现高可用。
其实整个过程只需要一个哨兵节点来完成,实现使用 Raft 算法(选举算法)实现选举机制,选出一个哨兵节点来完成转移和通知。
【2.2】哨兵是如何进行定时监控任务的?
》每个哨兵节点每10s 会向主节点和从节点发送info命令获取最新的拓扑结构图,哨兵配置时只需要对主节点的监控即可,通过向主节点发送info,获取从节点的信息,并当有心节点假如时可以马上感知到。
》每个哨兵节点每隔2S会向redis数据节点的指定频道上发送该哨兵节点对于主节点的判断以及当前哨兵节点的信息,同时每个哨兵节点也会订阅该频道,来了解其他哨兵节点的信息以及对主节点的判断,其实就是通过 Publish 和 subscribe 来完成的。(以此机制来投票选出处理问题的哨兵)
》每间隔1S每个哨兵会向主节点、从节点以及其余哨兵节点发送以此ping命令做一次心跳检测,这个也是哨兵用来判断节点是否正常的重要依据。
【2.3】Redis哨兵选举流程(3个中,谁是老大)
主观下线
哨兵(Sentinel)节点会每秒一次的频率向建立了命令连接的实例发送PING命令,如果在down-after-milliseconds
毫秒内没有做出有效响应包括(PONG/LOADING/MASTERDOWN)以外的响应。
哨兵就会将该实例在本结构体中的状态标记为SRI_S_DOWN
主观下线
主观下线 适用于所有 主节点 和 从节点。如果在 down-after-milliseconds
毫秒内,Sentinel
没有收到 目标节点 的有效回复,则会判定 该节点 为 主观下线。
客观下线
当一个哨兵节点发现主节点处于主观下线状态是,会向其他的哨兵节点发出询问,该节点是不是已经主观下线了。如果超过配置参数quorum
个节点认为是主观下线时,
该哨兵节点就会将自己维护的结构体中该主节点标记为SRI_O_DOWN
客观下线询问命令SENTINEL is-master-down-by-addr <ip> <port> <current_epoch> <run_id>
客观下线 只适用于 主节点。如果 主节点 出现故障,Sentinel
节点会通过 sentinel is-master-down-by-addr
命令,向其它 Sentinel
节点询问对该节点的 状态判断。
如果超过 <quorum>
个数的节点判定 主节点 不可达,则该 Sentinel
节点会判断 主节点 为 客观下线。
》每个在线的哨兵节点都可以称为领导者,但它确认(比如哨兵3)主节点下线时,会向其他哨兵发is-master-down-by-addr命令,征求判断并要求将自己设置为领导者,由领导者处理故障转移;
》当其他哨兵收到次命令时,可以同意或拒绝它称为领导者
》如果哨兵3发现自己的选举票大于50%时,将称为领导者,如果没有超过,继续换人选举。
【2.4】Resi哨兵故障转移步骤
》由Sentinel节点定期监控发现主节点是否出现了故障
》当主节点出现故障,此时3个Sentinel节点共同选举了 Sentinel3 节点为领导,负载处理主节点的故障转移。
》由Sentinel3领导者节点执行故障转移,过程和主从复制一样,但自己执行
1)主节点挂了,将 slave1 脱离原从节点,升级为主节点
2)将其他的从节点比如 slave-2/slave3 等指向新的主节点 slave1
3)通知客户端主节点已经更换成 slave1
4)原主节点修复恢复后,将原主节点变成从节点,指向新的主节点 slave1
【2.5】哨兵的常见配置参数
# redis进程是否以守护进程的方式运行,yes为是,no为否(不以守护进程的方式运行会占用一个终端)。
daemonize no
# 指定redis进程的PID文件存放位置
pidfile /var/run/redis.pid
# redis进程的端口号
port 6379
# 绑定的主机地址
bind 127.0.0.1
# 客户端闲置多长时间后关闭连接,默认此参数为0即关闭此功能
timeout 300
# redis日志级别,可用的级别有debug.verbose.notice.warning
loglevel verbose
# log文件输出位置,如果进程以守护进程的方式运行,此处又将输出文件设置为stdout的话,就会将日志信息输出到/dev/null里面去了
logfile stdout
# 设置数据库的数量,默认为0可以使用select <dbid>命令在连接上指定数据库id
databases 16
# 指定在多少时间内刷新次数达到多少的时候会将数据同步到数据文件
save <seconds> <changes>
# 指定存储至本地数据库时是否压缩文件,默认为yes即启用存储
rdbcompression yes
# 指定本地数据库文件名
dbfilename dump.db
# 指定本地数据问就按存放位置
dir ./
# 指定当本机为slave服务时,设置master服务的IP地址及端口,在redis启动的时候他会自动跟master进行数据同步
slaveof <masterip> <masterport>
# 当master设置了密码保护时,slave服务连接master的密码
masterauth <master-password>
# 设置redis连接密码,如果配置了连接密码,客户端在连接redis是需要通过AUTH<password>命令提供密码,默认关闭
requirepass footbared
# 设置同一时间最大客户连接数,默认无限制。redis可以同时连接的客户端数为redis程序可以打开的最大文件描述符,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回 max number of clients reached 错误信息
maxclients 128
# 指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key。当此方法处理后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis新的vm机制,会把Key存放内存,Value会存放在swap区
maxmemory<bytes>
# 指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为no。
appendonly no
# 指定跟新日志文件名默认为appendonly.aof
appendfilename appendonly.aof
# 指定更新日志的条件,有三个可选参数 - no:表示等操作系统进行数据缓存同步到磁盘(快),always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全), everysec:表示每秒同步一次(折衷,默认值);
appendfsync everysec
【3】Redis Cluster
分布式数据库集群
【3.1】RedisCluster是redis的分布式解决方案,支持分片,Redis3.0版本之前,可以通过Redis Sentinel(哨兵)来实现高可用(HA),从3.0版本后,官方退出了Redis Cluster,它的主要用途是实现数据分片(Data Sharding),不过听杨可以实现HA,是官方当前推荐的方案,有效的解决了Redis分布式的需求,当一个服务挂了可以快速的切换到另外一个服务。
优势:去中心化,去中间件,集群中任意节点平等,任意节点可以获得全局的数据。(和Mysql MGR有点类似)
【3.2】分布式数据库把整个数据按分区规则映射到多个节点,即把数据划分到多个节点上,每个节点负责整体数据的一个子集。
【3.2.1】案例:
比如有3000条用户数据,有3个redis节点,将3000条数据分成3份(分块分片了,并不是绝对的3个1000行数据),按分区规则分别存入到3个redis节点。
【3.2.2】分区规则:
Redis 集群使用了 哈希分区的 “虚拟槽分区” 方式(槽:slot),即哈希槽的概念
所有的键根据 哈希函数(CRC16[KEY]&16383) 即 HASH_SLOT(key)= CRC16(key) % 16384
映射到 0~16383槽内,共16384个槽位。每个节点维护部分槽及槽锁映射的键值数据。
Redis 集群有16384个哈希槽(slot),当需要在 Redis 集群中放置一个 key-value 时,每个key通过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽。
【3.2.3】redis用虚拟槽分区的原因:
解耦数据与节点的关系,节点自身维护槽映射关系,分布式存储。
Redis 集群有16384个哈希槽(slot),当需要在 Redis 集群中放置一个 key-value 时,每个key通过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽。
【3.2.4】RedisCluster的缺陷:
》键的批量操作支持有限,比如mset,mget,如果多个键映射在不同的槽,就不支持了
》不支持多数据库,只有0,select 0
》复制结构只支持单层结构,不支持树形结构(即不能级联复制了)
》键事务支持有限,当多个Key分部在不同节点时无法使用事务,同一节点是支持事务的
》键是数据分区的最小粒度,不能将一个很大的键值对 映射到不同的节点
【4】关于第三方中间件+Redis主从复制(3.0以后很少使用中间件了)
【4.1】Twemproxy
Redis client=》Twemproxy=》根据路由规则发送到对应的Redis实例=》Twemproxy把结果返回 Client
【4.2】Codis(如果真要用,用这个)
【4.3】基本架构