Redis

Redis是单线程,为什么还那么快?

redis是一个基于内存实现的key-value数据格式的nosql。

  1. 它完全基于内存操作,执行速度非常快
  2. 降低了CPU的消耗。采用单线程,避免不必要的上下文切换和竞争条件,也不用考虑多线程带来的线程安全问题

采用更高效的非阻塞I/O。redis采用epoll作为I/O多路复用技术的实现,使得Redis在网络I/O操作中能并发处理大量的客户端请求,实现高吞吐率。

什么是I/O多路复用?
I/O多路复用是一种同步的I/O模型,利用I/O多路复用模型可以实现一个线程监视多个文件句柄。一旦某个文件句柄就绪,就通知对应的应用程序进行相应的读写操作;没有文件句柄就绪时,就会阻塞应用程序,从而释放出CPU资源。

简单来说,就是单个线程处理多个TCP连接,尽可能的减少系统开销,无需创建和维护过多的线程或进程

实现I/O多路复用模型有三种:分别是select,poll,epoll

  1. select模型基本实现原理:采用轮询和遍历的方式实现I/O多路复用,使用数组结构来存储文件句柄(FD),最大连接是为1024;优点是跨平台支持性好,几乎支持所有平台,缺点由于使用轮询的方式全盘扫描,会随着FD的数量增多和导致性能下降,时间复杂度是O(n)。
  2. poll模型也是是轮询加遍历的方式,采用链表的方式来存储FD;优点没有最大FD的限制,缺点和select一样,使用轮询的方式全盘扫描,会随着FD的数量增多和导致性能下降,时间复杂度是O(n)。
  3. epoll模式使用时间通知机制来触发相关的I/O操作,将轮询改为回调,提高CPU的执行效率,使用B+数来存储FD,没有FD数量的限制,缺点只能在Linux下工作

缓存雪崩:指在缓存中的大量数据,在同一时刻全部过期,到达数据库,导致数据库压力增加,造成数据库服务器 崩溃的现象。
产生原因与解决:

  1. 缓存中间件宕机,可以通过对缓存做高可用集群来避免。
  2. 对缓存失效时间增加1-5min的随机值

缓存穿透:短时间内有大量不存在缓存中的数据请求到应用中,而这些key在缓存中找不到,穿透到了数据库。

解决:

  1. 把无效的key也保存到缓存中,并设置一个特殊的值,比如null
  2. 如果攻击者不断用随机的不存在key访问数据库,可以用布隆过滤器来实现,在系统启动的时候把目标数据全部缓存到布隆过滤器里面。

缓存击穿:指存在缓存中的热点数据过期时,仍有大量的请求进来到数据库,导致数据库压力增大

解决:

  1. 设置缓存永不过期。将某些热点数据的缓存设置保持长期有效,避免因过期导致的击穿问题
  2. 使用互斥锁或分布式锁。当缓存失效时,使用锁机制控制并发请求,避免同一资源的并发读写竞争

Redis持久化机制RDB和AOF

RDB是通过快照的方式来进行持久化的,也就是说,Redis根据快照的触发条件,把内存里面的数据快照写入磁盘,以二进制的压缩文件进行存储

RDB快照触发条件:

  1. 执行bgsave命令触发异步快照,执行save命令触发同步快照,同步快照会阻塞客户端的执行命令
  2. 根据redis.conf文件里面的配置自动触发bgsave (conf文件内 # save 3600 1 300 100 60 10000)
  3. 主从复制时触发

RDB执行原理:

bgsave执行时会fork(浅拷贝)主进程得到子进程,子进程会共享主进程的内存数据(页表:记录虚拟地址与物理地址的映射关系),完成fork后读取内存数据并写入RDB文件中;这个速度比较快,纳秒级别的

fork采用的copy-on-write技术:

当主进程执行读操作时,共享内存

当主进程执行写操作时,则会拷贝一份数据,执行写操作

AOF持久化是一种近乎实时的方式,把Redis Server执行的事务命令进行追加存储,简单来说就是客户端执行一个数据变更的操作,Redis Server就会把这个命令追加到AOF缓冲区的末尾,再把缓冲区的数据写入磁盘的AOF文件,至于最终什么时候真正持久到磁盘,是根据刷胖策略决定的。AOF默认是关闭的,需要把redis.conf中的appendonly修改为yes进行开启。

AOF刷盘三种策略:

  1. appendfsync always #表示每执行一次写操作,立即写入AOF文件
  2. appendfsync everysec #写命令执行完先放到AOF缓冲区,每隔一秒写入AOF文件,默认策略
  3. appendfsync no #写命令执行完先放到AOF缓冲区,由操作系统决定什么时候写入AOF文件

区别:

配置项

刷盘时机

优点

缺点

always

同步刷盘

可靠性高,几乎不丢数据

性能影响大

everysec

每秒刷盘

性能适中

最多丢失一秒数据

no

操作系统刷盘

性能最好

可靠性较差,可能丢失大量数据

AOF重写机制:

因为是记录命令,AOF文件要比RDB文件大的多。而且AOF会记录对同一个key的多条命令,但只有最后一条有意义。通过bgrewriteaof 命令可以让aof执行重写功能,用最少的命令达到相同的效果

例如set num 12

set name  jack ====> bgrewriteaof  ===> mset name jack num 666

set num 666

Redis也会在触发阈值的时候自动去重写AOF文件,可在redis.conf中配置

#AOF文件比上次文件,增长多少百分比才会触发重写

auto-aof-rewrite-percentage  100

#AOF文件体积最小多大以上可以触发

auto-aof-rewrite-min-size  64mb

RDB和AOF各有优缺点,如果对数据安全性要求较高,在实际开发中可以结合两者来使用

RDB

AOF

持久化方式

定时对整个内存做快照

记录每次执行的命令

数据完整性

不完整,两次备份之间有缺少

相对完整,取决了刷盘策略

文件大小

会有压缩,体积相对小

记录命令,文件体积大

宕机恢复速度

很快

数据恢复优先级

低,因为没有AOF完整

高,数据完整性更高

系统资源占用

高,大量CPU和资源消耗

低,主要是磁盘IO资源,但AOF重写时会占有CPU和内存资源

使用场景

可以容忍数分钟的数据丢失,追求更快的启动速度

对数据安全性要求较高

Redis 数据删除策略 - 惰性删除、定期删除两者配合使用

惰性删除:设置该key过期时间后,不需要管它,当需要该key时,在检查它是否过期,如果过期就删除它, 没有就返回它所在的key

优点:对CPU友好,只会在使用该key时进行过期检查,对于许多用不到的key不用浪费时间去检查

缺点:对内存不友好,如果一个key过期了,但是一直没有去使用,那么该key就一直存在,不会释放

定期删除:每隔一段时间,就对一部分key进行检查,删除里面过期的key;随着时间推移,它会遍历redis 所有的key,直到都检查一遍,可以确保假如一个key过期了,就一定会被删除

定期删除有两种模式:

SLOW模式:定时任务,执行频率默认是10hz(每秒执行10次),每次不超过25ms,通过redis.conf中hz 选项来配置

FAST模式:执行频率不固定,但两次间隔不超过2ms,每次耗时不超过1ms

优点:可以通过限制删除操作执行时长和频率来减少删除操作对CPU的影响。另外定期删除,也能有效的释 放过期键占用的内存

缺点:难以确定删除操作执行的时长和频率

Redis淘汰策略:

当redis中的内存不够用时,此时在向Redis中添加新的key时,那么redis就会按照某一种规则将内存中的数据淘汰掉,这种数据的删除规则被称为内存的淘汰策略

Redis支持8中不同的策略来选择被删除的key:

noeviction :不淘汰任何key,但是内存满时不允许写入新数据,默认策略(会报错)

volatile - ttl : 对设置了ttl的key,比较key的剩余TTL值,TTL越小越先被淘汰

allkeys - random :对全体key,随机进行淘汰

volatile - random :对设置了ttl的key,随机进行淘汰

allkeys - lru :对全体的key,基于LRU算法进行淘汰

volatile - lru :对设置了ttl的key,基于LRU算法进行淘汰

allkeys - lfu :对全体的key,基于LFU算法进行淘汰

volatile - lfu : 对设置了ttl的可以,基于LFU算法进行淘汰

LRU算法:最近最少使用,用当前时间减去最后一次访问的时间,这个值越大则淘汰优先级越高

(在Redis中只是使用了简单的队列或链表去缓存数据,而mysql中Buffer_Poll也用到了LRU,只不过 mysql中设计了冷热数据分离的机构)

LFU算法:最少使用频率,会统计每个key的访问频率,值越小则淘汰优先级越高

(使用两个双向链表形成一个二维双向链表,横向保存访问频率,竖向保存访问频率相同的所有元素,新添加 的元素,访问次数默认为1,并添加到相同频率节点对应双向链表头部。当元素被访问时,就会增加对应key 的访问频次,并把当前访问的节点移动到下一个频次节点。)

数据淘汰策略-使用建议:

  1. 优先使用allkeys - lru策略,充分利用lru算法的优势,把最近最常使用的数据留在缓存中,如果业务有冷 热数据区分,建议使用
  2. 如果业务中数据访问频率不大,可以使用allkeys - random, 随机选择淘汰
  3. 如果业务中有置顶的需求,可以使用 volatile - lfu ,把置顶的数据不设置过期时间,这些时间就不会被淘 汰,会淘汰其他具有过期时间的数据
  4. 如果业务中有短时高频的数据,可以用 allkeys - lfu 或 volatile - lfu

Redis提供的集群方案有三种:

主从复制:读写分离,解决高并发读

哨兵模式:高可用

分片集群:高并发写

1、主从数据同步原理:

主从全量同步:

  1. slave(从节点)执行replicaof命令与master(主节点)建立连接
  2. 请求数据同步,slave会把自己的repId和offset发送给master
  3. master判断是不是第一次连接,是第一次,返回master的版本信息,slave保存版本信息;

(每个master都有唯一的Replication id简称repId,如果是第一次连接,master和slave的repId是不相等的,slave会保存master发送过来的repId和offset)

  1. matser执行bgsave命令,生成RDB文件,发送给slave节点RDB文件
  2. slave清空本地数据,写进RDB文件
  3. 若master生成RDB文件时,有其他数据在写入,master记录其中的所有数据,并写入repl_baklog日志文件
  4. master发送repl_baklog给slave
  5. slave执行接受到的文件命令
  6. 主从复制完成后,主节点每接收一个写操作都会通过复制缓冲区(replication_buffer)发送给从节点,保证主从节点数据一致

主从增量同步:

  1. slave发起psync命令携带repId和offset参数
  2. master判断请求repId是否一致,若不一致返回continue
  3. mater去repl_baking文件中获取offset之后的数据返回给slave

2、Redis哨兵作用:

redis提供哨兵(sentinel)机制来实现对主从集群的自动故障恢复

哨兵的结构和作用:

1、监控:sentinel会不断检查你的master和slave是否按预期工作

2、自动故障恢复:如果master故障了,sentinel会将一个slave提升为master,当故障实例恢复后也以新的 master为主

3、通知:Sentinel充当redis客户端的服务发现来源,当集群发生故障转移时,会将最新的消息推送给redis 的客户端

服务状态监控:

Sentinel基于心跳机制监控服务状态,每隔一秒向集群中的实例发生ping命令

·主观下线:如果某Sentinel发现某实例未在规定时间响应,则认为该实例是主观下线

·客观下线:若超过指定数量(quorum)的Sentinel都认为该实例主观下线,则该实例客观下线,quorum值最好超过Sentinel实例数量的一半。

哨兵选举规则:

·首先判断主与从节点断开时间长短,如超过指定值则排除该节点

·然后判断从节点的slave-priority值(redis.conf中配置),越小优先级越高

·如果slave-priority的值一样,则判断slave的offset值,值越大优先级越高

·最后判断slave节点的运行id大小,越小优先级越高

哨兵脑裂问题

·由于哨兵和集群中的主从节点可能处于不同的网络分区,哨兵只能监测到从节点们,这个时候哨兵会发现监测的节点中没有主节点,那么他会经过选举产生一个新的主节点,但是客户端这个时候还是会持续的向老的主节点发送数据,新的主节点此时时没有新的数据写入的,这样就造成了类似大脑分裂的情况。

·而当网络恢复后,哨兵会将老的主节点降为从节点,这时候再从新的主节点master中同步数据,老master会把自己节点中的数据清空,从而导致数据丢失

解决方案

修改Redis的两个参数来解决问题

min-replicas-to-write 1

#表示最少的slave节点为1个,这样就能保证如果出现网络问题,如果主节点没有从节点了,那么 服务端拒绝写入数据,这样老的主节点是没有新数据产生的

min-replicas-max-lag 5  

#表示数据复制和同步的延迟不能超过5秒

3、分片集群

作用:

·集群中有多个master,每个master保存不同的数据(解决高并发写)

·每个master有多个slave(解决高并发读)

·master之间通过ping检测彼此的健康(类似哨兵)

·客户端可以访问集群中的任意节点,最终都会转发到正确的节点(路由规则)

redis分片集群中数据是怎么存储和读取的(路由规则)

·Redis分片集群引入哈希槽的概念,Redis有16384个哈希槽

·将16384个槽分配到不同的实例上

·读写数据:根据key的有效部分计算哈希值,通过CRC16校验后对16384取余(有效部分,如果key前面有大括号,大括号的内容就是有效部分,如果没有,则以key作为有效部分)余数作为插槽,寻找插槽的实例

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江湖中的阿龙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值