Redis
2023-05-10 03:57:29 1 举报
AI智能生成
登录查看完整内容
涵盖Redis大量知识点总结
作者其他创作
大纲/内容
单值缓存 set key value
对象缓存 set key json
对象属性缓存(批量set string) mset key value key value key value
设置锁 setnx key value
删除锁 del key
EX seconds – 设置键key的过期时间,单位时秒
PX milliseconds – 设置键key的过期时间,单位时毫秒
NX – 只有键key不存在的时候才会设置key的值
XX – 只有键key存在的时候才会设置key的值
KEEPTTL -- 获取 key 的过期时间
GET -- 返回 key 存储的值,如果 key 不存在返回空
设置可配置锁 set key value EX 10 NX
分布式锁
设置文章数据 set key value
value++ incr key(如果key不存在则默认添加并生成value为1)
获取文章数据 get key
记录文章浏览量
计数器
Web集群Session共享
分布式系统全局唯一序列号
应用场景
String
对象缓存 hmset key field value field value field value
添加商品到购物车 hset key field value(value是商品的数量)
修改商品在购物车中的数量 hincrby key field number (number可以是正数也可以是负数,正数代表加,负数代表减)
查看购物车中的商品总数 hlen key
删除购物车中的商品 hdel key field
获取购物车中全部商品 hgetall key
购物车示例
电商购物车
同比String类型去存储对象属性,Hash存储的更加规整
同比String占用的内存、cpu消耗更少
同比String更加节省存储空间
优势
Redis集群架构下不适合大规模使用
过期时间只能设置到key上而不能设置到field上
劣势
优劣势
Hash
栈 Stack FILO
队列 Queue FIFO
阻塞队列 Blocking Queue
石衫架构笔记发布了最新的文章
订阅公众号的用户都执行该命令 新增一条文章id的数据 LPUSH userId articleId
macrozheng发布了最新的文章
订阅公众号的用户都执行该命令 新增一条文章id的数据 LPUSH userId articleId
用户查看自己的订阅号 LRANGE userId start stop(可以根据start、stop来实现分页)
方案一:暂时只推送在线的用户接收到最新文章到订阅号,后续在后台执行其他用户的数据更新
补充方案一:如果用户登录需要去更新自己的订阅号,并且后台执行其他用户的数据更新要实时判断命令是否已执行,可以考虑都从一个队列中取,避免重复执行导致用户看到重复的订阅号内容
思考点:如果是超大数据量,例如几千几万个用户需要接收到文章,该如何优化?
PUSH方案
订阅号
数据结构
List
添加用户id到set集合中:SADD key value
随机取出count个元素并在集合中删除 :SPOP key count
随机取出count个元素不删除:SRANDMEMBER key count
查看集合中的全部元素:SMEMBERS key
抽奖
点赞 SADD {msg:id} {user:id}
取消点赞 SREM {msg:id} {user:id}
用户是否点赞 SISMEBER {msg:id} {user:id}
点赞数量 SCARD {msg:id}
点赞用户列表 SMEBERS {msg:id}
点赞、收藏、标签
场景描述:假设你查看了某一个博主的主页可能存在以下内容
共同关注 SINTER 查看交集
我关注的人也关注了他 SISMEBERS 查看集合中是否存在该用户,该命令需要逐条去执行,可以考虑分页
可能认识的人 SDIFF 查看差集
微博关注模型
搜索栏展示内容
华为手机的型号:SADD phone phone:huawei:p40 phone:huawei:p50
CPU:SADD cpu:inter phone:huawei:P40
CPU:SADD cpu:haisi phone:huawei:P50
运行内存4G:SADD memory:4G phone:huawei:P40
运行内存8G:SADD memory:8G phone:huawei:P50
存储数据命令
SINTER phone cpu:inter memory:8G
返回结果:{}
查询命令(假设查询华为手机且CPU是inter且运行内存8G的商品)
SINTER phone cpu:inter memory:4G
返回结果:{phone:huawei:P40}
查询命令(假设查看华为手机且CPU是inter且运行内存4G的商品)
电商产品的筛选
交集:SINTER key1 key2 ...keyn
并集:SUNION key1 key2 ...keyn
差集:SDIFF key1 key2 ...keyn
高级操作
Set
文章排行榜
热搜
七日内热搜
ZSet:有序集合(按权重排序)
命令手册
1、网络IO,键值对读写是通过单线程的方式
2、持久化、集群数据同步、异步删除等是由额外的线程来完成
严格意义上来说Redis并不是单线程,它分两种情况
单线程
Redis的所有数据都存储在内存当中,运算是内存级别的运算
单线程避免了切换线程所造成的性能损耗(正因为Redis是单线程,线上一定要避免执行时间过长的命令,例如keys,会阻塞其他命令,造成Redis卡顿,谨慎使用)
IO多路复用
单线程如何保证高性能
单线程和高性能
直接生成持久化文件,每次执行都会重新生成持久化文件
同步执行,可能会阻塞redis的其他命令
save
异步生成持久化文件,通过copy on write(cow)写时复制,不会阻塞redis的其他命令
1、主线程fork一个bgsave的子进程,并且共享内存数据
2、假设主线程持久化过程中对内存数据都是读的操作,不会影响到bgsave子进程持久化内存数据
3、如果主线程修改内存数据,那么会复制一份生成副本,bgsave会根据副本内容写入到持久化文件中
工作流程简述:
bgsave(background save)
save 同步
bgsave 异步
IO类型
save 不需要多余的内存开销
bgsave 不会阻塞redis的其他命令
save 阻塞redis的其他命令
bgsave 开销相对较大
O(n)
复杂度
二者对比(save、bgsave)
持久化文件:dump.rdb(二进制文件)
场景1:通常我们的生成持久化策略是根据时间n,操作了m次数据就生成持久化,如果在没达到配置策略的条件下redis宕机,就会造成数据丢失
缺陷:可能会造成数据丢失数据
RDB(默认)
慢,安全不容易造成数据丢失
每条命令都写入aof文件
always
快,宕机时会丢失1s的数据
每秒钟写入一次
everysec(默认)
关闭aof
no
模式
aop中记录了大量可省略的命令,可以通过重写精简文件内的命令
例如:set key value | set key value1 | set key value2,同一个key,AOF文件只需要保存最后一条命令即可
auto‐aof‐rewrite‐min‐size 64mb //aof文件至少要达到64M才会自动重写,文件太小恢复速度本来就很快,重写的意义不大
auto‐aof‐rewrite‐percentage 100 //aof文件自上一次重写后文件大小增长了100%则再次触发重写
配置
AOF重写:bgrewriteaof
与rdb相并它并非二进制文件,所以恢复速度慢
是否会丢失数据是根据策略决定
缺陷
AOF(append-only file)
rdb 小
aop 大
体积
rdb 恢复更快
aop 相对较慢
效率
rdb 可能会造成数据丢失
aop 根据策略配置
可靠性
rdb 低
aof 高
启动优先级
RDB与AOF对比
配置:aof-use-rdb-preamble yes(前置条件必须开启aof)
说明:实际上rdb文件容易造成数据丢失,通常我们会以aof持久化的数据来恢复文件,但是往往aof恢复的太慢了,redis在4.0之后增加了混合持久化机制,它可以更快的恢复我们的内存数据。
混合持久化是基于AOF重写的策略。如果没有开启混合持久化,AOF重写是压缩命令,而开启后则是压缩完命令并转成二进制,后续AOF存储的命令还是字符的形式,直到下一次压缩
混合持久化机制(最佳持久化方式)
定时任务 - 多目录备份(过旧的文件删除)
定时任务 - 多机备份(过旧的文件删除)
定时任务策略:每小时、每天
Redis数据备份
持久化
基础持久化方式
简介:客户端一次性向服务端发送打包多条命令,无需等待服务器端逐条响应
优势:减少网络开销,提高命令执行效率
并非原子性,失败感知能力较弱,服务端全部执行完后客户端才能拿到结果
Redis必须在管道提交的命令执行完之前将结果缓存起来(消耗内存),并不是提交的命令越多越好,要考虑到服务端到压力
管道(Pipelined)
简介:lua是一种脚本语言,redis在2.6版本推出了脚本语言,开发者可以使用lua脚本来执行redis命令
打包命令减少网络开销(与管道类似)
原子操作:将一次脚本视为一个整体,如果失败就全部不执行
替代redis事物:原生事物并不支持报错回滚,而redis的lua脚本基本实现了全部常规事物(例如支持报错回滚),官方也更提倡这种方式开启事物
lua脚本如果出现死循环或耗时运算,则会阻塞服务端的命令
示例(服务端命令):eval \
Lua脚本
当单机无法满足系统的使用时,可以通过主从的方式去扩充多个节点,它是有一个主节点,多个从节点
主节点用来向从节点同步数据
简介
1、Master、Slave都启动后,Slave会向Master发送一个psync命令并建立socket连接
fork的子进程会不断的记录操作内存数据的命令并生成副本
2、Master收到psync后,通过bgsave来生成最新持久化文件
3、Master发送持久化完成的rdb文件
4、Slave接收到Master发送的rbd文件,清空内存数据重新同步内存数据
5、Matster发送子进程记录的持久化rbd文件
6、Slave接收rbd文件并同步数据
7、Master与Slave保持socket长连接并实时同步数据,保证数据一致性
建立连接(全量复制)
1、Master检测到与Slave的长连接超时(断开)
2、Master将断开后操作内存数据的命令生成副本repl backlog buffer
offset代表偏移量
3、Slave重新建立连接并发送psync(offset)命令,表示我需要接收丢失的数据
可根据配置指定buffer的大小,例如1m,如果长时间断开或超过预置大小会走全量复制
4、Master将repl backlog buffer的文件内容发送给Slave
5、Slave同步数据
6、Master与Slave保持socket长连接并实时同步数据
数据复制
多个从节点申请向主节点同步数据,主节点可能会压力过大
为了避免Master同步压力过大,可以使用这样的方式同步数据
主从复制风暴
主从复制的模式是一个主节点负责写入数据,多个从节点负责复制主节点数据,假设客户端向服务端发送命令,需要区分命令是写还是读,如果是写要将命令发送到主节点
如果Master节点宕机,运维需要手动修改Slave作为Master节点,包括服务的连接地址
注意事项
主从架构
sentinel哨兵,它是独立的进程,不负责Redis的读写操作,用来检测Redis的Master是否存活,如果检测到Master宕机,开启Slave选举机制,保证Redis的高可用性
说明
根据sentinel配置文件中的quorum数量来确定,有多少个sentinel节点认为redis主节点宕机才开启选举机制
值一般为:sentinel总数 / 2 +1
选举完成后sentienl会自动更新配置文件中的master ip port
选举机制
假设我们是主从架构,一个Master,两个Slave,突然出现网络抖动,哨兵认为Master宕机了,发起选举机制,让Slave开启选举。但是因为网络抖动的原因很快原Master又恢复了正常,那么就出现了两个Master节点。
如何解决:我们可以通过设置半数以上哨兵认为Master宕机再是实行选举机制,其次可以设置通信时间,减少快速通信造成宕机的错觉
脑裂
哨兵会根据配置文件声明的Master节点不停的发送Ping(默认1/1s),如果Master长时间没有回复Pong就认为它宕机了
哨兵与Master如何通信
借助Redis的PUBLISH/MESSAGE来实现消息广播,每个哨兵都需要知道其他哨兵的地址,用来异步通讯
哨兵与哨兵之间如何通信
如何通信
核心问题
自动故障转移(选举)
高可用性
灵活性与扩展性,动态的修改Redis或哨兵的实例
单点故障(不可配置单一哨兵、哨兵本身也可能发生故障
配置复杂
延迟(主节点宕机后,哨兵监控到再选举完成这一过程需要一定的时间
哨兵模式
Cluster其实是对主从和哨兵的一种新的架构,其中实现了哨兵对不同主节点的监控,每一个主节点都有自己的从节点,Redis会自动分片以保证数据不会大量倾斜到某一个主节点
创建Cluster的过程中Redis会自动配置主从的分配是在不同的机器交替通信,避免服务器宕机导致主从节点全部宕机
最大支持1w个主从集群,官方推荐不超过1000,因为主从之间是需要相互通信的,大量的主从集群会导致性能下降
参考主从脑裂描述
造成原因:脑裂后有两个Master节点,那么根据Redis的机制早期的Master会降级会Slave,从新的Master同步数据;Redis主从同步原理中,Slave会清空自己的持久化文件,也就是说早期的Master数据会丢失;
如何解决:可以设置服务端响应客户端的命令,如果是write数据必须同步到1台或以上Slave实例再返回成功,这样就可以保证Master至少与一台Slave的数据是相同的
脑裂数据丢失
Cluster模式下会存在一个逻辑的hash slot,它的长度是16384
假设我们有3个主从组成的Cluster,那么3个Master就会根据slot的长度平均分配可以存储哪些范围的hash
客户端发送的命令例如get、set需要提前计算存在于哪个实例,否则会造成not found data的情况(可以参考jedis是如何实现的)
数据分片
假设客户端发送一条get命令到服务端,服务端发现该元素并非在自己的hash slot内,会返回元素的slot。客户端收到后会纠正自己记录的错误hash slot信息,重新获取元素
跳转重定位
为了避免脑裂造成数据丢失,监听实例的心跳通信时间可以实当延长,减小因为网络抖动造成假宕机的可能
网络抖动
可以根据配置来确定如果某主从宕机集群还对外提供完整服务,还是只提供当前健全的服务
集群是否完整才能对外提供服务
1.slave监测到自己的master节点fail,会向全体master节点发送广播FAILOVER
2.其他master接收到后会去验证master是否已经fail,如果是就返回ack
3.slave的currentEpoch会根据master返回的ack去+1,当超过预期设置的选举阈值就成为新的master
4.发送广播通知其他节点,slave转换成新的master节点
延迟计算公式:DELAY = 500ms + random(0 ~ 500ms) + SLAVE_RANK * 1000ms
•SLAVE_RANK表示此slave已经从master复制数据的总量的rank。Rank越小代表已复制的数据越新
这种方式下,持有最新数据的slave将会首先发起选举(理论上)
补充:其他master收到failover后并不是立即对master节点尝试通信,而是有计算公式
选举原理
增加新的主从节点到集群
需要重新分片hash slot
水平扩展
数据倾斜
数据平均分配
Cluster
高可用
Redisson-Lock流程图
lua脚本
如何保证原子性?
监听器+watchdog
如何保证业务未执行完前,即使到过期时间也不释放锁,给锁续时?
导致redis阻塞线程,响应慢
网络拥挤
过期删除,如果没有异步删除就可能导致无法响应客户端的请求
避免一次性取出全部数据
危害
分段、分片存储
big list、big hash
如何解决
大Key
简述:大量的缓存数据在同一时间过期,导致大量请求直接请求到数据库层
1、固定过期时间 + 随机过期时间
缓存击穿
简述:缓存热点数据,查询不存在的key导致请求直接访问到数据库层
1、缓存空对象,避免查询不存在的key没有缓存(必须设置过期时间
2、布隆过滤器
缓存穿透
简述:缓存服务宕机(压力过大、异常情况),导致大量请求访问数据库层,数据库层也宕机导致其他服务也受到影响
1、缓存服务高可用(哨兵、主从、cluster)
2、超出峰值tps限流、熔断、降级
缓存雪崩
常见缓存问题
简述:非热点数据(冷数据),并没有被缓存,某一原因导致突然访问量飙升,直接访问数据库层
1、分布式锁,db操作成功后缓存到redis
热点缓存key重建优化
简述:数据被缓存后,并发情况下用户对数据进行修改,db可能与缓存的信息不一致
核心关注点:分什么样的场景,要满足多大程度的强一致性,要求一致性越高性能和复杂度都会有所不同
1、非强一致性、并发量低,例如:我的购物车、我的主页信息,可以更新db后直接删除缓存,后续根据系统的缓存逻辑自动重新缓存
2、强一致性(可接受并发情况下容错),先更新db,后更新缓存并设置过期时间
3、延迟双删,防止并发情况下删除缓存的线程被阻塞,其他线程更新缓存导致数据不一致,可以先sleep一段时间后再删除缓存
4、阿里开源的canal监听mysql的binlog日志及时修改缓存,但是引入新的数据库中间件增加系统的复杂度
4、分布式锁(读写锁
缓存与数据库双写不一致
常见问题
读数据时验证数据是否过期,如果过期则直接删除这个key
1.被动删除(惰性删除
惰性删除无法满足清理大量冷数据时,redis会主动定期淘汰一批已过期的key
2.主动删除
3.内存达到maxmemory执行主动清理策略
volatile-ttl
allkeys-random
volatile-random
allkeys-lru
volatile-lru(最近最少使用
allkeys-lfu
volatile-lfu(最不经常使用
内存满后提示客户端无法写入数据“OOM”
noeviction
4.0之后增加了一些新的策略
过期策略
使用redission可以轻松实现
分布式系统全局序列号是指在分布式系统中,为了保证各个节点之间的数据同步,而对数据进行编号的一种机制。
概述
全局序列号必须在整个分布式系统中是唯一的,不能重复。
1.全局唯一性
全局序列号必须是递增的,每次生成的序列号都要比上一次生成的序列号大。
2.递增性
全局序列号必须是有序的,保证数据在分布式系统中的顺序性。
3.有序性
全局序列号必须是可比较的,可以通过比较序列号的大小来判断数据的先后顺序。
4.可比较性
全局序列号的生成不能过于复杂,否则会影响系统的性能。
5.易于生成
全局序列号的传输必须是高效可靠的,否则会影响系统的性能。
6.易于传输
全局序列号的存储必须是高效可靠的,否则会影响系统的性能。
7.易于存储
特点
在分布式系统中,为了保证各个节点之间的数据同步,需要使用全局序列号来对事务进行编号。
1.分布式事务
在分布式系统中,为了保证各个节点之间的数据同步,需要使用全局序列号来对锁进行编号。
2.分布式锁
在分布式系统中,为了保证消息的顺序性,需要使用全局序列号来对消息进行编号。
3.分布式消息队列
在分布式系统中,为了保证日志的顺序性,需要使用全局序列号来对日志进行编号。
4.分布式日志
在分布式系统中,为了保证缓存的一致性,需要使用全局序列号来对缓存进行编号。
5.分布式缓存
分布式系统全局序列号
优秀Redis实战文章推荐
redis
0 条评论
回复 删除
下一页