Redis进阶

文章目录

  • 前言
  • 1 单点Redis的问题
  • 2 Redis持久化
    • 2.1 RDB
      • 2.1.1 RDB基本介绍
      • 2.2.2 RDB的bgsave分析 和 RDB的优缺点分析
    • 2.2 AOF
      • 2.2.1 AOF基本介绍
    • 2.3 AOF和RDB的比较
  • 3 Redis主从集群
    • 3.1 主从集群概念
    • 3.2 主从集群的搭建配置
    • 3.3 主从的全量同步原理
    • 3.4 主从的增量同步
    • 3.5 全量同步和增量同步总结和对比
  • 4 Redis 哨兵机制
    • 4.1 哨兵的作用和工作原理
    • 4.2 配置哨兵集群
    • 4.3 RedisTemplate连接哨兵
  • 5 Redis 分片集群
    • 5.1 分片集群简介
    • 5.2 分片集群的搭建
    • 5.3 Redis分片集群的散列插槽
    • 5.4 集群伸缩(集群节点的增加和删除)
    • 5.5 集群的故障转移(自动故障转移 和 手动故障转移)
    • 5.6 Redistemplate连接分片集群
  • 6 Redis内存回收
    • 6.1 过期key处理
    • 6.2 Redis内存淘汰策略
  • 7 Redis最佳实践
    • 7.1 键值设计 bigkey问题
  • 8 Redis数据结构
    • 8.1 skipList(跳表)
    • 8.2 五种数据类型之一(Zset)
    • 8.3 五种数据类型之一(Hash)
    • 8.4 Dict
      • 8.4.1 dict的结构
      • 8.4.2 dict的rehash(扩容与收缩)
  • 9 Redis原理探析
    • 9.0 Redis到底是单线程还是多线程
    • 9.1 Redis Resp协议
  • 总结


前言

Redis进阶学习


1 单点Redis的问题

数据丢失问题 持久化
并发问题 主从集群保证可用
故障恢复 哨兵机制
存储能力扩展 分片集群

Redis进阶_第1张图片

2 Redis持久化

2.1 RDB

2.1.1 RDB基本介绍

RDB被称为Redis数据快照
当1 停机以后 或者 2 满足我们在配置文件中配置的RDB触发规则
会进行一次RDB持久化
把数据保存到本地磁盘

尽量使用save命令而非bgsave
bgsave会异步开启一个子进程 避免Redis主进程的正常工作受影响
Redis进阶_第2张图片
redis.conf 有关RDB的配置如下
Redis进阶_第3张图片

2.2.2 RDB的bgsave分析 和 RDB的优缺点分析

Redis进阶_第4张图片
Redis进阶_第5张图片

2.2 AOF

2.2.1 AOF基本介绍

AOF简介

Redis的每一个写命令都会记录在AOF文件中 下次重启Redis执行AOF文件中的命令加载数据
Redis进阶_第6张图片
redis.conf中关于AOF的配置

AOF默认关闭 需要配置appendonly yes打开
AOF的默认命令记录频率 每秒钟记录一次 每秒刷盘 这时最多丢失一秒的数据量 性能适中
Redis进阶_第7张图片
AOF的bgrewriteaof 命令 重写命令
让AOF文件执行重写功能 对 其中存储的命令做 优化 以最少的命令达成相同效果
并且redis.conf中有一些默认的AOF文件重写机制
Redis进阶_第8张图片

2.3 AOF和RDB的比较

Redis进阶_第9张图片

3 Redis主从集群

3.1 主从集群概念

为了应对更高的并发 我们把单机的redis 升级为主从集群
Redis主从集群 一台master 应对写操作 其余的slave应对读操作
master把数据同步到slave来保证数据的一致性
Redis进阶_第10张图片

3.2 主从集群的搭建配置

假如有 A B两个节点
在B上执行命令 slaveof A节点的IP A节点的端口
相当于让B成为A的子节点
之后B会成为只读节点 对B的写操作失效 同时对A的写操作 会同步到B节点中

Redis进阶_第11张图片

Redis进阶_第12张图片

3.3 主从的全量同步原理

Redis进阶_第13张图片
Redis进阶_第14张图片
Redis进阶_第15张图片
Redis进阶_第16张图片

3.4 主从的增量同步

1 slave往master发送replid和 offset
2 master节点判断replid是否一致 一致说明不是第一次来 恢复continue
3 主节点去repl_backlog获取offset之后数据发给slave节点
4 slave节点执行命令
Redis进阶_第17张图片
主从集群的优化
1 如果网络带宽足够 我们可以开启无磁盘复制 减少磁盘io
2 redis单节点内存占用不要太大 避免复制RDB时过多的磁盘IO
避免全量同步
适当提升repl_backlog的大小 尽量在slave宕机后快速恢复 避免全量同步
限制一个Master节点的slace数量 采取主-从-从的链式结构
Redis进阶_第18张图片

3.5 全量同步和增量同步总结和对比

Redis进阶_第19张图片

4 Redis 哨兵机制

4.1 哨兵的作用和工作原理

哨兵的三大作用
1 监控
sentinel会不断检查slave和master是否正常工作
2 故障恢复
当master宕机时 会选取一个slave作为Master 原来的master恢复后作为slave
3 通知
sentinel作为redis客户端的服务发现来源 当集群发生故障转移时
会将最新信息通知给客户端
Redis进阶_第20张图片
三大作用之监控
sentinel监控基于心跳机制 每隔一秒向集群中的每个实例发送Ping命令
1 主观下线
某sentinel发现某实例未在规定时间内响应‘’
2 客观下线
一定数量的sentinel发现某实例主观下线
Redis进阶_第21张图片

master选举规则
Redis进阶_第22张图片
三大作用之故障转移
1 给指定要成为master的节点发送slaveof no one 使其成为master
2 给其他所有节点发送新的Master节点的地址 通过salveof 命令使他们成为新master的从节点
3 把故障原master节点标记为slave 故障恢复后成为新master节点的salve

Redis进阶_第23张图片
Redis进阶_第24张图片

4.2 配置哨兵集群

sentinel.conf 配置文件

port 27001
sentinel announce-ip 192.168.150.101
sentinel monitor mymaster 192.168.150.101 7001 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
dir "/tmp/s1"

解读:

  • port 27001:是当前sentinel实例的端口
  • sentinel monitor mymaster 192.168.150.101 7001 2:指定主节点信息
    • mymaster:主节点名称,自定义,任意写
    • 192.168.150.101 7001:主节点的ip和端口
    • 2:选举master时的quorum值

启动哨兵
redis-sentinel s1/sentinel.conf

解读
s1/sentinel.conf 你哨兵配置文件存放的绝对路径

4.3 RedisTemplate连接哨兵

Redis进阶_第25张图片

Redis进阶_第26张图片Redis进阶_第27张图片

5 Redis 分片集群

5.1 分片集群简介

Redis主从解决了高并发读 可用性的问题
但仍存在 高并发写 海量数据存储的问题 在这时我们应该采用分片集群

分片集群中有多个master 每个存储不同的数据 这解决了海量数据存储 和 高并发写的问题
并且master节点之间通过Ping互相检测状态 分片集群没有哨兵机制
每个Master也可以配置从节点 保证其高可用
Redis进阶_第28张图片

5.2 分片集群的搭建

==1 编写配置文件 ==
这里给出一个节点的配置文件 其他节点的配置文件参照这个
redis.conf

port 6379
# 开启集群功能
cluster-enabled yes
# 集群的配置文件名称,不需要我们创建,由redis自己维护
cluster-config-file /tmp/6379/nodes.conf
# 节点心跳失败的超时时间
cluster-node-timeout 5000
# 持久化文件存放目录
dir /tmp/6379
# 绑定地址
bind 0.0.0.0
# 让redis后台运行
daemonize yes
# 注册的实例ip
replica-announce-ip 192.168.150.101
# 保护模式
protected-mode no
# 数据库数量
databases 1
# 日志
logfile /tmp/6379/run.log

2 一键启动所有节点

# 进入/tmp目录
cd /tmp
# 一键启动所有服务
printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t redis-server {}/redis.conf

3 创建集群 让节点之间产生关系

redis-cli --cluster create --cluster-replicas 1 192.168.150.101:7001 192.168.150.101:7002 192.168.150.101:7003 192.168.150.101:8001 192.168.150.101:8002 192.168.150.101:8003

命令说明:

  • redis-cli --cluster或者./redis-trib.rb:代表集群操作命令
  • create:代表是创建集群
  • --replicas 1或者--cluster-replicas 1 :指定集群中每个master的副本个数为1,此时节点总数 ÷ (replicas + 1) 得到的就是master的数量。因此节点列表中的前n个就是master,其它节点都是slave节点,随机分配到不同master

Redis进阶_第29张图片Redis进阶_第30张图片

通过命令可以查看集群状态:

redis-cli -p 7001 cluster nodes

在这里插入图片描述

5.3 Redis分片集群的散列插槽

== 1 什么是散列插槽==
Redis会默认对分片集群的每一个master节点分配插槽
数据不是和master节点绑定 而是和插槽绑定
在添加数据时 redis会根据Key的有效值计算插槽值 然后存到对应的slot上
有效值是指如果有大括号 那么有效值就是大括号内的 没有大括号 整个Key都是有效值

2 散列插槽解决的问题
由于数据存储在slot上 当一个master节点宕机时 其对应的slot可以转移给别的节点
防止了数据的丢失问题 保证可用性
Redis进阶_第31张图片
Redis进阶_第32张图片

5.4 集群伸缩(集群节点的增加和删除)

通过查看文档实现以下内容
Redis进阶_第33张图片

5.5 集群的故障转移(自动故障转移 和 手动故障转移)

1 自动故障转移
当集群中的一个master宕机
首先他失去与其他实例的链接
确认下线以后会自动进行故障转移 提升一个slave作为master
Redis进阶_第34张图片2 数据迁移(手动故障转移 cluster failover)
可以手动切换slave和master节点
缺省流程
1 slave节点通知master节点拒绝新的任何客户端请求
2 Master返回offset 等待slave数据同步一致
4 开始故障转移
5 slave 最后标记自己为master 广播通知其他master节点 和原来的master节点

Redis进阶_第35张图片

5.6 Redistemplate连接分片集群

配置跟连接主从集群差不多 可以参考上文
1 引入Pom
2
主要是配置文件和配置主从集群不同
在这里插入图片描述

3 配置读写分离

6 Redis内存回收

6.1 过期key处理

1 TTL的记录方式
TTL记录在datebase结构体的一个dict字典中 以Key TTL形式存储 另一个dict字典 存储的是 Key - Value值
Redis进阶_第36张图片
2 过期key删除策略
1 惰性清理
每次查找对应的Key时 判断key是否过期 过期则删除
其存在问题 如果极端情况下 Key过期后一直没有被查找 则会堆积大量的Key
2 定期清理
定期抽样一定数量的Key 判断是否 过期 过期则删除

关于定期清理
Redis会设置一个定时任务serverCron() 按照server.hz的频率执行过期Key清理 模式slow
Redis每个事件循环前会调用beforesleep() 执行过期Key清理 模式为fast

fast模式和slow模式相比 执行频率高 执行速度快
在这里插入图片描述

Redis进阶_第37张图片

6.2 Redis内存淘汰策略

1 Redis在什么时候进行内存淘汰
Redis会在每一次处理客户端命令时 processcommand()函数中判断是否进行内存淘汰
Redis进阶_第38张图片
2 Redis内存淘汰策略
要注意 Redis默认的内存淘汰策略是noevication 不进行内存淘汰 内存满时不会写入新数据
1 allkeys 淘汰策略 所有key淘汰
random 在所有Key中随机淘汰
lru 最少最近使用 淘汰 用当前时间减去最后一次访问时间 值越大越优先被淘汰
lfu 最少频率使用 淘汰 优先淘汰使用频率低的

2 volatile 淘汰策略 设置TTL的key中进行淘汰
random
ttl 优先淘汰ttl值最小的Key
lru
lfu

Redis进阶_第39张图片
3 lru lfu算法
redis的数据都会被封装成redisobject结构
其中存有类型 编码方式 指向真实数据的数据指针等
其中还有一个字段 根据内存淘汰策略的不同 存储不同的值

加入使用lru 最少最近使用策略
那么这个值24位存储的是最近最后一次使用的秒数

假如使用lfu 最少频率使用策略
16高位记录的是最近最后一次访问该Key的时间 低8位记录的是访问的频率
其中这个频率是基于Lfu算法的逻辑访问频率
并且会这个访问频率在算法计算中会随着时间逐渐降低 默认每和上一次访问时间间隔一分钟 计数器就减去1

Redis进阶_第40张图片
4 淘汰策略流程分析
1 先会判断内存是否充足
2 判断内存策略是哪种 noevicaiton 则不进行内存淘汰 allkeys volitail
3 具体判断是 lru lfu ttl random的哪种策略
4 如果是 random随机挑选一个key删除
如果是lru lfu noevication 则会创建一个删除池
随机抽样一些数据加入这个删除池 进行删除
5 如果删除后判断内存足够 则取消内存淘汰
如果删除后内存仍然不够用 则再次进行淘汰策略循环

Redis进阶_第41张图片

7 Redis最佳实践

7.1 键值设计 bigkey问题

1 什么是bigkey
bigKey通常以Key的大小和其成员数量来进行综合判断
通常 如果一个key大于10kb
或者其成员数量超过1000 我们就认为它是一个bigkey
Redis进阶_第42张图片
bigkey的危害
网络阻塞 假如读一个bigkey 那么少量的QPS就可能导致带宽被占满 影响Redis 乃至物理机
数据倾斜 bigkey所在的实例内存占用远超其他实例 难以达到内存的均衡
Redis阻塞 当对一个hash zset类型的bigkey进行操作 因为Redis是单线程 很可能会造成阻塞
cpu压力 当对一个Bigkey进行序列化和反序列化 会对cpu造成巨大压力
Redis进阶_第43张图片
发现bigkey
1 通过redis-cli 的 --bigkeys 会返回每一种类型的最大key 但是只会返回最大的一个0 并且不一定返回满足bigkey要求的key
所以不推荐使用
2 自己编写scan进行扫描 根据长度进行判断
3 第三方工具分析RDB文件 如Redis rdb tools
4 云服务器自带的分析功能

Redis进阶_第44张图片
删除Bigkey
由于Bigkey很大 占用内存过多
对其进行删除操作也可能造成Redis阻塞
在Redis4.0之后提供了Unlink命令来进行异步删除
Redis3.0和以下版本最好遍历bigkey 逐个元素进行删除 来避免阻塞
Redis进阶_第45张图片

8 Redis数据结构

8.1 skipList(跳表)

1 什么是跳表
跳表首先是一个双向链表
他和普通链表的区别是升序排列
一个链表节点可能包含多个节点指针 跨度不同 这就解决了传统链表的查询遍历慢问题
Redis进阶_第46张图片

2 跳表的数据结构
跳表的本身
包含头尾指针
节点的数量 最大的层级(默认为1)
跳表的节点
ele 节点存储的值
score 节点分数 可以排序用
backward 前一个节点的指针
多级索引数组 里面有索引跨度和下一个节点的指针
Redis进阶_第47张图片

Redis进阶_第48张图片
Redis进阶_第49张图片

8.2 五种数据类型之一(Zset)

ZSET也就是sortedset 没一个元素需要指定一个socre和member值
可根据score值排序 可以根据member查出对应的score member值唯一

基于sorted ser这几个特性
zset底层采用了dict 实现 键值对存储 键值唯一
skiplist 实现排序
skiplist每个节点的ele存储member score就存储score
dict的每个dictentry存储Key value 就是member score值 实现通过member查询score
Redis进阶_第50张图片
Redis进阶_第51张图片
但是当元素不多时dict和skiplist的优势不明显
并且由于dict和skiplist都存储了一份member dict数据 所以比较占用内存空间
因此当满足一下两个条件
1 元素数量小于一定值 默认为128
2 每个元素都小于一定字节数 默认为64字节
zset底层会采用ziplist存储
由于ziplist没有键值对这一概念 是连续的内存空间
因此会把score和member连续存储
score和member存在紧挨在一起的entry
队首的score小

如果ziplist的元素数量或者大小超标 则zset底层会转为dict和skiplist存储

Redis进阶_第52张图片

Redis进阶_第53张图片

8.3 五种数据类型之一(Hash)

hash底层默认采用ziplist进行编码
zplist中两个相邻的entry分别存储field和value
当数据量较大
满足一定条件
1 ziplist中元素个数超过512 默认
2 ziplist单个元素大小超过64字节
zplist会转为dict结构进行存储
Redis进阶_第54张图片
当执行heset命令时
先会判断hash的key是否存在 如果不存在则创建一个 默认为ziplist
接着判断是否需要把ziplist转为dict存储 (主要检查元素大小)
接着最后进行set操作 在进行set操作的同时 检查ziplist的长度是否超出
超出的话也向dict转化
Redis进阶_第55张图片
Redis进阶_第56张图片
Redis进阶_第57张图片
Redis进阶_第58张图片

8.4 Dict

8.4.1 dict的结构

Dict由三部分组成
dict(字典) dicthashtable(哈希表) dictentry(哈希节点)
Redis进阶_第59张图片
向dict中存储数据的步骤
先通过Key计算出hash值
然后用hash&sizemask 得到存储到数组中的哪一个位置
这个数组中存储的是dictentry 哈希节点的指针 指向hash节点
(size是哈希表的大小 sizemask是哈希表的大小减1 hash&sizemask 相当于 hash % size )

Redis进阶_第60张图片
dict结构中包含两个dicthashtable 一个平常存储数据 一个平常为null
供rehash使用
Redis进阶_第61张图片

8.4.2 dict的rehash(扩容与收缩)

dict的扩容条件
dict在每次新增键值对时会检查负载因子(used/size) 如果满足条件
1 负载因子>=1 且后台没有bgsave bgrewriteaof等后台进程
==2 负载因子>=5 ==
则会执行hash扩容

Redis进阶_第62张图片
== dict的收缩条件 ==
dict每次删除元素时 会对负载因子做检查 当负载因子<0.1时
会触发hash收缩

Redis进阶_第63张图片

hash扩容和收缩的流程
1 根据当前需要扩容还是收缩 重新计算size值大小
如果是扩容size第一个大于等于userd+1的第一个2的n次方
如果收缩size第一个大于used的第一个2的n次方
2 根据新的size值申请内存空间 创建哈希表 并将这个表放到dict结构的另一个哈希表中
3 把rehashid 变为 0 代表开始rehash
4 每次执行增删改查时都看一看是否rehashid > -1 如果是 则一个一个元素的转移到新的哈希表中
每次转移的这个元素是hash[0]表的rehashid这个元素 直到所有的数据都转移到新的哈希表中
5 把新的hash表1赋值给旧的hash表0 同时把hash表1 赋值Null
Redis进阶_第64张图片
Redis进阶_第65张图片

9 Redis原理探析

9.0 Redis到底是单线程还是多线程

Redis的核心命令处理部分是单线程
但从整体看Redis已经是多线程了
在Redis4.0中已经引入多线程处理耗时较长的任务 如bigkey等

在RS 6.0 中 网络模型是多线程 提高对cpu的利用率

那为什么Redis的命令处理仍然是单线程
1 因为Redis纯内存操作 速度极快 限制它的往往是网络瓶颈 多线程提升不大
2 引入多线程不可避免的产生线程安全问题
3 过多的多线程会造成频繁的上下文切换 造成开销

9.1 Redis Resp协议

由于redis 是一个cs架构软件
为保证客户端和服务端的正常通信
因此定义了一个RESP协议
Redis进阶_第66张图片
Redis进阶_第67张图片

总结

提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

你可能感兴趣的:(redis,redis,缓存,数据库)