Redis技术


目录

一、Redis万能回答(7个层面)

1、Redis读-写为什么这么快?

层面描述
内存存储没有磁盘IO上的开销;类似于HashMap,读写操作时间复杂度都是O(1)
执行是单线程 ① Redis6.0之前其核心网络IO模型使用的是单线程
② Redis6.0之后引入了多线程IO处理网络读写与协议解析;
但是不管哪个版本,Redis执行命令都是单线程,避免了多线程的切换与竞争锁的开销;
非阻塞-IO多路复用 Redis基于epoll函数模型实现多路复用IO技术(一个线程/进程处理多个网络模型的IO请求),以及Redis自身的事件处理模型将epoll模型中的连接、读写、关闭都转换为事件,减少网络IO时间;
数据结构:计算向数据移动 Redis本身提供了5种常见的数据结构(String、Hash、Set、List、SortedSet),每种类型提供了API方法,实现计算向数据移动,提高IO速度;
重写虚拟内存模型VM Redis本身的虚拟内存模型可以实现冷热数据分离(访问频率低的数据持久化到磁盘,内存空间优先存储热度高的数据,避免因内存不足造成访问速度下降)
注意:Redis的Swap分区没有用OS提供的,而是自己实现的;(Swap分区:当Linux内存不足时,会释放一部分物理空间,这部分被释放的数据存储在Swap分区中,待到用到这些数据时,在从Swap分区中恢复到内存)

2、Redis的持久化机制

机制触发机制原理优劣势
AOF日志① 每秒同步

② 每写同步

③ 不同步
1)将每一个redis的写指令记录在日志中,只追加不删除;当恢复时从头到尾执行一边写指令;

2)当AOF日志大小超过规定阈值时,触发重写机制;

3)低版本的redis重写机制时将AOF日志中的指令压缩到能恢复原数据的最小指令集;

4)高版本的redis重写机制是将AOF日志重写为RDB文件进行保存,恢复的时候先RDB,然后在AOF】
优势: 数据保存完整性较高;

缺点: 开启了AOF后会降低redis的整体性能,且持久化的文件会越来越大,恢复速度要慢;
RDB快照① save命令:阻塞Redis服务器,直到RDB保存结束;

② bgsave命令:Redis会在后台异步进行快照操作,快照同时还能响应客户端请求;
当 Redis 需要保存 dump.rdb 文件时, 服务器执行以下操作:

1)执行bgsave命令,Redis主进程会检查是否有子进程在执行RDB/AOF持久化任务,如果有的话,直接返回;

2) Redis 调用 fork() ,主线程阻塞,然后创建出子进程,阻塞解除;

3)子进程基于【写时复刻技术(COW)】将原数据写入到一个RDB文件中;

4)当子进程完成对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件;
优势: 恢复速度快;

缺点: RDB无法做到实时持久化,若在两次bgsave间宕机,则会丢失区间(分钟级)的增量数据,不适用于实时性要求较高的场景;

RDB快照-写时复刻技术(COW)

fork()之后,内核把父进程中所有的内存页的权限都设为read-only,然后子进程的地址空间指向父进程;

① 当父子进程都只读内存时,相安无事。

② 当其中某个redis请求写操作时,CPU硬件检测到内存页是read-only的,于是触发页异常中断(page-fault),陷入内核的一个中断例程。中断例程中,内核就会把触发的异常的页复制一份,于是父子进程各自持有独立的一份;

③ 然后父进程就可以继续执行写操作,子进程继续在原内存上进行RDB写入;


3、Redis的事务

层面解释
事务① Redis的单个操作都是原子性,要么执行要么不执行;

② Redis的批量操作可以通过【multi指令(事务开启) 和 exec指令(事务提交)】 或者 【Lua脚本】实现事务

③ Redis不支持事务回滚(非原子性):Redis事务可以理解为一个打包的批量执行脚本,单个操作是原子性的,但是批量指令不是原子的;事务中的某条指令执行失败,并不会导致前面的指令回滚,也不会阻止后边的指令继续执行;

⭐不支持事务回滚的原因: 【Redis命令只会因为语法错误而失败;在Multi命令开启事务后,这些语法错误在指令会入队失败,并被redis服务器进行记录;待执行exec命令后,如果发现有入队失败的记录,则事务就会拒绝执行并取消事务;因此这也保证了我们【redis不需要进行回滚】;保证redis内部的简洁与高效;

4、Redis的数据结构及其底层实现原理

数据结构底层原理
String
类型
String的底层原理是基于 动态字符串 (SDS) 实现的,它不仅保存了数据char[],而且保存了缓冲区已经占用空间的长度,以及缓冲区剩余可用空间的长度。

SDS相对于字符串char的优势:
(1) 修改SDS时,可以预先检查SDS的缓冲区空间是否足够,如果足够会先拓展SDS的空间,以防止缓冲区溢出。而char字符串不会检查缓冲区是否足够,容易出现溢出情况。
(2) SDS有着预分配空间的机制,可以减少为字符串重新分配空间的次数。
List
类型
List的底层原理是基于 双向链表 实现的。
Hash
类型
Hash的底层原理是基于 字典 实现的, 其解决哈希冲突的方法采用的是拉链法,具体的就是将哈希表的装载因子维持在一个合理的范围之内,方便rehash。
Set
类型
Set的底层原理是基于 intset数据结构和字典 实现的。
(1) 当存储的数据全都是整数类型,且数量小于512个时,会使用intset存储数据。intset是一个由整数组成的有序集合,可以进行二分查找。
(2) 当不满足使用intset的条件时,都使用字典存储数据,同样是使用拉链法解决哈希冲突。
zSet
类型
zset中的每个元素包含数据本身和一个对应的分数(score)。zset的数据本身不允许重复,但是score允许重复。其底层原理是:
(1) 当数据较少时,使用 ziplist (压缩列表) 存储数据,它是一种特殊的双向链表。ziplist占用连续内存,每项元素都是(数据+score)的方式连续存储,按照score从小到大排序。
(2) 当数据较多时,使用 字典 + 跳表 实现:字典用于根据key值查询score,跳表根据socre查找数据 (时间复杂度O(logN))。
Bitmap
类型
Bitmap是布隆过滤器的底层,每一位只存储0和1。1就代表存在,0代表不存在。

位图数据结构

数据结构原理优缺点使用场景
BitMapBitMap底层是int(32bit),一个int就能放32个数优点:
 ① 查询速度快,O(1)的查询复杂度;
 ② 节约空间,能够用少量空间存储大量的非重复数字;

缺点:
 BitMap不适合存储稀疏数据,会超级浪费空间;比如,一个Posting List ={1,9999},为了表示这两个数,BitMap就需要用9999个bit位表示这两个数;
与BitSet一致
BitSetBitSet底层是long(64bit),它实现了一个按需增长的位向量,位 set 的每个组件都有一个boolean值;两个BitSet之间可进行位运算(逻辑与、逻辑或、逻辑异或)等操作;给BitSet1个GB的空间,能够大约存储85亿个数

优缺点:
 与BitMap相比,能够快速的进行逻辑运算操作;其它的优缺点一样;
① 海量数据的统计工作,比如日志分析、用户数统计等;

② Bloom Filter(布隆过滤器);

③ 快速去重 + 快速查询O(1);
RoaringBitMap
(压缩位图索引)
RoaringBitMap底层结构:

① short[] keys:key = N / 65536


② Container[] values:value = N % 65536

③ int size:包含了有效key和values中有效数据对的数量;
解决了BitMap与BitSet存储稀疏数组时浪费空间的弊端;

压缩思想:与其重复写100遍0,不如声明0重复了100遍;
RBM是BitSet和BitMap的进阶版,集万千优点于一身;

5、Redis的键过期删除策略

策略描述
定时删除设置过期时间,到期立刻删除
惰性删除用到这个key时,再判断是否过期,然后再删除;
定期删除每隔一段时间,就对一些key进行检查,若过期就删除;

AOF主从同步过程中如何处理过期的key?

  • salve节点并不会主动删除过期的key;master当检测到某个key过期后,那么这个删除key的命令会随着AOF文件一起从master节点发送至所有的slave节点,salve收到master的DEL命令后,才会去删除key;保证主从同步的一致性;

6、Redis的内存淘汰策略

策略描述
AllKeys-LRU(最常用)基于LRU缓存算法,移除最近最少使用的key;
Volatile-LRU基于LRU缓存算法,移除设置了过期时间的全部key;
Volatile-TTL从设置了过期时间的数据集中,移除将要过期的key;
Volatile-random从设置了过期时间的数据集中,随机移除key;
AllKeys-random从键空间中,随机移除key;
No-eviction禁止移除key,内存不够写入时会报错;
Volatile-LFU维护每个key的使用次数,当内存不够时,从【设置了过期时间的key中】,删除使用频率最低的那个;
Allkeys-LFU维护每个key的使用次数,当内存不够时,从【全部的key中】,删除使用频率最低的那个;

7、Redis主从复制

模式描述场景
单机redis架构简单,部署方便;但是不保证数据可靠性,无节点备份,容易数据丢失;每秒的读QPS在1W以上
主从复制优点: 高可用性,能够实现读写分离;

缺点: ① 故障恢复复杂,master故障时,需要手动的选择一个slave节点作为新的master;
    ② Redis复制中断后,slave发起psync,如果同步失败会进行全量同步,此时会卡顿;且全量同步过程中由于写时复刻COW机制,可能会造成主库内存溢出;
读写分离
主从复制 + 哨兵集群优点: ① 故障自动转移,master故障,无需人为指定新master,会有选举机制 ;
    ② 高可用,有主观下线与客观下线两种机制,不轻易实施故障转移;
主要是解决单点故障问题,每秒的读QPS在10W级以上
Redis集群特点: 异步复制,不能保证数据的强一致性;缓存1T等超多的数据
Redis自研

计算向数据移动与数据向计算移动?

  • 数据向计算移动: 比如要获取某个集合的index = 2的数据,那么这种方式下,会先返回集合的全部数据给刻客户端,然后再取index = 2 位置上的元素展示;涉及到了大部分的数据移动,速度慢;Memcached就是这种;
  • 计算向数据移动: 直接在数据层找到index = 2 位置上的元素,然后返回给客户端;没有数据的大规模移动,计算逻辑是在数据层面上解决的;Redis的数据结构提供了诸多API方法就可以实现这一点;
    在这里插入图片描述

二、Rediss使用场景?

场景应用
分布式缓存持久化机制、键过期删除策略、内存淘汰策略
分布式锁setnx命令获取锁,返回1则说明获取锁成功,反之失败;减库存、秒杀等场景;信号量等等
分布式会话用redis保存全局session,结合SpringSession实现单点登录、社交登录等;
消息队列Redis提供了发布/订阅阻塞队列共嗯那个,可以实现简易的消息队列;实现业务解耦、流量削峰与异步处理
排行榜key-Sorted带权重的有序数据集合
社交网络点赞、共同好友等功能, key-Set、key-Hash等数据集合
最新列表key-List数据结构提供了【正向、反向查找】
计数器商品浏览量+1等,保证时效性,incr命令操作内存+1;

三、如何保证缓存与数据库的一致性?

方案潜在问题解决方法
先删除缓存,后更新数据库缓存可能会存储脏数据① 延时双删: 防止请求A更新数据库的过程中,请求B查询缓存,没查到,然后将A没有更新之前的数据刷入缓存;

② 在主从模式下发现更新命令,则强制请求主库查询: 先删除缓存;有可能主库更新完之后,从库还没有同步完成,此时一个请求查询从库,会读到未更新前的数据;解决方法就是:强制在主库同步的过程中,让请求查询主库;

③ 利用JVM的内存队列实现【更新与读操作的异步串行化】: 系统内部维护n个内存队列;先删除缓存,然后更新的请求入队列;当另一个查询请求过来后,读到了空缓存;那么此时先不读数据库,而是将更新缓存的请求也发送到队列中,排在数据库的更新请求后边;等到数据库与缓存的更新请求都执行结束,此时才开始读;注意:可以做读操作去重
先更新数据库,后删除缓存缓存可能会删除失败① 订阅bin log日志 + 消息队列补偿机制: 先更新数据库,redis订阅bin log日志;若缓存删除失败,则各个redis服务器拉取bin log日志,进行缓存的更新

四、缓存异常及解决方案

  • 针对所有异常的统一方案:
  • 事前设置热点key永不过期;
  • ① 第一步:先查布隆过滤器(DB中所有的key全存储在布隆过滤器中),过滤掉查询数据库根本没有的数据的请求;
  • ② 第二步:经过布隆过滤器的key允许查询缓存;在某个线程redis缓存中首次查询不到时;抢互斥锁,只允许一个线程访问数据库;
  • ③ 第二步:若数据库中有,则访问完DB将数据写入缓存,后续的其它线程直接访问缓存即可;若数据库中没有,则将(key,null)写入缓存;
问题 描述解决方案
缓存穿透查询缓存中不存在的key,不能命中缓存。导致每次都去DB中查询; ① 将不存在的key也存入缓存中,value = null; 这种方式不适用于大规模随机的key;

② 布隆过滤器: 将所有可能存在的key哈希到一个足够大的bitmap中,当一个一定不存在的key会被bitmap拦截掉,避免对数据库的直接访问;
缓存击穿大并发请求 + 某个热点key过期,在缓存中没有;导致重复去访问DB; ① 缓存失效后,使用互斥锁或队列控制访问DB的线程数量; 例如,使用Redis的setnx去设置一个互斥锁,当获取到互斥锁后,再进行数据库操作并回设缓存,否则重试get缓存的方法;

② 热点key设置永不过期: 物理不过期,逻辑过期(过期时间也存储在value中,当发现快过期时,后台启动异步线程去重置过期时间)
缓存雪崩大规模的key同时过期(redis宕机或者key设置的过期时间一样)① 互斥锁控制访问DB的数量: 保证缓存失效时,只有一个线程能够获取到锁,进而访问数据库,更新缓存;期间其它线程等待并重试;

② 分散缓存失效时间: 在原有的失效时间基础上增加一个随机值;

③ 分级缓存: 设计多级缓存,上一级缓存失效,则访问下一级缓存(每一级缓存的过期时间都不同)

④ 熔断机制: 限流降级;超过阈值的并发请求直接提示“系统拥挤”;

⑤ 主从模式 + 哨兵: 防止redis宕机导致的全面崩溃;

⑥ 开启redis持久化机制: AOF与RDB;

五、Redis事务

1、Redis事务三种实现方式

实现方式描述
事务指令Multi(开启事务)、Exec(执行命令,事务提交)
Lua脚本Redis可以保证Lua脚本内的命令一次性、按顺序的执行
基于中间标记变量通过额外的标记变量来标识事务是否执行完毕;执行请求时,首先要根据标记变量判断事务是否执行完毕

2、Redis事务的ACID都是怎么实现的?

事务redis是否支持描述
原子性(A)×Redis事务不支持事务回滚,因为redis指令执行失败只可能是语法错误,这些在开发过程中就能被发现
一致性(C)Redis事务的两种主要实现方式【multi指令与exec指令】【Lua脚本】; Redis 事务在执行的过程中,不会处理其它命令,而是等所有命令都执行完后,再处理其它命令。因此在 Redis 事务在执行过程中发生错误或进程被终结,都能保证数据的一致性
隔离性(I)Redis是单线程执行的,天然具有隔离性,不会出现脏读、幻读、不可重复读等问题;
持久性(D)Redis有【AOF、RDB】两种持久化机制

3、Redis事务执行的三个阶段

阶段描述
Step1执行 Multi命令 开启事务
Step2redis指令按顺序入队,入队成功返回Queued关键字;反之则入队失败,并被服务器所记录;
Step3执行 exec命令,检查是否有入队失败的记录来决定是否提交事务
① 若有入队失败失败,则拒绝执行并取消该事务;
② 若没有入队失败记录,则正常执行并提交事务;

执行multi命令,事务开启后,若服务端收到除了exec以外的命令,则会把请求放入队列中排队,待执行了exec命令后,才开始执行;

事务命令:

命令描述
multi开启事务
exec按顺序执行事务中的redis指令,提交事务
discard清空事务队列,并放弃执行事务;当事务中的redis指令入队失败后,在exec指令执行时发现失败记录,则调用用discard;
watch监视一个或者多个key,如果事务提交前key被改动,那么事务将会被中断;
watch命令是一种乐观锁机制,可以基于它实现与事务回滚一样的效果;
unwatch取消监视

4、Redis事务为什么不支持回滚?

   ⭐因为redis不需要回滚: 【Redis命令只会因为语法错误而失败;在Multi命令开启事务后,这些语法错误在指令会入队失败,并被redis服务器进行记录;待执行exec命令后,如果发现有入队失败的记录,则事务就会拒绝执行并取消事务;因此这也保证了我们不需要进行redis回滚;保证redis内部的简洁与高效;

    虽然Redis不支持回滚,但是可以通过Redis事务中的【watch命令】实现乐观锁,进而实现做到与事务回滚类似的的效果;

watch命令实现事务回滚的效果
① 【watch命令】可以为 Redis 事务提供CAS操作
② 我们可以使用 watch 命令来监视一个或多个 key,如果被监视的 key 在事务执行前被修改过那么本次事务将会被取消;也就实现了" 事务回滚 "
③ 只有确保被监视的 key,在事务开始前到执行的时间段内未被修改过,事务才会执行成功(类似乐观锁)
④ 如果一次事务中存在被监视的 key,无论此次事务执行成功与否,该 key 的监视都将会在执行后失效 ,也就是说监视是一次性的

六、Redis线程模型

1、Redis的IO多路复用模型

Redis执行命令请求过程

  • 当用户请求redis读写命令时,基于以下步骤:
步骤描述
Step1用户线程阻塞,向内核空间请求数据
Step2内核空间向硬件磁盘请求数据,并拷贝到内核缓冲区
Step3内核缓冲区拷贝数据到用户空间
Step4用户线程拿到了数据,开始执行redis命令请求,执行完毕将结果写入用户空间缓冲区
Step5用户缓冲区数据拷贝到内核缓冲区,然后写入硬件磁盘
  • 读数据时,要从磁盘读取数据到内核缓冲区,然后拷贝到用户缓冲区
  • 写数据时,要把用户缓冲数据拷贝到内核缓冲区,然后写入磁盘

在这里插入图片描述


1)阻塞式IO

  • 在上述磁盘、内核态与用户态的交互过程中,主要可以分为两个阶段,阻塞式IO在这两个阶段,用户线程都处于阻塞状态;

  • 那么如果每个redis请求都分配一个线程,那么所有线程在等待数据与拷贝数据的过程中,都是阻塞的,效率太差;

阶段描述用户线程
阶段一① 内核向硬件磁盘请求数据
② 内核空间数据就绪
阻塞
阶段二①从内核空间拷贝数据到用户空间
② 拷贝完成
阻塞

在这里插入图片描述


2)非阻塞式IO

  • 非阻塞式IO虽然在第一阶段不阻塞,但是CPU会一直自旋的请求数据;

  • 如果为每一个redis请求都分配一个线程,如果每个线程等待数据就绪的时间都很长,那么多个线程就会一直自旋式的向用户态请求数据,CPU使用率会暴增,但是效率却依然很差;

阶段描述用户线程
阶段一① 内核向硬件磁盘请求数据
② 期间用户线程一直自旋的向内核请求数据
③ 内核态数据准备就绪,用户线程不再自旋,等待数据拷贝
非阻塞
阶段二①从内核空间拷贝数据到用户空间
② 拷贝完成
阻塞

在这里插入图片描述


3)⭐IO多路复用

概念描述
文件描述符(File Descriptor)简称FD,是一个从0 开始的无符号整数,用来关联Linux中的一个文件。在Linux中,一切皆文件,例如常规文件、视频、硬件设备等,当然也包括网络套接字(Socket)
IO多路复用是利用单个线程来同时监听多个FD,并在某个FD可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源
  • IO多路复用使用一个线程监听多个socket的句柄(fd),当某个socket数据就绪时,就会返回一个readable事件,通知用户线程,充分利用CPU资源;避免BIO对某个socket一直阻塞以及NIO的CPU空转问题;

  • 而且用户线程不用分配多线程,一个线程就可处理多个客户端的请求;

阶段描述用户线程
阶段一① 进程调用select、poll、epoll模型来监听多个socket的FD信息② 期间任一socket的数据准备就绪,就返回readable事件
阻塞
阶段二① 用户进程找到就绪的socket
② 依次调用recvfrom读取每个就绪状态socket的数据(按照事件的顺序进行读取,不会一直卡在一个socket上)
③ 内核将数据拷贝到用户空间
非阻塞

在这里插入图片描述

IO多路复用实现方式
  • IO多路复用根据监听与通知方式的不同,有3种实现方式:
函数描述
select① 只会通知用户进程有FD就绪,但不确定具体是哪个FD,需要用户进程逐个遍历FD来确认
② 监听的fd数量有限
poll与select的区别就是监听的FD数量无限制,底层基于链表;
epoll① 在通知用户进程FD就绪的同时,把已就绪的FD写入用户空间,不用再入遍历FD查询;底层基于红黑树(redis默认)

② 事件通知函数epoll_wait有两个模式:边沿触发与水平触发;
1)边沿触发是FD就绪后,会重复通知多次用户线程,直至数据处理完成,是epoll模式的默认模式;
2)水平触发是FD就绪后,只通知1次;

4)信号驱动IO

在这里插入图片描述


5)异步IO

在这里插入图片描述


2、Redis真的是单线程的吗?

工作线程是单线程;

  • 1)Redis基于反应器reactor模式开发了一个事件处理器;因为这个文件事件处理器是单线程的,因此称Redis是单线程的;Redis线程模型基于epoll函数模型实现了【IO多路复用技术】,同时监听多个Socket,根据Socket上的事件选择对应的事件处理器来处理;我们常说的Redis单线程,实际上说的是【Redi的s工作线程是单线程】,其实在后台还有3个线程,主要用于处理耗时长的操作,分别是:
后台线程作用
close_File关闭 AOF、RDB 等过程中产生的大临时文件;
aof_fsync将追加至 AOF 文件的数据写入磁盘;
lazy_free惰性释放大对象;
  • 2)Redis6.x之前,网络IO操作与redis读写操作都是同一个工作线程中串行化的执行;为了优化网络IO模块,提高吞吐量,在redis6.x后引入了多线程模型;
  • 3)Redis6.x之后,引入的多线程主要用来处理网络IO,主线程(工作线程)单线程串行化的执行redis命令;能够充分的利用多核CPU资源,并且分摊Redis的同步IO开销;

Redis6.x之前,工作线程串行处理网络IO与redis命令(业务处理)
在这里插入图片描述

Redis6.x之后,工作线程串行处理=redis命令(业务处理),网络IO由IO线程并行处理
在这里插入图片描述


1)Redis6.x之前的线程模型

  • 客户端与Redis的通信过程

在这里插入图片描述

  • 事件处理器的模型:
    事件处理器(连接应答处理器、命令请求处理器、命令回复处理器),当一个事件处理器处理完一个事件后,IO多路复用程序才会向文件事件分派器分配下一个事件;
    在这里插入图片描述
步骤阶段描述
Step1Redis启动将【连接应答器】与【AE_READABLE】事件关联
Step2客户端请求与redis服务器建立通信连接① server socket收到连接请求,产生【AE_READABLE】事件
②【AE_READABLE】事件被IO多路复用程序监听到,进入事件队列;
③【AE_READABLE】事件被事件分配器分给【连接应答器】进行处理;
④ 连接成功,【AE_READABLE】事件与【命令请求处理器】关联;
Step3客户端发起读写请求① redis创建一个socket,产生【AE_READABLE】事件;
②【AE_READABLE】事件被IO多路复用程序监听到,进入事件队列;
③【AE_READABLE】事件被事件分配器分给【命令请求处理器】进行处理;
④ 工作线程单线程的操作redis执行命令,将执行结果存入命令回复处理器,然后将【AE_WRITEABLE】事件与【命令回复处理器】关联;
Step4客户端接收操作结果①【当socket满足可写条件】,表示客户端准备好接收数据,产生【AE_WRITEABLE】事件;
②【AE_WRITEABLE】事件被IO多路复用程序监听到,进入事件队列;
③【AE_WRITEABLE】事件被事件分配器分给【命令回复处理器】进行处理;
④ 命令回复器将操作结果写入socket,供客户端读取;
Step5清除事件与处理器的关联关系命令回复器处理完后,将【AE_WRITEABLE】事件与【命令回复处理器】的关联关系清除

2)Redis6.x之后的线程模型

  • 客户端与Redis的通信过程

在这里插入图片描述

步骤描述
Step1主线程接收连接请求,将可读Socket放入全局等待处理队列
Step2根据轮询法分配网络IO线程,与socket绑定
Step3主线程阻塞等待socket读取完成(从内核态拷贝数据到用户空间)
Step4主线程串行化的执行redis命令
Step4主线程阻塞等待IO线程将数据会写到Socket中
  • 事件处理器的模型:
  • 网络IO(内核拷贝数据到用户空间的过程 + 用户空间将结果写到内核空间缓存)的过程是并行的,redis执行命令的过程是单线程串行化的;

注意,绿色的或紫色的都是串行化执行的,先执行完绿色的;才会执行紫色的,不会有并发问题;单看哪一个都是串行的,图只能这么画;
在这里插入图片描述


3、Redis是线程安全的吗?

  • redis是单线程执行的,内部能够保证线程安全;但是外部使用的时候,业务逻辑需要我们自行保障;

七、零散面经(待更)

1、redis如何解决并发竞争key问题

方法原理
分布式锁想操作某个key,要先抢到锁之后才能操作;实现方式有redis、zookeeper、mysql等
乐观锁
watch命令
用watch命令实现乐观锁
时间戳即版本号机制,修改之后记录时间戳,若修改时时间戳早于现有的时间戳,则不操作;适用于有序场景
消息队列在并发量很大的情况下,可以通过消息队列进行串行化处理。这在高并发场景中是一种很常见的解决方案
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值