1. Redis高级
2. 过期数据
Redis是一个内存级别的数据库 所有数据都存放在内存中 我们可以通过指令TTL获取指定数据状态
- XX 具有时效性的数据
- -1 永久有效的数据
- -2 已经过期的数据 或被 已删除 或 未定义的数据
我们通过SETEX设置的有效时间 redis会新开辟一个键值对 用来存储key对应的value地址值 以及存活时间
2.1. 定时删除
创建一个定时器,当key设置的过期时间到达时,由定时器执行的对键删除操作
优化:节约内存 快速释放不必要的内容
缺点:cpu负载量大 会影响redis服务器响应时间和指令吞吐量
使用处理性能换取存储空间
2.2. 惰性删除
数据到达过期时间,不做处理,等下次访问该数据时删除
- 如果未过期 返回数据
- 如果已过期 删除 返回不存在
通过get方法查询时,get绑定expirelfNeeded()方法 此方法会检测数据是否过期
优点:节约cpu性能 只有访问时才进行判别删除
缺点:内存压力大 出现长期占用内容
用存储空间换取处理器性能
2.3. 定期删除
通过设置redis.config文件中的 hz 属性 默认为10
为每秒执行hz的次数
设置后服务器每秒执行hz次 serverCron() —> databasesCron() —> activeExpireCycle()
activeExpireCycle() 对每个expires* 逐一进行检测 每次执行250ms/hz
对某个expires[*]检测时,随机挑选W个key检测
- 如果key超时,删除key
- 如果一轮中删除key的数据>W*25%,循环该过程
- 如果一轮中删除key的数据<=W25%,则检查下一个expires[]
- W取值=ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP属性值
参数current_db用于记录activeExpireCycle()进入哪个expires[*]执行
如果activeExpireCyle()执行时间到期,下次从current_db继续向下执行
优点:cpu性能占用设置峰值 检测频度可自定义
内容压力不是很大 长期占用内存的冷数据会被清理掉
2.4. 淘汰策略
当redis使用内容存储数据,执行每一个命令前,会调用freeMemoryIfNeeded()检测内存是否充足.如果不满足加入数据的最低存储要求,则redis会为当前指令清理存储空间.清理数据的策略又称为逐出算法
逐出数据的过程不是百分百能够清理出足够的可使用空间,如果不成功则反复执行.当对所有数据尝试完毕,如果仍然不能到达内存清理要求,将出现错误信息
而redis中内存空间是根据物理内存和配置信息来指定
- maxmenory ?mb 默认值为0不做限制 通常设置在50%以上
- maxmemory-samples count 每次选取待删除数据的个数,采用随机获取数据的方式作为待检测删除数据
- maxmemory-policy policy 对数据进行删除的选择策略
- 检测易失数据(可能会过期的数据集server.db[i].expires)
- volatile-lru 挑选最近最少使用的数据淘汰
- valatile-lfu 挑选最近使用次数最少的数据淘汰
- valatile-tfl 挑选将要过期的数据淘汰
- volatile-random 任意选择数据淘汰
- 检测全库数据(所有数据集server.db[i].dict)
- allkeys-lru 挑选最近最少使用的数据淘汰
- allkeys-lfu 挑选最近使用次数最少的数据淘汰
- allkeys-random 任意选择数据淘汰
- 放弃数据驱逐
- no-enviction(驱逐) 禁止驱逐数据(redis4.0中默认策略) 会引发错误OOM(Out Of Memory)
- 检测易失数据(可能会过期的数据集server.db[i].expires)
使用INFO命令输出监控信息,查询缓存hit和miss的次数,根据业务需求调用Redis配置
3. 主从复制
3.1. 互联网 “三高” 架构
- 高并发
- 高性能
- 高可用
- 可用性 = (1年的总时间 - 1年宕机的总时间) / 1年的总时间 * 100% : 业界可用性目标5个9 即99.999% 即服务器年宕机时长低于315秒 约5.25分钟
3.2. 阶段一:建立连接阶段
建立slave到master的连接,使用master能够识别slave,并保存slave端口号
- saveof ip port : salve发送指令给master
- master接受到指令,响应对方
- 保存master的ip与端口 masterhost 和 masterport
- 根据保存的信息创建连接master的socket
- 周期性发送ping指令
- 响应pong
- 发送指令 auth password (如无密码则忽略)
- 验证授权
- replconf listening-port
<port-number>
- 保存slave的端口号
3.2.1. 主从连接 (slave连接mazster)
- 方式一 从机客户端发送命令 slaveof masterip masterport
- 方式二 启动服务器时连接 redis-server -slaveof masterip masterport
- 方式三 服务器配置 在从机服务器的redis服务器配置文件 加上 slaveof masterip masterport
3.2.2. 主从断开连接
- 断开slave与master的连接 不会删除已有数据 只是不再接受master发送的数据 salveof no one 在从机上执行
3.2.3. 授权访问
3.3. 阶段二:数据同步阶段工作流程
- 如果master数据量巨大,数据同步阶段应该避开流量高峰期,避免造成阻塞
- 如果复制缓冲区设置不合理,会导致数据溢出. 如果全量复制周期太长,进行部分复制时发现数据已经存在丢失,必须进行第二次全量复制 导致slave陷入死循环
#在主服务器配置文件配置 配置缓存区大小
repl-backlog-size ?mb
- master单机内存占用主机内存比例应在50%-70%,留下30-50作为bgsave命令和创建复制缓冲区
- 在slave进行全量复制/部分复制时 建议关闭此时对外服务
#在从服务器配置文件中配置 关闭和开启
slave-serve-stale-data yes|no
- 数据同步阶段 master发送给slave信息可以理解为master是slave的一个客户端 主动向slave发送命令
- 多个slave同时对master请求数据同步 master发送的rdb文件增多 如果master带宽不足 会对带宽造成巨大冲击 建议适量错峰请求同步
- salve过多时 建议调整拓扑结构 由一主多从结构变为树状结构 中间的节点即使master 也salve 使用树状结构时 由于层级深度 导致深度越高的slave与最顶层master间数据同步延迟较大 数据一致性变差 应谨慎选择
3.4. 阶段三:命令传播阶段
命令传播阶段出现了断网现象:
网络闪断闪连:忽略
短时间网络中断:部分复制
长时间网络中断:全量复制
工作原理
- 通过offset区分不同的slave当前数据传播的差异
- master记录已发送的信息对应的offset
- slave记录已接收的信息对应的offset
3.4.1. 流程更新
3.4.2. 心跳机制
进入命令传播阶段候,master与slave间需要进行信息交换,使用心跳机制进行维护,实现双方连接保持在线
master心跳:
- 内部指令:PING
- 周期:由repl-ping-slave-period决定,默认10秒
- 作用:判断slave是否在线
- 查询:INFO replication 获取slave最后一次连接时间间隔,lag项维持在0或1视为正常
slave心跳任务
- 内部指令:REPLCONF ACK {offset}
- 周期:1秒
- 作用1:汇报slave自己的复制偏移量,获取最新的数据变更指令
- 作用2:判断master是否在线
心跳阶段注意事项:
- 当slave多数掉线,或延迟过高时,master为保障数据稳定性,将拒绝所有信息同步
#在主服务器配置文件中配置
min-slaves-to-write 2
min-slaves-max-lag 8
slave数量少于2个,或者所有slave的延迟都大于等于8秒时,强制关闭master写功能,停止数据同步
- slave数量由slave发送REPLCONF ACK命令做确认
- slave延迟由slave发送REPLCONF ACK命令做确认
3.5. 频繁的全量复制
- 伴随着系统的运行,master的数据量会越来越大,一旦master重启,runid将发生变化,会导致全部slave的全量复制操作
内部优化调整方案:
1:master内部创建master_replid变量,使用runid相同的策略生成,长度41位,并发送给所有slave
2:在master关闭时执行命令shutdown save,进行RDB持久化,将runid与offset保存到RDB文件中
- repl-id repl-offset
- 通过redis-check-rdb命令可以查看该信息
3:master重启后加载RDB文件,恢复数据,重启后,将RDB文件中保存的repl-id与repl-offset加载到内存中
- master_repl_id=repl master_repl_offset =repl-offset
- 通过info命令可以查看该信息
作用:本机保存上次runid,重启后恢复该值,使所有slave认为还是之前的master
- 第二种出现频繁全量复制的问题现象:网络环境不佳,出现网络中断,slave不提供服务
问题原因:复制缓冲区过小,断网后slave的offset越界,触发全量复制
最终结果:slave反复进行全量复制
解决方案:修改复制缓冲区大小
repl-backlog-size ?mb
建议设置如下:
1.测算从master到slave的重连平均时长second
2.获取master平均每秒产生写命令数据总量write_size_per_second
3.最优复制缓冲区空间 = 2 second write_size_per_second
3.5.1. 频繁的网络中断
- 问题现象:master的CPU占用过高 或 slave频繁断开连接
问题原因
- slave每1秒发送REPLCONFACK命令到master
- 当slave接到了慢查询时(keys * ,hgetall等),会大量占用CPU性能
- master每1秒调用复制定时函数replicationCron(),比对slave发现长时间没有进行响应
最终结果:master各种资源(输出缓冲区、带宽、连接等)被严重占用
解决方案:通过设置合理的超时时间,确认是否释放slave
repl-timeout seconds
该参数定义了超时时间的阈值(默认60秒),超过该值,释放slave
3.5.2. 数据不一致
问题现象:多个slave获取相同数据不同步
问题原因:网络信息不同步,数据发送有延迟
解决方案
- 优化主从间的网络环境,通常放置在同一个机房部署,如使用阿里云等云服务器时要注意此现象
- 监控主从节点延迟(通过offset)判断,如果slave延迟过大,暂时屏蔽程序对该slave的数据访问
slave-serve-stale-data yes|no
开启后仅响应info、slaveof等少数命令(慎用,除非对数据一致性要求很高)
4. 哨兵模式
哨兵(sentinel) 是一个分布式系统,用于对主从结构中的每台服务器进行监控,当出现故障时通过投票机制选择新的master并将所有slave连接到新的master
哨兵也是一台redis服务器 只是不提供数据相关服务 通常哨兵的数量配置为单数
4.1. 配置哨兵
通过sentinel.conf 配置
- sentinel monitor master_name master_host master_port sentinel_number 设置哨兵监听的主服务器信息 sentinel_number表示参与投票的哨兵数量 设置超过哨兵半数即可
- sentinel down-after-milliseconds master_name million_seconds 设置判定服务器宕机时长 该设置控制是否进行主从切换
- sentinel failover-timeout master_name million_seconds 设置故障切换的最大超时时长
- sentinel parallel-syncs master_name sync_slave_number 设置主从切换后,同时进行数据的slave数量 值越大 要求网络资源越高 值越小 同步时间越长
- redis-sentinel filename(sentinel.conf配置文件路径) 启动哨兵
4.2. 阶段一:监控阶段
- 获取各个sentinel的状态(是否在线)
- 获取master的状态
- 获取所有slave的状态(根据master中的slave信息)
4.3. 阶段二:通知阶段
sentinel在通知阶段要不断的去获取master/slave的信息,然后在各个sentinel之间进行共享,具体的流程如下:
4.4. 阶段三:故障转移
4.4.1. 判断master宕机
主观下线:任意一个sentine认为master已经下线
客观下线:半数以上的sentine认为master已经下线
4.4.2. 选举master
每个sentine都有一票 最终票最多的称为处理事故的哨兵服务
首先它有一个在服务器列表中挑选备选master的原则
- 不在线的OUT
- 响应慢的OUT
- 与原master断开时间久的OUT
- 优先原则
优先级
offset
runid
总结:故障转移阶段
- 发现问题,主观下线与客观下线
- 竞选负责人
- 优选新master
- 新master上任,其他slave切换master,原master作为slave故障恢复后连接
5. Cluster集群
集群就是使用网络将若干台计算机联通起来,并提供统一的管理方式,使其对外呈现单机的服务效果
5.1. Cluster集群结构设计
数据存储设计:
- 通过算法设计,计算出key应该保存的位置
- 将所有的存储空间计划切割成16384份,每台主机保存一部分
注意:每份代表的是一个存储空间,不是一个key的保存空间 - 将key按照计算出的结果放到对应的存储空间
那redis的集群是如何增强可扩展性的呢?譬如我们要增加一个集群节点
当我们查找数据时,集群是如何操作的呢?
- 各个数据库相互通信,保存各个库中槽的编号数据
- 一次命中,直接返回
- 一次未命中,告知具体位置
5.2. 配置
在redis-6401.conf中配置
- cluster-enabled yes|no 是否启用cluster,加入cluster节点
- cluster-config-file cluster-6503.conf cluster配置文件名,该文件属于自动生成,仅用于快速查找文件并查询文件内容
- cluster-node-timeout milliseconds 节点服务响应超时时间,用于判定该节点是否下线或切换为从节点 毫秒数
- cluster-migration-barrier min_slave_number master连接的slave最小数量
5.3. 启动Culster
redis-cli –-cluster create masterhost1:masterport1 masterhost2:masterport2 masterhost3:masterport3 [masterhostn:masterportn …] slavehost1:slaveport1 slavehost2:slaveport2 slavehost3:slaveport3 -–cluster-replicas n
n为多少个主服务器 前n个ip为主服务器 后面为slave服务器
- cluster nodes 查询集群节点信息
5.4. 节点增删
- 添加master到当前集群中,连接时可以指定任意现有节点地址与端口
redis-cli --cluster add-node new-master-host:new-master-port now-host:now-port
- 添加slave
redis-cli --cluster add-node new-slave-host:new-slave-port master-host:master-port --cluster-slave --cluster-master-id masterid
- 删除节点,如果删除的节点是master,必须保障其中没有槽slot
redis-cli --cluster del-node del-slave-host:del-slave-port del-slave-id
- 重新分槽,分槽是从具有槽的master中划分一部分给其他master,过程中不创建新的槽
redis-cli --cluster reshard new-master-host:new-master:port --cluster-from src- master-id1, src-master-id2, src-master-idn --cluster-to target-master-id -- cluster-slots slots
#将需要参与分槽的所有masterid不分先后顺序添加到参数中,使用,分隔
#指定目标得到的槽的数量,所有的槽将平均从每个来源的master处获取
- 重新分配槽,从具有槽的master中分配指定数量的槽到另一个master中,常用于清空指定master中的槽
redis-cli --cluster reshard src-master-host:src-master-port --cluster-from src- master-id --cluster-to target-master-id --cluster-slots slots --cluster-yes
6. 缓存预热
1.请求数量较高,大量的请求过来之后都需要去从缓存中获取数据,但是缓存中又没有,此时从数据库中查找数据然后将数据再存入缓存,造成了短期内对redis的高强度操作从而导致问题
2.主从之间数据吞吐量较大,数据同步操作频度较高
- 前置准备工作:
1.日常例行统计数据访问记录,统计访问频度较高的热点数据
2.利用LRU数据删除策略,构建数据留存队列例如:storm与kafka配合
- 准备工作:
1.将统计结果中的数据分类,根据级别,redis优先加载级别较高的热点数据
2.利用分布式多服务器同时进行数据读取,提速数据加载过程
3.热点数据主从同时预热
- 实施:
4.使用脚本程序固定触发数据预热过程
5.如果条件允许,使用了CDN(内容分发网络),效果会更好
总的来说:缓存预热就是系统启动前,提前将相关的缓存数据直接加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
7. 缓存雪崩
1.系统平稳运行过程中,忽然数据库连接量激增
2.应用服务器无法及时处理请求
3.大量408,500错误页面出现
4.客户反复刷新页面获取数据
5.数据库崩溃
6.应用服务器崩溃
7.重启应用服务器无效
8.Redis服务器崩溃
9.Redis集群崩溃
10.重启数据库后再次被瞬间流量放倒
解决方案
- 思路:
1.更多的页面静态化处理
2.构建多级缓存架构
Nginx缓存+redis缓存+ehcache缓存
3.检测Mysql严重耗时业务进行优化
对数据库的瓶颈排查:例如超时查询、耗时较高事务等
4.灾难预警机制
监控redis服务器性能指标
CPU占用、CPU使用率
内存容量
查询平均响应时间
线程数
5.限流、降级
短时间范围内牺牲一些客户体验,限制一部分请求访问,降低应用服务器压力,待业务低速运转后再逐步放开访问
- 落地实践:
1.LRU与LFU切换
2.数据有效期策略调整
根据业务数据有效期进行分类错峰,A类90分钟,B类80分钟,C类70分钟
过期时间使用固定时间+随机值的形式,稀释集中到期的key的数量
3.超热数据使用永久key
4.定期维护(自动+人工)
对即将过期数据做访问量分析,确认是否延时,配合访问量统计,做热点数据的延时
5.加锁:慎用!
总的来说:缓存雪崩就是瞬间过期数据量太大,导致对数据库服务器造成压力。如能够有效避免过期时间集中,可以有效解决雪崩现象的 出现(约40%),配合其他策略一起使用,并监控服务器的运行数据,根据运行记录做快速调整。
8. 缓存击穿
1.系统平稳运行过程中
2.数据库连接量瞬间激增
3.Redis服务器无大量key过期
4.Redis内存平稳,无波动
5.Redis服务器CPU正常
6.数据库崩溃
问题排查:
1.Redis中某个key过期,该key访问量巨大
2.多个数据请求从服务器直接压到Redis后,均未命中
3.Redis在短时间内发起了大量对数据库中同一数据的访问
总而言之就两点:单个key高热数据,key过期
解决方案:
1.预先设定
以电商为例,每个商家根据店铺等级,指定若干款主打商品,在购物节期间,加大此类信息key的过期时长 注意:购物节不仅仅指当天,以及后续若干天,访问峰值呈现逐渐降低的趋势
2.现场调整
监控访问量,对自然流量激增的数据延长过期时间或设置为永久性key
3.后台刷新数据
启动定时任务,高峰期来临之前,刷新数据有效期,确保不丢失
4.二级缓存
设置不同的失效时间,保障不会被同时淘汰就行
5.加锁
分布式锁,防止被击穿,但是要注意也是性能瓶颈,慎重!
总的来说:缓存击穿就是单个高热数据过期的瞬间,数据访问量较大,未命中redis后,发起了大量对同一数据的数据库访问,导致对数 据库服务器造成压力。应对策略应该在业务数据分析与预防方面进行,配合运行监控测试与即时调整策略,毕竟单个key的过 期监控难度较高,配合雪崩处理策略即可。
9. 缓存穿透
1.系统平稳运行过程中
2.应用服务器流量随时间增量较大
3.Redis服务器命中率随时间逐步降低
4.Redis内存平稳,内存无压力
5.Redis服务器CPU占用激增
6.数据库服务器压力激增
7.数据库崩溃
问题排查:
1.Redis中大面积出现未命中
2.出现非正常URL访问
问题分析:
- 获取的数据在数据库中也不存在,数据库查询未得到对应数据
- Redis获取到null数据未进行持久化,直接返回
- 下次此类数据到达重复上述过程
- 出现黑客攻击服务器
解决方案:
1.缓存null
对查询结果为null的数据进行缓存(长期使用,定期清理),设定短时限,例如30-60秒,最高5分钟
2.白名单策略
提前预热各种分类数据id对应的bitmaps,id作为bitmaps的offset,相当于设置了数据白名单。当加载正常数据时放行,加载异常数据时直接拦截(效率偏低)
使用布隆过滤器(有关布隆过滤器的命中问题对当前状况可以忽略)
2.实施监控
实时监控redis命中率(业务正常范围时,通常会有一个波动值)与null数据的占比
非活动时段波动:通常检测3-5倍,超过5倍纳入重点排查对象
活动时段波动:通常检测10-50倍,超过50倍纳入重点排查对象
根据倍数不同,启动不同的排查流程。然后使用黑名单进行防控(运营)
4.key加密
问题出现后,临时启动防灾业务key,对key进行业务层传输加密服务,设定校验程序,过来的key校验
例如每天随机分配60个加密串,挑选2到3个,混淆到页面数据id中,发现访问key不满足规则,驳回数据访问
总的来说:缓存击穿是指访问了不存在的数据,跳过了合法数据的redis数据缓存阶段,每次访问数据库,导致对数据库服务器造成压力。通常此类数据的出现量是一个较低的值,当出现此类情况以毒攻毒,并及时报警。应对策略应该在临时预案防范方面多做文章。
无论是黑名单还是白名单,都是对整体系统的压力,警报解除后尽快移除。
10. 性能指标监控
性能指标
- latency 响应请求的平均时间
- instantaneous_ops_per_sec 平均每秒处理请求总数
- hit_rate(calculated) 缓存查询命中率(通过查询总次数与查询得到非nil数据总次数计算而来)
内存指标
- used_memory 当前内存使用量
- mem_fragmentation_ratio 内存碎片率(关系到是否进行碎片整理)
- evicted_keys 为避免内存溢出删除的key的总数量
- blocked_clients 基于阻塞操作(BLPOP等)影响的客户端数量
活动指标
- connected_clients 当前客户端连接总数
- connected_slaves 当前连接slave总数
- master_last_io_seconds_ago 最后一次主从信息交换距现在的秒
- keyspace key的总数
持久性指标
- rdb_last_save_time 当前服务器最后一次RDB持久化的时间
- rdb_changes_since_last_save 当前服务器最后一次RDB持久化后数据变化总量
错误指标
- rejected_connections 被拒绝连接的客户端总数(基于达到最大连接值的因素)
- keyspace_misses key未命中的总次数
- master_link_down_since_seconds 主从断开的秒数
11. 性能指标工具
测试当前服务器的并发性能
redis-benchmark [-h ] [-p ] [-c ] [-n <requests]> [-k ]
启动服务器调试信息
monitor
慢日志 记录查询慢的日志
- slowlog [operator]
- get 获取慢日志信息
- len 获取慢日志条目数
- reset 重置慢查询日志
慢日志通过redis-xxx.conf配置
slowlog-log-slower-than 1000 #设置慢查询的时间下线,单位:微妙
slowlog-max-len 100 #设置慢查询命令对应的日志显示长度,单位:命令数