单机/单点
- 单点故障/瓶颈:多个节点负载:面向数据:
- 一变多(一致性<弱一致,最终一致性>)》可用性
- 最终一致性:一部分角色确认 》 网络分区(脑裂)》过半机制
镜像:数据容量不变
切片:横向扩展
集群分类
- 主从复制 Replication:镜像:增删改(主<退化到单节点>)查询负载到从节点
高可用 Sentinel - 分布式 twemproxy:切片
集群 Cluster
Redis主从复制
主从复制 Replication
- 一个Redis服务可以有多个该服务的复制品,这个Redis服务称为Master,其他复制品称为Slaves
- 只要网络连接正常,Master会一直将自己的数据更新同步给Slaves,保持主从同步
- 只有Master可以执行写命令,Slaves只能执行读命令
- 从服务器执行客户端发送的读命令,比如GET、LRANGE、SMEMMBERS、HGET、ZRANGE等等
- 客户端可以连接Slaves执行读请求,来降低Master的读压力
主从复制创建
- redis-server --slaveof <master-ip> <master-port>,配置当前服务称为某Redis服务的Slave
- redis-server --port 6380 --slaveof 127.0.0.1 6379
- SLAVEOF host port命令,将当前服务器状态从Master修改为别的服务器的Slave
- redis > SLAVEOF 192.168.1.1 6379,将服务器转换为Slave
- redis > SLAVEOF NO ONE ,将服务器重新恢复到Master,不会丢弃已同步数据
- 配置方式:启动时,服务器读取配置文件,并自动成为指定服务器的从服务器
- slaveof <masterip> <masterport>
- slaveof 127.0.0.1 6379
主从复制演示
redis-server --slaveof <master-ip> <master-port>
redis-server --port 6380 --slaveof 127.0.0.1 6379
redis-cli -p 6380 -n 0
测试Master和Slave的读写SLAVEOF 命令
从服务器连接到192.168.56.201的6379端口
redis > SLAVEOF 192.168.56.201 6379
redis > SLAVEOF NO ONE ,将服务器重新恢复到Master,不会丢弃已同步数据
redis > SET n2key 5
redis > get n2key
redis > keys *
redis > SLAVEOF 192.168.56.201 6379
redis > keys *配置方式
在node2节点安装配置redis服务,修改配置文件
slaveof 192.168.56.201 6379
启动服务,观察和node1的同步
redis > SLAVEOF NO ONE,观察是否可写set、keys *
主从复制问题
- 一个Master可以有多个Slaves
- Slave下线,只是读请求的处理性能下降
- Master下线,写请求无法执行
- 其中一台Slave使用SLAVEOF no one命令成为Master,其它Slaves执行SLAVEOF命令指向这个新的Master,从它这里同步数据
以上过程是手动的,能够实现自动,这就需要Sentinel哨兵,实现故障转移Failover操作
Redis哨兵
高可用 Sentinel
- 官方提供的高可用方案,可以用它管理多个Redis服务实例
- 编译后产生redis-sentinel程序文件
- Redis Sentinel是一个分布式系统,可以在一个架构中运行多个Sentinel进程
启动 Sentinel
- 将src目录下产生redis-sentinel程序文件复制到$REDIS_HOME/bin
- 启动一个运行在Sentinel模式下的Redis服务实例
- redis-sentinel
- redis-server /path/to/sentinel.conf --sentinel
- Redis Sentinel是一个分布式系统,可以在一个架构中运行多个Sentinel进程
监控 Monitoring
- Sentinel会不断检查Master和Slaves是否正常
- 每一个Sentinel可以监控任意多个Master和该Master下的Slaves
Sentinel网络
监控同一个Master的Sentinel会自动连接,组成一个分布式的Sentinel网络,互相通信并交换彼此关于被监视服务器的信息。
下图中3个Sentinel监控着S1和它的2个Slave:
服务器下线
- 当一个sentinel认为被监视的服务器已经下线时,它会向网络中的其他Sentinel进行确认,判断该服务器是否真的已经下线
- 如果下线的服务器为主服务器,那么sentinel网络将对下线主服务器进行自动故障转移,通过将下线主服务器的某个从服务器提升为新的主服务器,并让其从服务器转为复制新的主服务器,以此来让系统重新回到上线的状态
服务器下线后重新上线
Sentinel 配置文件
- 至少包含一个监控配置选项,用于指定被监控Master的相关信息
- Sentinel monitor<name><ip><port><quorum>,
例如 sentinel monitor mymaster 127.0.0.1 6379 2
监视mymaster的主服务器,服务器ip和端口,将这个主服务器判断为下线失效至少需要2个Sentinel同意,如果多数Sentinel同意才会执行故障转移 - Sentinel会根据Master的配置自动发现Master的Slaves
- Sentinel默认端口号为26379
Sentinel 配置举例
- 执行以下两条命令,将创建两个监视主服务器s1的sentinel实例:
$redis-sentinel sentinel1.conf
$redis-sentinel sentinel2.conf - 其中sentinel1.conf的内容为:
port 26379
Sentinel monitor s1 127.0.0.1 6380 1 - sentinel2.conf的内容为:
Port 26380
Sentinel monitor s1 127.0.0.1 6379 2
Sentinel 实验
Sentinel 总结
- 主从复制,解决了读请求的分担,从节点下线,会使得读请求能力有所下降;
- Master只有一个,写请求单点问题;
- Sentinel会在Master下线后自动执行Failover操作,提升一台Slave为Master,并让其他Slaves重新成为新Master的Slaves;
- 主从复制+哨兵Sentinel只解决了读性能和高可用问题,但是没有解决写性能问题。
问题引出
- 主从对写压力没有分担
- 解决思路就是,使用多个节点分担,将写请求分散到不同节点处理
- 分片Sharding:多节点分担的思路就是关系型数据库处理大表的水平切分思路
Redis Twemproxy
Twemproxy
- Twitter开发,代理用户的读写请求
- Twitter开发的代理服务器,他兼容Redis和Memcached,允许用户将多个redis服务器添加到一个服务器池(pool)里面,并通过用户选择的散列函数和分布函数,将来自客户端的命令请求分发给服务器池中的各个服务器
- 通过使用twemproxy我们可以将数据库分片到多台redis服务器上面,并使用这些服务器来分担系统压力以及数据库容量:在服务器硬件条件相同的情况下,对于一个包含N台redis服务器的池来说,池中每台平均1/N的客户端命令请求
- 向池里添加更多服务器可以线性的扩展系统处理命令请求的能力,以及系统能够保存的数据量
Twemproxy配置
sxt:
listen: 192.168.56.201:22121
hash: fnv1a_64
distribution: ketama
auto_eject_hosts: true
redis: true
server_retry_timeout: 2000
server_failure_limit: 3
servers:
- 192.168.56.201:6379:1
- 192.168.56.202:6379:1
- 192.168.56.203:6379:1
Twemproxy配置说明
sxt,服务器池的名字,支持创建多个服务器池
listen: 192.168.56.201:22121,这个服务器池的监听地址和端口号
hash: fnv1a_64,键散列算法,用于将键映射为一个散列值
distribution: ketama,键分布算法,决定键被分布到哪个服务器
redis: true,代理redis命令请求,不给定时默认代理memcached请求
servers,池中各个服务器的地址和端口号及权重
auto_eject_hosts、
server_failure_limit: twemproxy连续3次向同一个服务器发送命令请求都遇到错误时,twemproxy就会将该服务器标记为下线,并交由池中其他在线服务器处理
问题:如何监听本地所有地址的某个端口
Twemproxy运行
nutcracker -d -c /opt/sxt/twemproxy/conf/nutcracker.sxt.yml
redis-cli -p 22121 -h 192.168.56.201
节点下线
经过重试次数后,将Redis置为下线
127.0.0.1:22121> get mykey
"123"
127.0.0.1:22121> get mykey
(error) ERR Connection refused
127.0.0.1:22121> get mykey
(error) ERR Connection refused
127.0.0.1:22121> get mykey
(nil)
error到nil的变化,说明代理之前是把key的访问执行原来的服务器,置为下线后,将key的访问交给了其它服务器处理.
节点上线
经过重试次数后,将Redis置为下线
127.0.0.1:22121> get mykey
"123"
127.0.0.1:22121> get mykey
(error) ERR Connection refused
127.0.0.1:22121> get mykey
(nil)
error到nil的变化,说明代理之前是把key的访问执行原来的服务器,置为下线后,将key的访问交给了其它服务器处理.
Redis服务恢复后,原来这个key的值可以再次取到
重试超时配置
server_retry_timeout <time>选项
当一个服务器被twemproxy判断为下线之后,在time毫秒之内,twemproxy不会再尝试向下线的服务器发送命令请求,但是在time毫秒之后,服务器会尝试重新向下线的服务器发送命令请求
如果命令请求能够正常执行,那么twemproxy就会撤销对该服务器的下线判断,并再次将键交给那个服务器来处理
但如果服务器还是不能正常处理命令请求,那么twemproxy就会继续将原本应该交给下线服务器的键转交给其他服务器来处理,并等待下一次重试的来临
总结
- 前端使用 Twemproxy 做代理,后端的 Redis 数据能基本上根据 key 来进行比较均衡的分布
- 后端一台 Redis 挂掉后,Twemproxy 能够自动摘除。恢复后,Twemproxy 能够自动识别、恢复并重新加入到 Redis 组中重新使用
- Redis 挂掉后,后端数据是否丢失依据 Redis 本身的持久化策略配置,与 Twemproxy 基本无关
- 如果要新增加一台 Redis,Twemproxy 需要重启才能生效;并且数据不会自动重新 Reblance,需要人工单独写脚本来实现
- 如原来已经有 2 个节点 Redis,后续有增加 2 个 Redis,则数据分布计算与原来的 Redis 分布无关,现有数据如果需要分布均匀的话,需要人工单独处理
- 如果 Twemproxy 的后端节点数量发生变化,Twemproxy 相同算法的前提下,原来的数据必须重新处理分布,否则会存在找不到key值的情况
- 不管 Twemproxy 后端有几台 Redis,前端的单个 Twemproxy 的性能最大也只能和单台 Redis 性能差不多
- 如同时部署多台 Twemproxy 配置一样,客户端分别连接多台 Twemproxy可以在一定条件下提高性能
整合方案
- redis-mgr
- 整合了通过整合复制、Sentinel以及twemproxy等组件,提供了一站式的Redis服务器部署、监控、迁移功能,网址https://github.com/changyibiao/redis-mgr
Redis集群
Redis集群
- 3.0支持
- 由多个Redis服务器组成的分布式网络服务集群
- 每一个Redis服务器称为节点Node,节点之间会互相通信。两两相连
- Redis集群无中心节点
Redis集群节点复制
- Redis集群的每个节点都有两种角色可选:主节点master node、从节点slave node。其中主节点用于存储数据,而从节点则是某个主节点的复制品
- 当用户需要处理更多读请求的时候,添加从节点可以扩展系统的读性能,因为Redis集群重用了单机Redis复制特性的代码,所以集群的复制行为和我们之前介绍的单机复制特性的行为是完全一样的
Redis集群故障转移
- Redis集群的主节点内置了类似Redis Sentinel的节点故障检测和自动故障转移功能,当集群中的某个主节点下线时,集群中的其他在线主节点会注意到这一点,并对已下线的主节点进行故障转移
- 集群进行故障转移的方法和Redis Sentinel进行故障转移的方法基本一样,不同的是,在集群里面,故障转移是由集群中其他在线的主节点负责进行的,所以集群不必另外使用Redis Sentinel
Redis集群分片
- 集群将整个数据库分为16384个槽位slot,所有key都数据这些slot中的一个,key的槽位计算公式为slot_number=crc16(key)%16384,其中crc16为16位的循环冗余校验和函数
- 集群中的每个主节点都可以处理0个至16383个槽,当16384个槽都有某个节点在负责处理时,集群进入上线状态,并开始处理客户端发送的数据命令请求
举例
三个主节点7000、7001、7002平均分片16384个slot槽位
节点7000指派的槽位为0到5060
节点7001指派的槽位为5461到10022
节点7002指派的槽位为10923到16383
节点7003指派的槽位为5061到5460,10023-10922
Redis集群Redirect转向
- 由于Redis集群无中心节点,请求会发给任意主节点
- 主节点只会处理自己负责槽位的命令请求,其它槽位的命令请求,该主节点会返回客户端一个转向错误
- 客户端根据错误中包含的地址和端口重新向正确的负责的主节点发起命令请求
Redis集群搭建
- 创建多个主节点
- 为每一个节点指派slot,将多个节点连接起来,组成一个集群
- 槽位分片完成后,集群进入上线状态
- 6个节点:3个主节点,每一个主节点有一个从节点
Redis集群总结
- Redis集群是一个由多个节点组成的分布式服务集群,它具有复制、高可用和分片特性
- Redis的集群没有中心节点,并且带有复制和故障转移特性,这可用避免单个节点成为性能瓶颈,或者因为某个节点下线而导致整个集群下线
- 集群中的主节点负责处理槽(储存数据),而从节点则是主节点的复制品
- Redis集群将整个数据库分为16384个槽,数据库中的每个键都属于16384个槽中的其中一个
- 集群中的每个主节点都可以负责0个至16384个槽,当16384个槽都有节点在负责时,集群进入上线状态,可以执行客户端发送的数据命令
- 主节点只会执行和自己负责的槽有关的命令,当节点接收到不属于自己处理的槽的命令时,它将会处理指定槽的节点的地址返回给客户端,而客户端会向正确的节点重新发送
- 如果需要完整地分片、复制和高可用特性,并且要避免使用代理带来的性能瓶颈和资源消耗,那么可以选择使用Redis集群;如果只需要一部分特性(比如只需要分片,但不需要复制和高可用等),那么单独选用twemproxy、Redis的复制和Redis Sentinel中的一个或多个