单点Redis的问题
数据丢失问题:Redis是内存存储,服务器重启可能会丢失数据
解决方案:实现Redis持久化
并发能力问题:单节点Redis无法满足如同特别高的高并发场景,如双11活动
解决方案:搭建主从集群,实现读写分离
故障修复问题:如果Redis宕机,则服务不可用,需要一种自动的故障恢复手段
解决方案:利用Redis哨兵模式,实现健康检测和自动恢复
存储能力问题:Redis基于内存,单节点能力存储的数据
解决方案啊:搭建分片集群,利用插槽机制实现动态扩容
Redis持久化
RDB和AOF是Redis的持久化机制
RDB
RDB全称Redis Database Backup file(Redis数据备份文件),也叫Redis数据快照。
简单来说就是把内存中的所有数据都记录到磁盘中,生成一个dump.rdb,当Redis实例故障重启后会从磁盘中加载dump.rdb文件,恢复数据
Redis停机时会执行一次RDB
[root@localhost ~]# redis-cli 127.0.0.1:6379> save ok 127.0.0.1:6379> bgsave Background saving started
save命令:由Redis主进程来执行RDB,会阻塞所有命令(不推荐)
bgsave命令:开启子进程执行RDB,避免主进程收到影响
bgsave开始时会fork主进程得到子进程,子进程共享主进程的内存数据,完成fork后读取内存数据并写入RDB文件,用新的RDB文件替换旧的RDB文件
fork采用的是copy-on-write技术:
当主进程执行读操作时访问共享内存
当主进程执行写操作时则会拷贝一份数据执行写操作
RDB的缺点:
RDB执行间隔时间长,两次RDB之间写入数据有丢失的风险fork子进程、压缩、写出RDB文件都比较耗时
Redis内部有出发RDB的机制,可以在redis.conf文件中找到
#900秒内如果至少有1个key被修改则执行bgsave,如果是save ""则表示禁用RDB save 900 1 save 300 10 save 60 10000
RDB的底层实现原理:
底层fork出来一条子进程,这条子进程和主进程一模一样,子进程就负责持久化,而再次操作过程中主进程不会进行任何操作,这保证了RDB的高效性,底层采用bgsave(save)异步进行持久化
AOF
AOF全称为Append Only File(追加文件),Redis处理的每一个写命令都会记录在AOF文件,可以看作是命令日志文件
AOF默认是关闭的,休要修改redis,config配置文件来开启AOF
# 是否开启AOF功能,默认是no appendonly yes # AOF文件的名称 appendfilename "appendonly.aof"
AOF的命令记录的频率也可以通过redis,conf文件来配
# 表示每执行一次写命令就立即记录到AOF文件 appendonly always # 写命令执行完先放入AOF缓冲区,然后表示每隔1秒将缓冲区数据写到AOF文件,是默认的方案 appendfsync everysec # 写命令执行完先放入AOF缓冲区,由操作系统决定何时将缓冲区内容写回磁盘
同步机制对比
配置项 刷盘时机 优点 缺点 Always 同步刷盘 可靠性高几乎不丢数据 性能影响大 everysec 每秒刷盘 性能适中 最多丢失1秒数据 no 操作系统控制 性能最好 可靠性差,可能丢失大量数据
RDB和AOF的对比
RDB | AOF | |
持久化方式 | 定时对整个内存做快照 | 记录每一次执行的命令 |
数据完整性 | 不完整,两次备份之间会丢失 | 相对完整,取决于刷盘策略 |
文件大小 | 会有压缩,文件体积小 | 记录命令,文件体积很大 |
宕机速度恢复 | 很块 | 慢 |
数据恢复优先级 | 低,因为数据完整性不如AOF | 高,因为数据完整性高 |
系统资源占用 | 高,大量CPU和内存消耗 | 低,主要是磁盘IO资源但AOF重写时会占用大量的CPU和内存资源 |
使用场景 | 可以容忍数分钟的数据丢失,追求更快的启动速度 | 对数据安全性要求较常见 |
优点:
aof丢数据是可控的,aof在少量数据的时候性能较好
rdb在恢复大面积数据的时候性能较好
缺点:
随着数据越来越多,aof文件就会越来越大
当需要大面积回复的时候aof的效率就会偏低
rdb可能会丢失数据:rdb+aof--->rdb来恢复大面积数据,用aof追加rdb丢失的数据
Redis的s 过期键的删除策略
过期策略
1、定时删除:在设置键的过期时间的同时,创建一个定时器 timer). 让定时器在键 的过期时间来临时,立即执行对键的删除操作
2、惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是 否过期,如果过期的话,就删除该键;如果没有过期,就返回该键
3、定期删除:每隔一段时间程序就对数据库进行一次检查,删除里面的过期键。至 于要删除多少过期键,以及要检查多少个数据库,则由算法决定
淘汰机制
当内存达到了max-memoery的时候执行淘汰机制
Redis主从
主从的第一次同步时全量同步
全量同步及原理
第一阶段:
slave(从)执行replicaof命令建立连接
slave(从)向master(主)请求数据同步
master判断是否是第一次同步
如果是第一次同步就返回master的数据版本信息
slave保存版本信息
第二阶段:
master执行bgsave命令生成RDB文件
master记录RDB期间的所有命令
master向slave发送RDB文件
slave清空本地数据加载RDB文件
第三阶段:
master向slave发送repl_backlog中的命令
slave执行接收到的命令
master根据Replication Id 和 offset来判断slave是不是第一次来同步数据
Replication Id:简称replid,是数据集的标记,id一致则说明是同一数据集。每个master都有唯一的replid,salve则会继承master节点的replid
offset:偏移量,随着记录在repl_baklog中的数据增多而逐渐增大。slave完成同步时也会记录当前同步的offset。如果slave的offset小于master的offset则说明slave的数据落后于master,需要更新
slave做数据同步必须向master声明自己的replication Id和offset,master才能判断到底需要同步哪些数据
如果slave重启后同步则执行增量同步
增量同步及原理:
第一阶段:
slave重启向master发送psync replid offset
master判断请求replid是否一致
如果不是一次同步则回复continue
第二阶段:
master去repl_baklog中获取offset后的数据
master向slave发送offset后的命令
slave执行命令
因为repl_baklog大小有上限,写满后会覆盖最早的数据。如果slave断开时间过久导致尚未备份的数据背覆盖则无法记录log做增量同步,只能再次全量同步
优化Redis主从就集群方案
在master中配置repl_diskless-sync yes启动无磁盘复制,避免全量同步时的磁盘IO
Redis单节点上的内存占用不要太大,减少RDB导致过多的磁盘IO
适当提高repl_baklog的大小,发现slave宕机时尽快实现故障恢复,尽可能避免全量同步
哨兵(Sentinel)
Redis提供了哨兵(Sentinel)机制来实现主从集群的自动故障恢复
哨兵的作用
监控:Sentinel会不断检查您的master和slave是否按预期工作
自动故障恢复:如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主
通知:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时会将最新的信息推送给Redis客户端
哨兵的服务状态和监控
Sentinel基于心跳机制监测服务状态,每隔1秒向集群的每个实例发送ping命令
主观下线:如果某Sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线
客观下线:若超过指定数量的Sentinel都认为该实例主观下线,则该实例客观下线。数量值最好超过Sentinel实例的一半
一旦发现master故障,sentinel需要在salve中选择一个作为新的master
选举依据:
首先判断slave节点与master节点断开的时间长短,如果超过指定值则排除该slave节点
然后判断slave节点的slave-priority值,越小优先级越高,如果是0则用不参与选举
如果slave-priority一样则判断salve节点的offset值,越大说明数据越新,优先级越高
最后判断slave节点的运行id大小,越小优先级越高
当选择了其中一个slave为新的master后,故障的转移步骤
sentinel给备选的slave节点发送slaveof no one命令,让该节点成为master
sentinel给所有其他slave发送 slaveof +(新master的ip和端口),让这些slave成为新master的从节点,开始从新的master上同步数据
最后sentinel将故障节点标记为slave,当故障节点恢复后会自动成为master的slave节点
分片集群
分片集群可以解决海量数据存储问题,高并发写入问题;这是主从和哨兵无法解决的,主从和高并发能解决高可用和高并发读取
分片集群的特征:
集群中有多个master,每个master保存不同的数据
每个master都可以有多个slave节点
master之间通过ping监测彼此的健康状态
客户端请求访问集群任意节点最终都会被转发到正确节点
散列插槽
Redis会把每一个master节点映射到0-16384(hash slot)上
数据key不是与节点绑定而是和插槽绑定,redis会根据key的有效部分计算插槽值
计算方式利用CRC16算法得到一个hash值,然后对16384取余,得到的结果就是slot值
redis如何判断某个Key应该在哪个实例?
将16384个插槽分配到不同的实例
根据key的有效部分计算哈希值对16384取余
余数作为插槽,根据余数寻找插槽所在实例
将一类数据使用相同的有效部分可以将同一类数据固定保存在同一个redis实例
Redis事务
redis可以一次执行多个命令,本质上是一组命令的集合。一个事务中的所有命令都会序列化,按顺序的串行化执行而不会被其他命令插入,不允许加塞
redis的实务操作
正常事务操作打开事务 multi
提交事务 exec
redis事务的放弃执行
放弃事务 discard
redis的全体连坐:
如发生了语法错误那么整个事务就会失效
redis事务中的指令在执行前会进行语法检查,如果语法错误则指将queued的数据丢掉
如果事务中发生了逻辑错误,那就仅仅是发生了逻辑错误的失败,其他的事务成功
Redis集群脑裂和异步丢数据
集群脑裂:
在主从架构下会由于网络抖动导致master和salve处于不同的网段,而且哨兵和slave划分到了同一个网段,此时哨兵就会认为master挂掉了,那么哨兵就会把salve提升成新的mater,但是原来的master并没有挂掉那么就导致现在集群中会具备两个master,此时就会脑裂异步丢数据:
异步的丢数据是在于master在rdb数据发送给slave之后,master接着去接收新的操作数据,然后master会将它放到缓冲区中,slave在完成rdb数据后此时mater会将缓冲区的数据给slave,但是在完成这一步之前master如果挂掉了那么哨兵会选举salve成为新的master,但是原来master的缓冲区数据就丢失了
问题解决
min-slaves-to-write 3 保证至少有几个salve 和master 之间 还有联系 min-slaves-max-lag 10 延迟最多多久
redis缓存击穿,缓存穿透
缓存穿透:
key对应的数据在数据源并不存在,每次针对此key的请求从缓存中获取不到,请求就会打到数据源从而有可能压垮数据源
缓存击穿:
key对应的数据存在但在redis中过期,此时如果有大量并发请求过来,这些请求发现缓存过期了一般都会从后端DB加载数据并设置到缓存中,那这时候大量的并发请求可能会瞬间把后端的DB压垮,但db是可以查得到数据的,对比缓存穿透是db查不到数据。
redis双写一致性
数据库和缓存不一致就是双写问题
解决方案:
先写数据库再写缓存
加锁
queue + 自旋
给redis的key添加过期时间(会有一段时间的脏数据)