1.数据删除与淘汰策略
1.1.过期数据
1.1.1.Redis中的数据特征
image.png
image.png
- 本节主要研究-2中的已经过期的数据
- get/set指令如果太多,cpu处理不过来,此时cpu对于已经过期的数据可能就不操作,这些数据就留下了
1.1.2.时效性数据的存储结构
image.png
- set的时候,value是有内存地址的
- 执行expires key seconds,在另外一块区域执行的hset expires 内存地址 时间
- 过期数据是另外一块独立的存储空间,保存了所有key的内存地址和过期秒数,形成hash结构
- 真正在进行过期处理的时候,对过期数据的存储空间进行检查,检测到时间过期,找到field存储的地址,进而找到key,执行删除操作
1.1.3.数据删除策略的目标
image.png
在cpu不忙的时候,删除一点数据,释放内存
1.2.数据删除策略
1.2.1.定时删除
image.png
image.png
image.png
- 定时器创建出来,一直盯着expires那块空间
- 当计时器时间等于那个value,先通过地址值找到数据,删除数据,然后把地址值、时间这个数据也删除
1.2.2.惰性删除
image.png
image.png
image.png
- 时间到了,不用管。等啥时候访问,啥时候删除
- 如何知道数据是否过期,在执行很多操作之前,先执行expireIfNeeded,该命令与很多命令都是绑定执行的
- 极端现象是redis里面的数据全是过期的,占据大量内存
1.2.3.定期删除
image.png
image.png
image.png
image.png
image.png
- 记录过期信息的空间有很多,每个库有一块
- server.hz建议设置10,除非你性能特别好设置100,超过100不建议
- databasesCorn()挨个遍历数据库
- activeExpireCycle对每个expires中逐一进行检测
- 1秒中执行10次,每次25ms,一秒钟执行250ms,cpu花费四分之一的性能做这件事
- redis数据是存在内存的,不要过多设置永久数据,否则内存很容易就满了
- 随机抽查,随机抽key,如果发现这个库中expires删除的数据多,说明过期的数据多,接着重点抽查
1.2.4.三种策略对比
图片.png
1.3.数据淘汰策略(数据逐出算法)
图片.png
图片.png
图片.png
图片.png
- 删除策略是日常维护,养生
- 淘汰策略是做手术,快不行的时候急救
- 上面的数字是9秒内 每个key访问的次数
-下面的时间表示 每个key在9秒内最后一次访问的秒数 - LRU长时间不用的淘汰(最后一次访问时间距离当前时间的时间间隔最大的),LFU一段时间内访问次数最少的淘汰。
-
官方解释不用看
图片.png图片.png
set name hello
get name
#hits 1
get name
#hits 2
get info
#misses 1
2.主从复制
2.1.主从复制简介
2.1.1.高可用
图片.png
图片.png
图片.png
2.1.2.主从复制概念
图片.png
- 即时、有效:master昨天收到的数据今天才复制给slave,晚了
- 一个slave不能对多个master,两个master同时给slave一个key,都是name,value不一致,冲突
- slave禁止写数据,slave得到的数据怎么给别的slave
2.1.3.主从复制的作用
图片.png
读数据是比写数据多的
2.2.主从复制工作流程
2.2.1.主从复制的工作流程(三个阶段)
图片.png
2.2.2.阶段一:建立连接阶段
图片.png
图片.png
图片.png
- 主从连接方式一
#复制三份配置(端口号分别是6381 6382 6383)
cp redis-6379.conf redis-6381.conf
bind 192.168.31.129
port 6381
#timeout 0
daemonize no
#logfile "log-6381.log"
dir /redis/data
dbfilename "dump-6381.rdb"
appendfilename "appendonly-6381.aof"
sed "s/6381/6382/g" redis-6381.conf > redis-6382.conf
sed "s/6381/6383/g" redis-6381.conf > redis-6383.conf
#启动master slave1 master客户端 slave1客户端
#先在 master客户端
set name zhangsan
#然后在slave1客户端
get name #获取不到
#在slave1客户端执行
slaveof 192.168.31.129 6381
get name #可以获取到
#master
set age 21
#slave1
get age#可以获取到
图片.png
图片.png
- 主从连接方式二
#启动第二个slave服务端,需要加参数
redis-server /redis/conf/redis-6383.conf -- slaveof 192.168.31.129 6381
#通过启动日志发现 启动的时候就已经连了master
#启动第二个slave客户端 都可以正常获取数据
get name
get age
#查看data目录 发现 有rdb文件 我们并没有手动触发rdb 也没有配置自动持久化
#主从复制自带 rdb持久化 数据怎么从主到从 也是通过rdb文件实现的
图片.png
- 主从连接方式三
#第三种 服务器配置才是主流方式
#把slave1 slave2 停了 修改二者的配置文件
bind 192.168.31.129
port 6382
#timeout 0
daemonize no
#logfile "log-6382.log"
dir /redis/data
dbfilename "dump-6382.rdb"
appendfilename "appendonly-6382.aof"
slaveof 192.168.31.129 6381
#分别启动slave1 slave2 master 执行
set name lisi
#slave1 slave2 都能获取到
图片.png
master执行
info
图片.png
slave执行
info
图片.png
-
主从断开连接
图片.png
#slave1执行
slaveof no one
#master执行 看不到slave1的信息
info
#slave还能获取到之前已经复制的数据,master新保存的数据 slave获取不到
-
授权访问(了解)
图片.png -
小结
图片.png
2.2.2.阶段二:数据同步阶段工作流程
图片.png
图片.png
- psync2命令就是要求master给数据
- master拿到psync2指令之后,开始bgsave(数据给人家需要先存起来,打个包)
- slave接收rdb,先清空再执行rdb文件恢复,万一slave里面有数据,所以需要先清空
- 为什么会有部分复制:在master全量复制阶段,master又会接收到一些命令,把这些命令放到复制缓冲区,等待全量复制结束,开始处理这部分命令,进行部分复制。全量复制数据是rdb形式,部分复制数据是aof形式
- 数据同步阶段master说明
图片.png
图片.png
- 数据同步虽然是bgsave,但是还是要消耗cpu性能的
- 复制缓冲区满了,会把第一条冲掉,导致部分数据是不完整的,slave发现不完整会再次进行全量复制,从而进入死循环
- 数据同步阶段slave说明
图片.png
2.2.3.阶段三:命令传播阶段
图片.png
图片.png
图片.png
图片.png
- master收到指令,会把指令发送给slave,负责发送命令的是命令传播程序
- 一旦出现断网,命令传播程序就发不过去了,slave就接收不到指令了,此slave和别的slave数据就不一样了,就靠复制缓冲区了
- 命令传播程序发指令的时候,会把指令在复制缓冲区存一份
图片.png
图片.png
图片.png
2.2.4.工作流程更新
图片.png
- slave刚连上,第一次发psync2指令,没有主机的runid,也没有偏移量,发的就是 psync2 ? -1
- master执行bgsave,保存自己的偏移量(存到哪,记到哪)
- 期间接收客户端命令,进入复制缓冲区,偏移量变化
2.2.5.心跳机制
图片.png
图片.png
图片.png
2.3.主从复制常见问题
图片.png
图片.png
图片.png
- 重连平均时间计算,slave1需要10秒,slave2需要20秒,slave3需要30秒,平均20秒
- 平均每秒产生写命令数据总量,配置文件有
- 二者相乘表示断网这段时间master最多产生的命令数量
- double一下可以满足大部分的服务器需求
- 硬件要跟得上
图片.png
图片.png
图片.png
3.哨兵模式
3.1.哨兵简介
3.1.1.哨兵概念
-
主从结构
图片.png -
主机宕机,主从结构崩塌。希望结构还能使用,在slave中找一个master
图片.png -
在工作的时候,想办法在不停机的情况下,完成这些事。需要一个人盯着机器的状态
图片.png - 一台机器监控可能有偏差,所以搞多个机器一起监控,哨兵越多,最终得到的结论越精准
3.1.2.哨兵作用
图片.png
- 哨兵要投票的,所以一般是单数
3.2.启用哨兵
3.2.1.sentinel.conf
图片.png
- 查看redis目录下sentinel.conf
- cat sentinel.conf | grep -v "#" | grep -v "^$"
图片.png
图片.png
#sentinel number 参与投票的哨兵数量 一般设置哨兵总数/2+1
sentinel monitor mymaster 127.0.0.1 6379 2
#主机宕机 多少秒后开始做切换工作
sentinel down-after-milliseconds mymaster 30000
#主从切换的一个超时时间
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
#出现警告信息 是否要停下
sentinel deny-scripts-reconfig yes
3.2.2.环境配置
#哨兵相关配置
cp sentinel.conf conf
cat sentinel.conf | grep -v "#" | grep -v "^$" > sentinel-26401.conf
port 26401
dir /redis/data
sentinel monitor mymaster 127.0.0.1 6401 2
sentinel down-after-milliseconds mymaster 5000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 20000
sentinel deny-scripts-reconfig yes
sed "s/26401/26402/g" sentinel-26401.conf > sentinel-26402.conf
sed "s/26401/26403/g" sentinel-26401.conf > sentinel-26403.conf
#redis 主从配置
cp redis-6381.conf redis-6401.conf
port 6401
dir /redis/data
dbfilename "dump-6401.rdb"
cp redis-6401.conf redis-6402.conf
port 6402
dir /redis/data
dbfilename "dump-6402.rdb"
slaveof 127.0.0.1 6401
sed "s/6402/6403/g" redis-6402.conf > redis-6403.conf
3.2.3.启动
- 主从结构先启动(一主两从服务端)
redis-server /redis/conf/redis-6401.conf
redis-server /redis/conf/redis-6402.conf
redis-server /redis/conf/redis-6403.conf
-
哨兵1启动 redis-sentinel /redis/conf/sentinel-26401.conf
图片.png - 哨兵启动后,会把信息记录到配置文件中
图片.png
- 哨兵2启动 redis-sentinel /redis/conf/sentinel-26402.conf
-
日志信息出现 找到了哨兵1
图片.png -
配置文件也多了一句 记录哨兵的信息
图片.png -
哨兵3启动redis-sentinel /redis/conf/sentinel-26403.conf
图片.png - 日志又多了一句,配置文件也多了一句
- 总结 哨兵1启动,日志展示主从信息,并把主从信息记录到配置文件
- 总结哨兵2启动,哨兵2日志展示哨兵1的信息并记录到配置文件,哨兵1反之
- 总计哨兵3启动 哨兵3 日志展示其他两位信息并记录到配置文件,哨兵1日志记录其他两位信息并记录到配置文件,以此类推
3.2.4.主从切换过程演示
- 主从功能演示
redis-cli -p 6401
set name zhangsan
redis-cli -p 6402
get name
- 哨兵客户端连接
redis-cli -p 26401
ping
get name
#(error) ERR unknown command `get`, with args beginning with: `name`,
info
图片.png
-
主客户端停了 查看哨兵的日志 5秒后开始做切换
图片.png图片.png
+sdown master mymaster 127.0.0.1 6401
+new-epoch 1第一轮选举
+vote-for-leader 当前哨兵要当领导,发送id,1表示第一次申请
+odown master mymaster 127.0.0.1 6401 #quorum 2/2 确认主机挂了 然后两票通过
+switch-master 换master
+slave 6402连选出来的主
- 重新启动master
slave1客户端
info 已经成master
master客户端
info 成了 slave
slave2客户端
info 连接新的master 即 slave1
3.3.哨兵工作原理
3.3.1.监控
图片.png
- 以左上角哨兵举例来说
- 发info给master,可以获取slave的基本信息,但不是完整信息,相当于获取一个名片
- 根据发info给master得到的slave信息为基础,再发info给各个slave获取完整的信息
图片.png
sentinel发info给master,发完之后建立cmd连接,cmd连接专门用于sentinel给主从机发命令的
连接建立,sentinel保存主从机、哨兵机相关信息
master也保存一份相同主从机、哨兵机相关信息
sentinel发info给slave,完善sentinel端之前保存主从机、哨兵机相关信息
sentinel2连接上来,发info给master,发完之后建立cmd连接,
sentinel2发现master端保存了一份主从机、哨兵机器的信息
sentinel2也保存一份主从机、哨兵机器的信息,其中哨兵机器信息包含两份
sentinel2会连接sentinel,把自己知道的信息都告诉sentinel2,sentinel也会和sentinel2共享信息
sentinel和sentinel2不停发ping,保证都在线
sentinel2也会连接slave
sentinel3加入,三个哨兵互相交换信息,建立一个信息网,信息网就干两件事,发布消息和从这里获取消息,即发布订阅模式
3.3.2.通知
图片.png
哨兵都互相建立了连接,相当于建立一个微信群,天天监视、聊天看着主从结构的状态
哨兵和主从机建立了cmd通道,不停的给主从机发消息,获取他们的信息
一个哨兵发消息给所有的主从机,如果都正常回应,哨兵再在哨兵的信息网里说一声正常
通知阶段就是不停的获取master slave信息 在哨兵的内网中进行数据共享
3.3.3.故障转移
认定下线的过程
图片.png
sentinel1日常给master发hello,master回信息那是正常
如果sentinel1不停的给master发,master没动静,就认为master下线了,给master标注一下,相当于日志里的+sdown
sentinel1把这个消息在内网中发送给其他哨兵
其他哨兵得知这一消息,闲着没事,一起发hello给master,确认他到底有没有挂
都确认挂了,其他哨兵也会在内网发布消息,再次确认master挂了
所有哨兵都确认完,把+sdown改成+odown
+sdown是一个哨兵探测完标记的,主观下线,任意一个哨兵认为下线
+odown是所有哨兵探测完,只要有一半以上的哨兵认为他下线就会标记,客观下线,半数以上的哨兵认为他下线
选举过程
图片.png
sentinel开会。讨论谁去处理故障
都想去处理故障,竞争
选举,每个人手里都有一票
图片.png
1和4想去处理,在内网发信息
2接收到1和4,就要投票,先接到谁的就投给谁
图片.png
以这样的形式继续处理,最终得票多的就成为处理故障的人
这个过程中有可能出现失败的情况,这一轮没有选举成,要进行第二轮、第三轮
处置过程
图片.png
面对这么多sentinel,怎么选
有原则,先采取排除法
偏移量大,说明同步之前master数据比较多
3.3.4.小结
图片.png
4.集群
4.1.集群简介
图片.png
图片.png
现阶段是一主带多从,如果能配置成多个一主带多从,这样就可以对外提供更多的数据访问量,同时存储空间也大了。
需要所有的主之间数据同步,连接在一起形成一个整体,对外提供功能
图片.png
客户机原来过来的带宽很大,现在可以进行分散
一台服务器,内存很容易慢,现在分成三台,每台都存一点数据
一台主服务器挂了,还有从,从可以切换成主,所以影响不大
4.2.集群结构设计
图片.png
单机情况,key直接保存到redis存储空间
集群情况,key面对多个存储空间,怎么选
key先放进CRC16方法中运行,得到一个值A(相当于得到hashcode)
用这个值%16384,得到另外一个值B,这个值就是你要存的地方
每个存储空间里面划好了若干区域 每个区域有一个编号,这个区域是存一堆key的
值B总会找到一个编号与其对应
图片.png
如果再加一台服务器,把已经存在的服务器中划分好的空间挪出来一点给新服务器用
如果把一台服务器去掉,把他拥有的存储空间再分给其他服务器
这个存储空间叫槽
图片.png
集群内机器相互之间是联通的
相互之间都知道谁那有什么东西
并且标注出来了几号在谁那
这样就可以保障每个存储空间对应的服务器都知道其他服务器有哪些存储空间
客户来了要访问某个key,通过计算得到他的槽编号,访问A,A刚好有这个槽,这就是一次命中,直接返回
如果访问的是A,但是槽在B,A就会把这个需求转向B.一次未命中,告知具体位置
20个服务器的集群,最多两次找到
4.3.集群结构搭建
4.3.1.搭建方式
图片.png
4.3.2.Cluster配置
图片.png
cluster配置文件,一定要配。自动生成的,要把名称分出来,加以区分。否则几个东西都会写在一个文件里
4.3.3.Cluster节点操作指令
图片.png
cluster nodes可以用,其他命令已经用不上了
4.3.4.redis-cli命令
图片.png
图片.png
图片.png
4.3.5.基本功能演示
- 配置文件
vim redis-6501.conf
port 6501
dir "/redis/data"
dbfilename "dump-6501.rdb"
cluster-enabled yes
cluster-config-file "cluster-6501.conf"
cluster-node-timeout 5000
sed "s/6501/6502/g" redis-6501.conf > redis-6502.conf
sed "s/6501/6503/g" redis-6501.conf > redis-6503.conf
sed "s/6501/6504/g" redis-6501.conf > redis-6504.conf
sed "s/6501/6505/g" redis-6501.conf > redis-6505.conf
sed "s/6501/6506/g" redis-6501.conf > redis-6506.conf
- 启动
redis-server /redis/conf/redis-6501.conf#主1
redis-server /redis/conf/redis-6502.conf #主2
redis-server /redis/conf/redis-6503.conf #主3
redis-server /redis/conf/redis-6504.conf #从1
redis-server /redis/conf/redis-6505.conf #从2
redis-server /redis/conf/redis-6506.conf #从3
- 创建集群
#1代表1主带1从 前三个是主 后三个是从
#2代表1主带2从 前两个是主 后四个是从 1带34 2带56
#任意一个命令窗口执行
redis-cli --cluster create 127.0.0.1:6501 127.0.0.1:6502 127.0.0.1:6503 127.0.0.1:6504 127.0.0.1:6505 127.0.0.1:6506 --cluster-replicas 1
图片.png
Slot分槽,主1、主2、主3分别有多少槽位
4作为slave分给主1
所有节点状态
输入yes才开始分槽
图片.png
分完槽,开始主从搭建,开始同步
多了6个配置文件,记录主从信息,有一个myself标记
图片.png
打开一个配置文件
打开哪个配置文件,哪个机器信息有myself标记
主从信息 以及从所属主的关系
图片.png
#连接master1、slave1客户端
redis-cli -p 6501
redis-cli -p 6504
#在matser1执行 (error) MOVED 5798 127.0.0.1:6502
#槽在5798 需要到6502客户端操作
set name zhangsan
#解决 启动的时候 加 -c 表示cluster的客户端
redis-cli -c -p 6501
#slave客户端 获取 同样是有问题 启动的时候 也要加-c
图片.png
图片.png
4.3.6.主从切换
master1关闭
查看slave1日志,会重连5秒,之后自己变成主
通过cluster nodes命令查看信息
cluster自带主从切换
图片.png
图片.png
再次启动master1 已经变成从了,要去连接之前的 slave1,slave1是主
图片.png
4.3.7.扩容(添加主从到集群中)
图片.png
加slave
#再弄两个服务器 6508slave
sed "s/6506/6507/g" redis-6506.conf > redis-6507.conf
sed "s/6506/6508/g" redis-6506.conf > redis-6508.conf
redis-server /redis/conf/redis-6508.conf
#6508 作为slave 连到6503上
redis-cli --cluster add-node 127.0.0.1:6508 127.0.0.1:6503 --cluster-slave --cluster-master-id f57b8d8b0cde9e9baae5e9d46a9664c29e422172
图片.png
减slave
redis-cli --cluster del-node 127.0.0.1:6508 07acf11046362ec80f2bd5c05f60259778649129
加master
redis-server /redis/conf/redis-6507.conf
redis-cli --cluster add-node 127.0.0.1:6507 127.0.0.1:6506
图片.png
connected 0-5460 表示master分配的槽
刚加入的master没有分配槽
分槽
redis-cli --cluster reshard 127.0.0.1:6507 --cluster-from cfe2e31f0a01927fde78b07cd40633fd190f5708,0c036e2a6daa4fc1d1af98bc8dd0c0d3eea1f619,f57b8d8b0cde9e9baae5e9d46a9664c29e422172 --cluster-to 4c99a516687f0f8e69462d527375eaf70d66b564 --cluster-slots 3000
先展示计划 然后输入yes确认
图片.png
把自己的槽都给别人,要给多次
#4c99a516687f0f8e69462d527375eaf70d66b564 之前从
#0c036e2a6daa4fc1d1af98bc8dd0c0d3eea1f619 获取到999 全部还回去
redis-cli --cluster reshard 127.0.0.1:6507 --cluster-from 4c99a516687f0f8e69462d527375eaf70d66b564 --cluster-to 0c036e2a6daa4fc1d1af98bc8dd0c0d3eea1f619 --cluster-slots 999 --cluster-yes
#4c99a516687f0f8e69462d527375eaf70d66b564 之前从
#cfe2e31f0a01927fde78b07cd40633fd190f5708 获取到1001 现在只还回去1000
redis-cli --cluster reshard 127.0.0.1:6507 --cluster-from 4c99a516687f0f8e69462d527375eaf70d66b564 --cluster-to cfe2e31f0a01927fde78b07cd40633fd190f5708 --cluster-slots 1000 --cluster-yes
图片.png
#多余的1个还回去
redis-cli --cluster reshard 127.0.0.1:6507 --cluster-from 4c99a516687f0f8e69462d527375eaf70d66b564 --cluster-to cfe2e31f0a01927fde78b07cd40633fd190f5708 --cluster-slots 1 --cluster-yes
redis-cli --cluster reshard 127.0.0.1:6507 --cluster-from 4c99a516687f0f8e69462d527375eaf70d66b564 --cluster-to f57b8d8b0cde9e9baae5e9d46a9664c29e422172 --cluster-slots 1000 --cluster-yes
5.企业级解决方案
5.1.缓存预热
图片.png
图片.png
图片.png
启服务器的时候,里面没有数据。用户访问一次加一次,主从同步量比较大
统计高频数据(可以利用LRU策略),高频数据先装进去
图片.png
用户来的时候,没有数据
提前把数据加上,加的时候把访问量高的先加
用脚本加
5.2.缓存雪崩
图片.png
图片.png
较短时间内,缓存中的key集中过期
redis拿不到数据了,只能从数据库拿,
所有的压力都给数据库了
图片.png
想办法错开
图片.png
设计层面
尽量降低用户的访问,拿数据走静态化的页面处理
多级缓存
提升数据库性能
监控redis性能
图片.png
具体措施:错峰
LRU(最后一次访问时间距离当前时间的时间间隔最大的)和LFU(段时间内访问次数最少的)切换,现在由于大量的因为时长的过期了,换成按访问次数过期
过期时间错开,稀释集中到期的key的数量
超热数据设成永久
加锁是让所有人访问这个数据的时候都排队
图片.png
所有这些措施可以解决雪崩,也只是解决一半而已
还得根据实际情况进行调整
找运维的人一起商量策略,毕竟不是纯开发的问题
5.3.缓存击穿
图片.png
图片.png
爆炸性新闻,所有人都在看,恰巧那个key过期了
一个key过期导致redis击穿
数据库查的结果还没到redis中,就已经过来几千个请求
图片.png
图片.png
电商指定主打商品,加大此类信息key的过期时长,但是无法避免失效,这个事只是有效避免,不能彻底解决
图片.png
信息太热了,通过一个key,访问数据库,结果量比较大
主要是预防、监控、及时调整
5.4.缓存穿透
图片.png
redis命中率快速下降
redis内存正常,CPU占用激增
图片.png
图片.png
访问的key压根就不存在,库里也不存在
图片.png
缓存null没啥用,有效期设置短一点。能解决问题,有限
key加密例如:正常是1234567,加密后变成12a23456b7,key中间固定两位不是ab就过滤,类似白名单,有一个判断过程,比较慢,影响性能。
图片.png
预防为主,这种情况大部分都是病毒,报警
5.5.性能指标监控
5.3.1.五大性能指标
图片.png
图片.png
图片.png
图片.png
图片.png
图片.png
5.3.2.benchmark
图片.png
图片.png
redis-benchmark -h 192.168.31.129
# 测试当前服务器对应的所有指令执行的效率
图片.png
get指令执行10万次 1.71秒内完成
2毫秒完成所有的
图片.png
5.3.3.monitor
图片.png
5.3.4.slowlog
图片.png