# Redis面试
# Redis使用场景
# 一些具体使用场景
- 计数器(incr命令)
- 保存token(String)
- 消息队列(List)
- 延迟队列(Zset)
# 缓存
击穿、缓存、穿透 双写一致性、持久化 数据过期、淘汰策略
# 缓存穿透:大量请求mysql也不存在的数据,此时Redis缓存中自然也没有这个数据,此时会导致每次请求都需要查询数据库。
简单解决方案
- 接口层增加校验
- 缓存空数据,查询返回的数据为空,仍把这个空结果进行缓存,优化:设置超时时间(不超过5分钟)
- 优点:简单
- 缺点:如果key值变化频繁,会导致 Redis 中缓存大量无效的 key,内存消耗严重,并且出现不一致问题
- 不一致原因:如果此时存储层添加了这个数据,那此段时间就会出现缓存层和存储层数据的不一致
- 解决:利用消息队列或者其它异步方式清理缓存中的空对象。
主流解决方法
- 布隆过滤器
- 将所有可能存在的数据hash到一个足够大的bitmap中(bitmap数组中只能存0或1)
- 作用:用于检索一个元素是否在一个集合中
- 影响误判的因素(影响Hash碰撞)
- Hash函数的个数
- 位数组的长度(数组越大误判率越小,但是会带来更多内存消耗)
- 种类
- Google的Guava,存储在JVM
- 分布式,存储在Redis位图
- 优点:内存占用较少,没有多余key
- 缺点:存在误判,且难以维护(数据不能删除,需要定时更新数据即重建)
# 缓存击穿:某个热点key过期失效的时候,恰好有大量并发请求访问这个key,瞬间把DB击垮。
- 简单解决方案
- 逻辑过期(不设置过期时间)
- 在value中添加expire字段记录过期时间,通过异步的⽅式不断的刷新过期时间(通知一个异步线程刷新缓存),在刷新成功前请求的都是过期数据。
- 不能保证数据强一致性,好处是高可用,性能也较优
主流解决方案
- 分布式锁(互斥锁)
- ⽐如请求查询A,发现缓存中没有,对A这个key加锁,同时去数据库查询数据,写⼊缓存,再返回给⽤户,这样后⾯的请求就可以从缓存中拿到数据了
- 可以保证数据的强一致性,但相对性能差
# 缓存雪崩:同一时段的大量缓存key同时失效或者Redis服务宕机,导致大量请求到数据库,带来巨大压力
解决方案
- 给缓存key添加随机因子,分散失效时间
- 利用Redis集群提高服务高可用
- 哨兵模式
- 集群模式
- 给业务添加多级缓存
- 第一级缓存失效的基础上,访问二级缓存,每一级缓存的失效时间都不同
- Guava、Caffine做一级缓存,Redis做二级缓存
- 给缓存业务添加降级限流策略(保底策略)
- 当出现大量缓存失效,而且处在高并发高负荷的情况下,在业务系统内部暂时舍弃对一些非核心的接口和数据的请求,而直接返回一个提前准备好的 fallback(退路)错误处理信息
- ngnix或spring cloud gateway
TIP
穿透无中生有key,布隆过滤null隔离。
缓存击穿过期key,锁与非期解难题。
雪崩大量过期key,过期时间要随机。
面试必考三兄弟,可用限流来保底。
# 双写一致性
参考文章:缓存与数据库一致性问题 (opens new window)
数据一致性在指的是缓存双写一致性,由于想要提高应用的性能,可以引入「缓存」来解决
引入缓存后,需要考虑缓存和数据库一致性问题,可选的方案有:「更新数据库 + 更新缓存」、「更新数据库 + 删除缓存」
- 更新数据库 + 更新缓存方案,在「并发」场景下无法保证缓存和数据一致性,且存在「缓存资源浪费」和「机器性能浪费」的情况发生
- 在更新数据库 + 删除缓存的方案中,
- 「先删除缓存,再更新数据库」在「并发」场景下依旧有数据不一致问题,解决方案是「延迟双删」,但这个延迟时间很难评估,所以推荐用「先更新数据库,再删除缓存」的方案
- 「先更新数据库,再删除缓存」方案下,为了保证两步都成功执行,需配合「消息队列」或「订阅变更日志」的方案来做,本质是通过「重试」的方式保证数据一致性
- 「先更新数据库,再删除缓存」方案下,「读写分离 + 主从库延迟」也会导致缓存和数据库不一致,缓解此问题的方案是「延迟双删」,凭借经验发送「延迟消息」到队列中,延迟删除缓存,同时也要控制主从库延迟,尽可能降低不一致发生的概率
- 因此最终解决的方案取决于业务背景对一致性的要求
- 一致性要求不高(允许延迟一致性)
- 延迟双删:先删缓存再更新数据库,等几百毫秒再删缓存 (延迟时间根据具体的业务耗时而定)(面试喜欢问,但是不常用)
- 使用中间件如消息队列(MQ)或订阅变更日志(阿里巴巴的canal)(可达到最终一致性)
- 引入消息队列:把要删除的key或者删除失败的key丢进消息队列,利用消息队列的重试机制,重试删除对应的key。
- 订阅变更日志:用一个服务(阿里的 canal)去监听数据库的binlog,获取需要操作的数据,然后用一个公共的服务获取订阅程序传来的信息,进行缓存删除操作,这种方式对业务代码侵入性很低,因为canal是基于mysql主从复制用到的binlog来操作的。
- 一致性要求高(强一致、但性能低)
- Redission中的读写锁readLock、writeLock,读共享,写互斥
- 一致性要求不高(允许延迟一致性)
问:redis作为缓存,mysql的数据如何与redis进行同步呢?(双写一致性)
回答时要设置前提,一定要先介绍自己的业务背景
- 一致性要求高
- 允许延迟一致性
TIP
redis做为缓存,mysql的数据如何与redis进行同步呢?(双写一致性)
- 介绍自己简历上的业务,我们当时是把文章的热点数据存入到了缓存中,虽然是热点数据,但是实时要求性并没有那么高,所以,我们当时采用的是异步的方案同步的数据
- 我们当时是把抢券的库存存入到了缓存中,这个需要实时的进行数据同步,为了保证数据的强一致,我们当时采用的是redisson提供的读写锁来保证数据的同步
那你来介绍一下异步的方案(你来介绍一下redisson读写锁的这种方案)
- 允许延时一致的业务,采用异步通知
- 使用MQ中间中间件,更新数据之后,通知缓存删除
- 利用canal中间件,不需要修改业务代码,伪装为mysql的一个从节点,canal通过读取binlog数据更新缓存
- 强一致性的,采用Redisson提供的读写锁
- 共享锁:读锁readLock,加锁之后,其他线程可以共享读操作
- 排他锁:独占锁writeLock也叫,加锁之后,阻塞其他线程读写操作
# 持久化
使用缓存的时候,我们经常需要对内存中的数据进行持久化也就是将内存中的数据写入到硬盘中。大部分原因是为了之后重用数据(比如重启机器、机器故障之后恢复数据),或者是为了做数据同步(比如 Redis 集群的主从节点通过 RDB 文件同步数据)。
Redis 不同于 Memcached 的很重要一点就是,Redis 支持持久化,而且支持 3 种持久化方式:
- 快照(snapshotting,RDB)
- 只追加文件(append-only file, AOF)
- RDB 和 AOF 的混合持久化(Redis 4.0 新增)
RDB(Redis Database Backup file)(数据备份文件),也被叫做数据快照,把内存中的所有数据都记录到磁盘中,当Redis实例故障重启后,从磁盘读取快照文件,恢复数据。
原理
- Redis会单独创建(fork)一个与当前进程一模一样的子进程来进行持久化,将数据写入到一个临时文件中,待持久化结束后替换上次持久化好的文件。
- 相当于两个redis进程,这期间主进程不参与持久化,保证了redis的高性能
- fork的时候底层Linux使用的copy on write技术,即写时复制
- 当主进程执行读操作时,访问共享内存
- 当主进程执行写操作时,则会拷贝一份数据,执行写操作
- Redis会单独创建(fork)一个与当前进程一模一样的子进程来进行持久化,将数据写入到一个临时文件中,待持久化结束后替换上次持久化好的文件。
触发
- 客户端执行shutdown命令时,如果没有开启aof时会触发
- 配置文件中(redis.conf)快照配置, 有三个默认配置
- save 900 1(15分钟内有1次修改)
- save 300 10(5分钟内有1次修改)
- save 60 10000(1分钟内有1次修改)
- 执行save或bgsave命令
- save 由Redis主进程来执行RDB
- bgsave开启子进程执行RDB,避免主进程受到影响
- bgsave会fork子进程异步持久化
- 执行flushall命令
- 清空内存中的数据,同时触发持久化,清空磁盘
特点
- 优点:恢复的时候比较快,适合大规模的数据恢复
- 缺点
- 如遇突然宕机,丢失的数据比较多
- 如果生成的快照文件比较大会影响redis性能
AOF(Append Only File、只追加文件),Redis处理的每一个写命令都会记录在AOF文件,可以看作是命令日志文件。
原理
- 将所有的写命令追加到AOF缓冲区中,根据对应的写入策略向硬盘进行同步操作
- 由主进程完成
- AOF文件存的是顺序命令序列
- 随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的
- fork子进程来进行
- 持久化文件可以在redis.conf中配置:appendfilename "append only.aof"
- 将所有的写命令追加到AOF缓冲区中,根据对应的写入策略向硬盘进行同步操作
触发
- 默认情况下Redis没有开启AOF方式的持久化(Redis6.0之后已经默认开启了),可以通过appendonly参数开启,在redis.conf中配置
- 手动开启:appendonly yes # 默认是no
- 线上开启:CONFIG SET append only yes,避免丢失数据
- 开启后redis会保留一块内存供缓冲区使用,默认是1M
- AOF和RDB同时开启时,只保留save 900 1减少fork子进程的次数
- 写入/刷盘策略:在redis.conf中配置,默认是appendsync everysec
- everysec:写命令执行完先放入AOF缓冲区,然后每隔一秒将缓冲区数据写到AOF文件
- 效率高,可能会丢失1秒的数据【默认也推荐使用】
- no:写命令执行完先放入AOF缓冲区,由操作系统决定何时将缓冲区内容写回磁盘、
- 次数少,效率高,不安全「追求效率」
- always:每执行一次写命令,立即记录到AOF文件
- 效率低,安全「追求安全」
- everysec:写命令执行完先放入AOF缓冲区,然后每隔一秒将缓冲区数据写到AOF文件
- 重写机制
- 通过bgrewrite命令,可以让AOF文件执行重写功能
- set num 123、set name jack、set num 666 =》mset name jack num 666
- 在redis.conf中配置重写触发机制
- 默认配置
- auto-aof-rewrite-min-size 64M
- 当文件大小大于等于64MB时触发重写
- 优化:由于重写会fork子进程,为了减少重写次数,可以配置为5GB以上
- auto-aof-rewrite-percentage 100
- 指文件比上次文件增长超过100个percent(即一倍)时触发重写
- auto-aof-rewrite-min-size 64M
- 通过bgrewrite命令,可以让AOF文件执行重写功能
特点
- 优点
- 以append-only模式写入,没有磁盘寻址开销,写入性能高,数据完整性高
- 相比于RDB,丢失的数据更少
- 缺点
- 不适合冷备,恢复文件大,恢复速度慢
- 优点
混合持久化(AOF+RDB)
- 配置 在redis.conf中配置aof-use-rdb-preamble yes,Redis5.0以后默认开启
- 优化重写机制
- 重写后新的AOF文件前半段是RDB格式的全量数据,后半段是AOF格式的增量数据
- 特点
- 优点
- 由于绝大部分都是RDB格式,加载速度快
- 同时结合AOF,增量数据得以保存,数据更少丢失
- 缺点
- 兼容性差,4.0之前不支持
- 可读性差
- 优点
Redis启动后持久化文件的加载流程:
- 先判断时候开启了AOF
- 如果存在AOF文件,则直接加载AOF文件
- 如果找不到AOF文件,则直接启动,不会加载RDB文件
- 如果没有开启AOF,则会去加载RDB文件,通过RDB来持久化数据
- 生产环境建议AOF和RDB同时使用,RDB做容灾备份
TIP
redis做为缓存,数据的持久化是怎么做的?
在Redis中提供了两种数据持久化的方式:1、RDB 2、AOF
这两种持久化方式有什么区别呢?
RDB是一个快照文件,它是把redis内存存储的数据写到磁盘上,当redis实例宕机恢复数据的时候,方便从RDB的快照文件中恢复数据。 AOF的含义是追加文件,当redis操作写命令的时候,都会存储这个文件中,当redis实例宕机恢复数据的时候,会从这个文件中再次执行一遍命令来恢复数据.
这两种方式,哪种恢复的比较快呢?
RDB因为是二进制文件,在保存的时候体积也是比较小的,它恢复的比较快,但是它有可能会丢数据,我们通常在项目中也会使用AOF来恢复数据,虽然AOF恢复的速度慢一些,但是它丢数据的风险要小很多,在AOF文件中可以设置刷盘策略,我们当时设置的就是每秒批量写入一次命令.
# 过期数据删除策略
- 惰性删除
- 当需要改key时才检查其是否过期,若过期则删除,反之返回改key
- 优点:对CPU友好,只会在使用到该key时才会进行过期检查,对于很多用不到的key不浪费时间进行过期检查
- 缺点:对内存不友好,可能存在大量过期key,内存不被释放
- 定期删除
- 每隔一段时间,就随机抽取一些key进行检查,删除里面过期的key
- 优点:通过限制删除操作执行的时常和频率来减少删除操作对CPU的影响,通过定时删除来有效释放过期key占用的内存
- 缺点:难以确定删除操作执行的时长和频率
Redis实际上是惰性删除+定期删除两种策略进行配合使用。
但是,仅仅通过给 key 设置过期时间还是有问题的。因为还是可能存在定期删除和惰性删除漏掉了很多过期 key 的情况。这样就导致大量过期 key 堆积在内存里,然后就 Out of memory 了。
怎么解决这个问题呢?答案就是:Redis内存数据淘汰机制。
# 内存数据淘汰机制
使用命令动态配置:config set maxmemory-policy <策略>
Redis提供了8种不同的数据淘汰策略
- allkeys-lru:从数据集中挑选最近最少使用的数据淘汰(留最常使用)
- allkeys-random:从数据集中任意选择数据淘汰
- volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰
- volatile-random:从已设置过期时间的数据集中任意选择数据淘汰
- volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰
- no-enviction:禁止驱逐数据,再写入会报错(默认不回收)
- volatile-lfu: 在已设置过期时间的键上,应用LFU(最近最少频率被使用)策略
- allkeys-lfu: 在所有键上,应用LFU(最近最少频率被使用)策略
LRU(Least Recently Used):最近最长时间未被使用,这个主要针对的是使用时间
LFU(Least Frequently Used):最近最少频率被使用,这个主要针对的是使用频率
TIP
关于数据淘汰策略其他的面试问题
数据库有1000万数据 ,Redis只能缓存20w数据, 如何保证Redis中的数据都是热点数据 ?
使用allkeys-lru(挑选最近最少使用的数据淘汰)淘汰策略,留下来的都是经常访问的热点数据
Redis的内存用完了会发生什么?
主要看数据淘汰策略是什么?如果是默认的配置( noeviction ),会直接报错
# 分布式锁
setnx、redisson
分布式锁使用的场景:集群情况下的定时任务、抢单、幂等性场景
setnx
获取锁
# 添加锁,NX是互斥,EX是设置超时时间
SET lock value NX EX 10
释放锁
# 释放锁则删除即可
DEL key
redisson
redisson实现的分布式锁是可重入的(同一个线程)
- 利用hash结构记录线程id和重入次数
KEY | VALUE_field | VALUE_value |
---|---|---|
Lock_yzm | Thread | 1 |
public void add1(){
RLock lock = redissonClient.getLock("Lock_yzm");
boolean isLock = lock.tryLock();
// 执行业务
add2();
// 释放锁
lock.unlock();
}
public void add2(){
RLock lock = redissonClient.getLock("Lock_yzm");
boolean isLock = lock.tryLock();
// 执行业务
// 释放锁
lock.unlock();
}
redisson实现的分布式锁不能保证主从一致性,但是可以使用红锁
集群中有主节点和从节点,主节点写,从节点读,此时线程1对主节点加锁,正常情况下主节点向从节点同步数据,不幸的是,主节点宕机了,此时在从节点中选出新的节点代替原来的主节点,加入线程2来了,对现在的主节点加锁,那么造成了多个线程同时持有锁,就很可能会造成数据的不一致。
RedLock(红锁):不能只在一个redis实例上创建锁,应该是在多个redis实例上创建锁(n / 2 + 1),避免在一个redis实例上加锁。
- 实现复杂
- 性能差
- 运维繁琐
TIP
redis分布式锁是如何实现的?
1、按照简历项目的业务场景描述分布式锁使用的场景
2、使用redisson实现的分布式锁,底层使用setnx和lua脚本(保证原子性)
Redisson实现分布式锁如何合理地控制锁的有效时长?
在redisson的分布式锁中,提供了一个WatchDog(看门狗),一个线程获取锁成功之后,WatchDog会给持有锁的线程续期(默认是每隔10秒续期一次)
Redisson的这个锁,可以重入吗?
可以重入,多个锁重入需要判断是否是当前线程,在redis中进行存储的时候使用的是hash结构来存储线程信息和重入的次数
Redisson锁能解决主从数据一致的问题吗?
不能解决,但是可以用redisson提供的红锁来解决,但是这样的话,性能就会很低,如果业务中非要保证数据的强一致性,可以采用zookeeper实现的分布式锁
# 其他面试题
# 集群
主从、哨兵、集群
# 主从模式
单点Redis的并发能力有限,并且可能会发生宕机,进一步提高并发能力,就需要搭建主从集群,实现读写分离。
主从全量同步
主从增量同步(slave重启或者后期数据变化)
主从同步的流程
全量同步:
- 从节点请求主节点同步数据(replication id、offset)
- 主节点判断是否是第一次请求,是第一次就与从节点同步版本信息(replication id、offset)
- 主节点执行bgsave,生成rdb文件后,发送给从节点去执行
- 在rdb生成期间,主节点会将此时的写操作以命令的方式记录到缓冲区(一个日志文件)
- 把生成之后的命令日志文件发送给从节点进行同步
增量同步(slave重启或者后期数据变化):
- 从节点请求主节点同步数据,主节点判断是否是第一次请求,不是第一次就获取从节点的offset值
- 主节点从命令日志中获取offset值之后的数据,发送给从节点进行数据同步
# 哨兵模式
虽然有了主从同步,但主节点要是挂了,还是需要程序员手动选择从节点升级为主节点提供写入服务,不够智能,为了使其自动化,Redis提供了哨兵(Sentinel)机制来实现主从集群的自动故障恢复。
- 一般有若干个节点组成哨兵Sentinel集群
- 监控:Sentinel会不断检查master和slave是否按预期工作
- 基于心跳机制检测服务状态,每隔1秒向集群的每个实例发送ping命令
- 主观下线:如果某sentinel节点发现某实例未在规定时间内响应,则认为该实例主观下线
- 客观下线:若超过quorum的sentinel都认为该实例主观下线,则该实例客观下线
- quorum值最好超过Sentinel实例数量的一半
- 基于心跳机制检测服务状态,每隔1秒向集群的每个实例发送ping命令
- 自动故障恢复:如果master故障,Sentinel会选出一个slave提升为master,故障实例恢复后也以新的master为主
- Sentinel领导者选举
- 使用Raft算法选举
- 每个在线的Sentinel节点都有资格成为领导者,当它确认主节点主观下线时候,会向其他Sentinel节点发送sentinel is-master-down-by-addr命令,要求将自己设置为领导者。
- 收到命令的Sentinel节点,如果没有同意过其他Sentinel节点的sentinel is-master-down-by-addr命令,将同意该请求,否则拒绝。
- 如果该Sentinel节点发现自己的票数已经大于等于max(quorum,num (sentinels) / 2 + 1),那么它将成为领导者。
- 如果此过程没有选举出领导者,将进入下一次选举。
- 使用Raft算法选举
- 主节点挑选规则
- 过滤:“不健康”(主观下线、断线)、5秒内没有回复过Sentinel节点ping响应、与主节点失联超过指定时间
- 选择优先级最高的从节点,slave-priority值越小优先级越高
- 选择复制偏移量offset最大的从节点(复制的最完整)
- 选择运行id最小的从节点
- Sentinel领导者选举
- 通知:Sentinel充当Redis客户端的服务来发现来源,集群发生故障转移时,将最新信息推送给Redis的客户端
TIP
怎么保证Redis的高并发高可用?
主从+哨兵模式:利用主从模式提高并发性,实现读写分离,哨兵模式实现主从集群的自动故障恢复(监控、自动故障恢复、通知)
你们使用的Redis是单点还是集群,哪种集群?
主从(1主1从)+哨兵就可以了,单点不超过10G内存(只存热点数据),如果Redis内存不足则可以给不同服务分配独立的Redis主从节点
redis集群脑裂,该怎么解决?
有时候由于网络等原因可能会出现脑裂的情况,就是说,由于redis master节点和redis salve节点和sentinel处于不同的网络分区,使得sentinel没有能够心跳感知到master,所以通过选举的方式提升了一个salve为master,这样就存在了两个master,就像大脑分裂了一样,这样会导致客户端还在old master那里写入数据,新节点无法同步数据,当网络恢复后,sentinel会将old master降为salve,这时再从新master同步数据,这会导致old master中的大量数据丢失。
解决:在redis的配置中可以设置:第一可以设置最少的salve节点个数,比如设置至少要有一个从节点才能同步数据,第二个可以设置主从数据复制和同步的延迟时间,达不到要求就拒绝请求,就可以避免大量的数据丢失
# 集群模式
主从和哨兵可用解决高可用、高并发读的问题,但如果有海量数据存储或高并发写时就会出现问题。使用分片集群模式可以解决这个问题。
Redis集群通过数据分区来实现数据的分布式存储,通过自动故障转移实现高可用。数据分区是在集群创建的时候完成的。自动故障转移类似于哨兵模式,但此时的集群:
- 有多个master,每个master保存不同的数据
- 每个master都可以有多个slave节点
- master之间通过ping检测彼此健康状态
- 客户端可以访问集群任意节点,最终都会被转发到正确节点
分片集群结构的数据读写
引入了 虚拟节点 的概念。Redis 集群使用的便是该方案,其中的虚拟节点称为 槽(slot)。槽是介于数据和实际节点之间的虚拟概念,每个实际节点包含一定数量的槽,每个槽包含哈希值在一定范围内的数据。
在使用了槽的一致性哈希分区中,槽是数据管理和迁移的基本单位。槽解耦了数据和实际节点之间的关系,增加或删除节点对系统的影响很小(只需重新分配少部分节点负责的槽位)。
Redis集群中有16384个hash槽,每个key通过hash运算(CRC16)后对16384取模得到其应该放置的槽位,集群中每个节点负责一部分hash槽。
tip:可以通过设置有效部分让一部分key放到相同节点。如 set {aaa}name salvatore
,此时用aaa计算得到其hash值进而得到其slot即槽位置。
# 事务
Redis 事务提供了一种将多个命令请求打包的功能。然后,再按顺序执行打包的所有命令,并且不会被中途打断。
Redis 可以通过 MULTI
,EXEC
,DISCARD
和 WATCH
等命令来实现事务(Transaction)功能。
Redis提供了简单的事务,但它对事务ACID的支持并不完备。MULTI
命令后可以输入多个命令,Redis 不会立即执行这些命令,而是将它们放到队列,当调用了 EXEC
命令后,再执行所有的命令。
Redis事务的注意点有哪些?
- Redis 事务是不支持回滚的,不像 MySQL 的事务一样,要么都执行要么都不执行,因此,Redis 事务其实是不满足原子性的。
- Redis 服务端在执行事务的过程中,不会被其他客户端发送来的命令请求打断。直到事务命令全部执行完毕才会执行其他客户端的命令。
- Redis 的三种持久化方式都会存在数据丢失的情况,因此,Redis 事务的持久性也是没办法保证的。
# Redis为什么快
Redis是纯内存操作,执行速度非常快
采用单线程,避免不必要的上下文切换可竞争条件,多线程还要考虑线程安全问题
使用I/O多路复用模型,非阻塞IO
Redis是纯内存操作,执行速度非常快,它的性能瓶颈是网络延迟而不是执行速度, I/O多路复用模型主要就是实现了高效的网络请求。
Redis通过IO多路复用来提高网络性能,并且支持各种不同的多路复用实现,并且将这些实现进行封装, 提供了统一的高性能事件库。
连接应答处理器
命令回复处理器,在Redis6.0之后,为了提升更好的性能,使用了多线程来处理回复事件
命令请求处理器,在Redis6.0之后,将命令的转换使用了多线程,增加命令转换速度,在命令执行的时候,依然是单线程
TIP
解释一下I/O多路复用模型?
I/O多路复用是指利用单个线程来同时监听多个Socket ,并在某个Socket可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。目前的I/O多路复用都是采用的epoll模式实现,它会在通知用户进程Socket就绪的同时,把已就绪的Socket写入用户空间,不需要挨个遍历Socket来判断是否就绪,提升了性能。
Redis网络模型就是使用I/O多路复用的基础上结合事件的处理器来应对多个Socket请求。
Redis6.0引入多线程来异步处理网络请求,提高网络IO性能(因为影响Redis性能的往往是网络IO),读写线程仍然是单线程(读写在内存中操作,速度很快)。