Redis集群
redis集群解决的问题
redis单机风险:
- 机器宕机了,服务不可用
- 单体服务器容量有限
- 单体服务器处理大量请求压力大
解决问题思路:AKF
AKF是一个x-y-z三轴模型解决方案
x轴:为主机搭建从机,数据完全复制
解决问题:
1.单机服务器宕机不可用
2.主机用于增删改,从机用于读数据,减少单机压力
y轴:多搭建几台主机,不同主机上放不同的业务数据
解决问题:
1.单体服务器容量有限
2.减少服务器压力
z轴:为y轴上不同业务数据的主机再搭建主机,再次减少y轴上主机的容量压力
解决问题:
1.减少服务器容量压力
x、y、z轴的方案一起使用,从而解决单体redis存在的问题
CAP原则
数据一致性问题
概念:集群中各个节点数据保持一致
方案
强一致性:
特点:
- 数据保持一致
- 数据保持一致需要时间,在这段时间内系统不能使用,破坏可用性
弱一致性:
特点:
- 数据会丢失
- 系统可用
最终一致性:
特点:
- 数据最终会保持一致
- 在某段时间内数据不一致
- 系统可用
可用性
概念:系统在时时刻刻都是可以给出响应的
分区容忍性
概念:集群在某个时刻,允许节点的数据不一致
现在的集群,分区容忍性是必须满足的,剩下就是在可用性和数据一致性中做选择。CAP原则只能满足其中两个原则
redis中的监控程序
问题和方案
集群中需要时刻检测主节点是否正常,如果不正常,让从节点顶上。这是由监控程序完成的。
监控程序可以是一个节点,也可以是一个集群
监控程序是一个集群会引发的问题:一个监控程序认为主节点正常,另一个认为不正常,出现脑裂现象
方案:
- 多数服从少数
- 超过n/2+1个监控节点认为正常就正常,认为不正常就不正常
- 一般集群节点选择奇数。例如3个节点和4个节点。首先3个节点中有2个相同就行,成本是2/3;4个节点中有3个相同才行,成本是3/4。2/3<3/4,3个节点成本低,意味着发现问题的成本低,所以选择奇数
主从复制及数据同步
搭建集群的重要配置参数
1. replicaof配置
格式:replicaof 主机IP 主机port
含义:配置跟随的主机地址
注意:如果自己当主机,使用replicaof no one
2. masterauth配置
格式:masterauth 主机密码
含义:配置主机密码
3. replica-serve-stale-data配置
格式:replica-serve-stale-data yes
含义:复制初始化阶段,从机的旧数据是否对外提供访问
注意:在复制初始化阶段,从机会把主机发送来的数据放入临时文件中,此时客户端访问从机,只能访问旧数据
4. replica-read-only配置
格式:replica-read-only yes
含义:从机是否只提供读取操作
5. repl-diskless-sync配置
格式:repl-diskless-sync yes
含义:主从复制期间,主机数据是否要落盘,yes表示不落盘,no表示落盘
6. repl-backlog-size配置
格式:repl-backlog-size 1mb
含义:在增量复制机制中,如果从机断线,这段时间内的命令会存到积存队列中,这个参数规定了队列的大小。如果命令数据大小超过队列大小,新命令会覆盖旧命令,所以从机恢复后会触发全量复制
7. min-slaves-to-write配置
格式:min-slaves-to-write 3
含义:最少有3个从机正常运行时,主机才支持写操作,否则在操作主机时会报错
8. min-slaves-max-lag配置
格式:min-slaves-max-lag 10
含义:这个配置与min-slaves-to-write联合使用。如果3个从机中有一个断线9秒,此时主机还是可以写入的。但是1秒后,主机认为从机断线,禁止数据写入
数据同步
数据同步过程
第一种情况:集群刚搭建完成
复制初始化过程
命令发送:在从机刚连上主机时,从机向主机发送ping命令,确认连接正常。如果正常,主机返回pong。然后从机向主机发送replconf命令,把自己的端口号发送给主机。然后再发送psync命令开始数据同步
同步过程:主机会启动一个子进程,子进程向从机发送数据,从机将接收的数据放入临时rdb文件中,等待接收结束后恢复数据。在整个过程中,可以通过replica-serve-stale-data配置参数决定从机是否对外提供访问
复制同步阶段
复制初始化过程结束后,开始复制同步阶段。在复制初始化阶段,主机会接收到增删改命令,这些命令存储在replication buffer中,到了复制同步阶段才会将buffer中的命令同步给从机。待同步完成后,主从机保持一个长连接,主机通过此连接将后续的增删改操作同步给从机。
第二种情况:从机断线重连上主机
如果从机在某段时间与主机断开连接后又连上,此时主从机会进行一次同步。在从机断开连接的这段时间内,主机会将增删改的命令存入环形缓存replication backlog buffer。此缓存如果已经存满,会覆盖之前的命令。所以,当从机连接上主机时,如果环形缓存已满,就需要进行全量同步,否则增量同步。
数据同步的时机:
- 集群刚搭建好,从机刚连上主机,进行一次全量同步
- 由于网络原因,从机与主机断开,后来又重新连上,进行一次同步,至于是全量同步还是增量同步,要看缓存replication backlog buffer是否满了来判断。此缓存大小由repl-backlog-size字段设置。
- 主机每次的增删改操作,都会进行同步
- redis的数据同步是弱一致性的。主机将数据发送给从机后就不管了,直接给客户端返回成功。此时从机可能会落盘失败,如果客户端去从机查询数据的话,获取的是脏数据。
哨兵
概念:
哨兵是监控程序,用于替代人工检测redis集群的节点故障,报警和故障转移
配置:
- port配置项
格式:port 26379
含义:哨兵程序的端口号 - sentinel monitor配置项
格式:sentinel monitor 集群名称 主节点IP 主节点port 通过票数
含义:指明哨兵监控的节点地址,以及选举主节点时通过的票数
启动方式:
- 进入redis的bin目录下,执行以下命令
- redis-sentinel sentinel的配置文件路径
Redis分布式方案
redis集群下,业务数据存储方案
在3.0版本之前,redis是不支持集群的。很多第三方组件在客户端侧实现了redis集群,它们实现的数据存储方案有很多种。在3.0版本之后,redis实现了集群部署。redis自身使用的是哈希槽的数据存储方案。
方案一:
根据业务类型不同划分数据,将数据分别存入各自的redis节点中
弊端:如果数据的业务类型不好划分,方案不可取
方案二:
通过哈希算法划分数据,存入各自redis节点中
弊端:如果以后增加集群节点,通过哈希算法取旧值取不到
方案三:
通过随机算法将数据存入redis节点中
弊端:要取数据时,自己也不知道取哪个节点获取
方案四:
通过一致性哈希算法,将数据存入redis节点中
弊端:新增节点,导致部分数据查询不到,出现缓存击穿
方案模型:
- 假设存在3个节点,x、y、z,有一个哈希算法,无论入参是什么,求出的结果在[0, 10]范围内
- 假设每个节点根据哈希算法求出结果是:x对应3、y对应6、z对应9
- 此时出现三个区间:[3, 6],[6, 9],[9, 3]
- 当有数据a要存入,假设根据哈希算法求出结果是4,4在[3, 6]范围内,那么数据a存入x节点;如果哈希算法求出的结果是7,那么数据存入y节点;如果哈希算法求出的结果是2,那么数据存入z节点。
redis分布式方案问题
问题1:
当有很多客户端连接redis服务器时,每个服务器连接压力很大
解决方案:客户端和服务器间加一个nginx反向代理服务器,做负载均衡
问题2:
增加一台redis服务器,可能出现缓存击穿的情况。除非把所有的数据重新哈希后存入对应的节点
解决方案:
- 先假设有很多redis服务器
- 每个实际的redis服务器分配多个虚拟节点,其实是槽点,且每个服务器存储了实际服务器与槽点的对应关系
- 当一个请求过来,客户端会随机选择一个服务器连接上,把数据发送给服务器,服务器进行哈希,算出哈希值
- 根据实际服务器与槽点映射关系,确定需要的数据在哪个服务器上,把消息发送给客户端
- 客户端根据返回的消息进行重定向,请求另一台服务器,拿到数据
- 当新增一台服务器时,根据哈希算法求出这台服务器应该有哪些槽点,把这些槽点分配给服务器,并且把槽点对应的数据也同步给服务器
- Redis用14位表示槽点数,总共有2^14 = 16384个槽点
这个解决方案与一致性哈希算法的区别在于,对数据进行哈希运算后对16384进行求余,再将数据放到对应的槽点下。因为redis主机再怎么增加都不可能超过16384个,所以在增加主机时,只需要指定哪些槽点数据迁移到新主机即可。下次要查询数据,在根据key值进行哈希运算后对16384进行求余,算出在哪个槽点后去对应主机上取数据即可。
问题三:
不同节点上的数据无法做聚合操作
解决方案:redis出于性能考虑,没有提供解决方案,开发者需要有意识的将需要进行聚合的数据放到一个节点中
Redis分布式方案部署
twenproxy代理部署
predixy代理部署
redis自身搭建集群:
部署步骤:
- 修改redis实例的配置文件,将其改成集群节点
- bind配置项后面要写真实IP
- 将cluster-enabled yes配置项打开
- 将cluster-config-file nodes.conf配置项打开
- 将cluster-node-timeout 5000配置项打开
- 将appendonly yes配置项打开
- 启动每个redis实例
- 在任何一个主机实例上,执行redis-cli --cluster create IP1:PORT1 IP2:PORT2... --cluster-replicas n
命令介绍:
- redis-cli --cluster create命令 格式:redis-cli --cluster create IP1:PORT1 IP2:PORT2... --cluster-replicas n 含义:创建redis集群,create后面是每个实例的真实地址,n表示每个主机拥有的从机数目。这个命令还会为每个主机分配槽点,槽点是平均分配的
- redis-cli --cluster reshard命令
格式:redis-cli --cluster reshard IP:PORT
含义:这个命令可以重新分配槽点和数据,用于解决数据倾斜问题
使用步骤:
- 连接一台主机后,要求输入需要分配的槽点数目
- 槽点数目输入后,要求输入接收这些槽点的节点ID
- 要求输入划分出槽点的节点ID,可以输入多个节点
- 要想结束输入,在命令行输入done命令即可
注意事项:
在构建redis集群时,各个节点上的配置文件中,bind配置后面要写真实IP
高并发下的Redis缓存
高并发下的问题
缓存击穿
现象:
大量的高并发请求访问数据库,导致数据库短时间内压力巨大,最终奔溃
原因:
Redis作为缓存,有些key设置了过期时间,或者采用了LRU/LFU的方案,导致某些key在某一时刻被删除,此时正好大量的并发请求访问这个key。
解决方案:
1.大量请求过来后,都在缓存中获取不到key
2.让所有请求线程执行setnx命令,由于Redis是单线程的,有一个能够执行成功,相当于加了把锁
3.执行成功的那个线程访问数据库,把数据更新到缓存中,然后删除锁
4.其他请求访问redis拿到数据
方案问题:访问数据的线程挂掉了,锁一直没有删除 进阶方案:给锁设置过期时间
进阶方案问题:数据库堵塞,访问数据库的线程没有挂掉,只是有些慢,导致锁过了有效时间 进阶方案:开启一个线程,监测redis缓存中数据有没有更新,没有更新就延迟一下锁的有效时间,直到锁的有效时间上限
缓存穿透
现象:
客户端恶意请求不存在的数据,导致大量请求访问数据库
方案1:
使用布隆过滤器
布隆过滤器部署方案:
方案1:在客户端侧部署布隆过滤器
方案2:在客户端侧写算法,过滤机制在redis中
方案3:在redis服务端部署布隆过滤器
方案缺陷:
1.布隆过滤器还是有概率让一些不存在的数据透过
2.布隆过滤器不支持删除和修改操作
方案2:
不存在的key在redis中设置空key,并且设置较短过期时间
缓存雪崩
现象:
大量的key在同一时间过期删除,导致大量请求访问数据库
方案1:
均匀分散key的过期时间
方案2:
有些业务场景下,key的过期时间不能修改,使用缓存击穿的策略也是可行的
方案3:
客户端代码中在key过期时间节点,对于此时的请求,延迟请求到达redis的时间,可以延迟几秒访问redis
分布式锁
方案:
1.使用setnx命令
2.给锁添加过期时间,防止加锁的进程挂掉
3.起一个监控进程,不停的更新锁的时间,防止加锁进程没有挂掉,但是业务处理时间过长,导致锁过期的现象出现
注意:
redis最求的是效率,而锁是降低效率的东西,与redis理念相违背。 其实redis不适合作为分布式锁,zookeeper更适合。