redis知识点
场景类
缓存
缓存穿透
定义:大量查询业务不存在的key击穿redis,直接查询数据库.
解决方案:
-
value null
实施:来一个这样的key,写入到缓存中,将其值设置为null。缺点:
- 会缓存大量这样的内容,内存存在溢出可能。
- 后续如果有这样的业务key生成,则缓存中的数据就会成为脏数据。
-
布隆过滤器
实施:使用bitmap存储所有key,实现布隆过滤。
缺点:因为存的是位图,所以key存储只能是整数型,那就决定了:
- key设计之初就是整型。
-
要么经过hash函数转换为整型。
Tips:hash函数可能出现不同key的hash值一样,存在误判。有部分数据穿透redis,进入数据库查询。
redisson guava 都可以设置误判率。
缓存击穿
定义:给key设置了过期时间,当key过期的时候,恰好这个时间点这个key有大量并发请求过来,这些请求可能会瞬间把DB压垮。
解决方案:
不过期:热点数据不过期。
互斥锁:当到达过期时间时,说给key的value加上互斥锁,等缓存重建后再释放锁。
缓存雪崩
定义:因为key的过期时间是同一个时间导致同一时段内缓存key大量同时失效或者redis服务器宕机,导致大量请求到达数据库,带来巨大压力。
解决方案
- 给不同的key设置不同的过期时间,给定值后面加上随机数字。
- 给缓存业务加入降级限流策略(熔断或者降级)
- 设置多级缓存(Guava、Caffeine)
- 提升Redis集群的稳定性设置为哨兵模式+集群模式
双写一致
定义:写入数据库和缓存的数据保持一致。
解决方案
- 延时双删
因为redis写入有延迟以及redis的哨兵以及集群模式,节点之间的同步也有延时。这种办法不能保证强一致性,可能会有脏数据的风险。
为什么要删除两次?因为无论是先删除数据库还是先删除缓存,在高并发场景下都没办法保证数据一致。
而延时删除以及api操作便捷性上来选择二次删除缓存比较合理点。
- 加锁
- 基于中间件
- MQ的可靠性广播以及ACK
- zk的监听机制
- canal充当mysql的从节点,感知到数据变更同步给缓存服务器,代码零侵入。
分布式锁
标准的同个JVM中如果出现互斥资源抢夺,则就会使用互斥锁,来保证互斥资源被唯一一个本JVM中的线程抢到,但是在多节点的服务场景中则行不通,因为JVM之间是隔离的,这种情况则需要第三方互斥锁来保证多个JVM的隔离线程之间抢夺互斥资源。
-
setnx
可以实现分布式锁,但是有个问题,过期时间的设定受制于业务场景,没办法轻易量化过期时间,所以可以用但是不灵活。 -
redission
-
原理
基于setnx的基础上,当前线程加入了看门狗策略当锁过期时,判断业务是否完成来给锁续期,业务完成则通知看门狗释放锁;
另一个线程则一直轮训等待锁的释放。
lua脚本保证加锁和释放锁的原子性。(事务保证)
-
是否可以重入?
可以该锁的value中记录了持有锁的线程id以及重入次数,来判断重入可行性(thread-id相同)以及释放次数。 -
主从一致性?
redlock 会保证在redis一半以上的节点加锁成功后才会返回加锁成功,避免在只在主节点加锁成功,这样只能在一定程度内保证锁数据一致性。
也就是AP特性,只要保证可用就好。
如果需要强一致性,建议使用CP架构的Zookeeper.
数据持久化
-
-
RDB
bgsave
读操作:开始时会fork一个子进程,同时将自己操作的页表也拷贝一份给子进程,这样子进程就可以通过页表完整的将fork之前的内存数据写入到RDB文件中。
写操作:利用copy-on-write技术,将原来物理内存中的要修改的数据拷贝一份进行写操作,通过主进程进行读数据的操作也会作用于新的数据副本。原来的物理内存数据就会变成read-only只用于数据的持久化。 -
AOF
写入操作数据的所有变更记录,同时加入重写操作缩减数据的体积大小。
bgrewriteaof,开启之后只会保留操作记录的最新一条,保存策略有两种:- 数据百分比
达到当前数据的百分之多少,就开始重写。 - 数据大小
当当前数据累计到指定大小就开始重写。
- 数据百分比
数据删除
-
惰性删除
优点:cpu友好,当过了过期时间,只有当key再次被使用时,才会从内存中被删除。
缺点:内存不友好,如果大量的过期key,没有被再次使用,则永远滞留在内存中。 -
定期删除
每隔一段时间从一定数量的数据库中筛选一定数量的随机key进行检查,过期就删除掉。- SLOW
定时:每次默认频率为10hz,每次不超过25ms - FAST
频率不固定,两次间隔不低于2ms,每次耗时不超过1ms。
优点:可以量化删除的频次,减少对CPU的影响。
缺点:频次不好控制。
- SLOW
过期删除策略:两种配置使用。
数据淘汰
配置的内存用完了,redis会怎么办?根据配置测略有以下表现。
建议策略:allkeys-lru(热数据策略)。
集群同步
-
全量同步
-
从节点执行relicaof命令。
-
携带repid和offset发起同步请求,建立同步连接,请求数据同步。
-
根据repid是否一致判断是否为第一次同步(是否需要全量同步)
-
是第一次返回master的repid和offset给到从节点
-
从节点保存版本信息。
-
执行bgsave命令,生成RDB。
-
发送RDB文件。
-
从节点清空本地数据,加载RDB文件。
-
记录RDB期间的所有命令生成(repl_baklog)
-
发送repl_baklog中的命令。
-
从节点执行接受到的命令。
-
-
增量同步
主要依据原则就是offset,如果从节点上送的offset小于主节点,则发起增量同步任务(主节点发送offset之后的命令给到从节点)。
哨兵模式
-
监控
监控主从节点的状态。
哨兵节点每隔1s发送ping命令给到集群节点,检测节点的存活。 -
自动状态恢复
监控主节点如果发生故障则启动故障转移,找一台从节点将slave提升为master。
下线判断:- 主动下线
ping命令在一定时间内未得到返回。 - 被动下线
半数以上的哨兵节点判断主节点出现故障,则必须下线。
选主策略:
- 剔除响应时间过长的slave节点。
- 判断slave_priority,值越小优先级越高。
- 如果前者一样,判断offset,offset越大,优先级越高。
- 最后则是节点id,越小优先级越高。
- 主动下线
-
通知
将最新的集群数据通知给所有的redisclient。
集群脑裂
现象:由于集群内部网络通信问题造成redis集群分裂成若干个小集群(小集群也是哨兵们推选出来的),当客户端连接是老的master,一直在写入数据,一旦网络恢复,集群新选举的master反生变动,原来的master可能会变为从节点(slave-priority决定),从新的master同步数据造成数据丢失。
解决:
- 最小集群原则:集群中最少存在一个从节点才能算集群,否则节点做下线处理。
- 通信原则:主节点和从节点之间的复制和同步不能超过特定时间,超过踢出集群,保证节点之前的网络通信问题。
分片集群
引入分片、多主、槽的概念,增加数据并发写的能力以及集群存储数据的能力。
- master之间通过ping来实现通信检测活跃。
- 每个master可以配置多个slave增加并发读的能力。
槽:Redis集群引入哈希槽的概念,集群有16384个槽,每个key经过CRC16的校验后对16384取模来决定放置那个槽,集群每个节点负责一部分的hash槽。
这样就决定了读和写都是根据key来hash取模路由到不通的节点的。
Tips:如果想要同样的数据存储到同一个节点中可以采用同样的key前缀来达到这一效果(同样的前缀hash取模后可以保证落入在同一个节点的hash槽)。
为什么快
- 纯内存操作
- 单线程通信,省去了多线程之间的上下文切换。
- 网络I/O的多路复用。
IO多路复用的原理:
利用单个线程同时监控多个socket,并在某个Socket可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。
不同的Socket监听、通知方式:
- select、pool只会通知Socket就绪,但是不确定具体是那个Socket,需要用户进程逐个来确认是那个就绪。
- epool则Socket会在就绪后通知用户进程时,携带自己的信息写入用户空间。
Redis的网络模型:
增加了事件派发机制:
- 连接应答处理器
处理来自客户端的请求 - 命令请求处理器
- 接受请求数据
- 把数据转换为Redis命令
- 选择并执行命令把结果写入到缓冲队列。
- 命令回复处理器
从缓冲区中拿到数据返回给客户端。
Tips: 从6.0开始接受请求数据->把数据转为Redis命令以及从缓冲区获取数据回复给客户端处理器改为多线程,提供更好请求以及响应处理能力。
标签:知识点,缓存,redis,集群,key,数据,节点 From: https://www.cnblogs.com/tyxy/p/17808303.html