redis精选面试题2022(持续更新)

2023-11-20

目录

一、redis是单线程还是多线程

二、Redis 为什么用单线程 

三、redis是单线程为什么还这么快

1、内存数据库

2、简单的数据结构

3、单线程

4、IO多路复用

四、redis是如何使用跳表来存储的

 五、redis没有设置过期时间,为什么redis自动删除了

六、删除redis的key会阻塞redis么,如何删除一个大key


一、redis是单线程还是多线程

通常我们看到redis介绍的时候都会说redis是单线程的,但是当我们深入学习的时候,我们使用一些命令对redis进行持久化操作的时候,比如bgsave命令,它的作用就是在后台异步启动一个线程保存当前数据库的数据到磁盘。

其实通常说的redis是单线程指的是redis在对外提供键值对存储服务的主要流程,即网路IO和键值对读写是由单个线程来完成的。除此外redis的其他功能,比如:redis的持久化、异步删除、集群数据同步等,是由额外的线程来完成,因此redis严格意义上并不是完全的单线程。

多线程是 Redis6.0 推出的一个新特性。正如上面所说 Redis 是核心线程负责网络 IO ,命令处理以及写数据到缓冲,而随着网络硬件的性能提升,单个主线程处理络请求的速度跟不上底层络硬件的速度,导致网络 IO 的处理成为了 Redis 的性能瓶颈。

而 Redis6.0 就是从单线程处理网络请求到多线程处理,通过多个 IO 线程并处理网络操作提升实例的整体处理性能。需要注意的是对于读写命令,Redis 仍然使单线程来处理,这是因为继续使单线程执行命令操作,就不为了保证 Lua 脚本、事务的原性,额外开发多线程互斥机制了。

需要注意的是在 Redis6.0 中,多线程机制默认是关闭的,需要在 redis.conf 中完成以下两个设置才能启用多线程。

设置 io-thread-do-reads 配置项为 yes,表示启用多线程。

设置线程个数。一般来说,线程个数要小于 Redis 实例所在机器的 CPU 核数, 例如,对于个 8 核的机器来说,Redis 官建议配置 6 个 IO 线程。

io-threads-do-reads yes
io-threads 6

二、Redis 为什么用单线程 

Redis 为什么用单线程?在回答这个问题前,先来看大家都很熟悉的数据库 MySQL,它使用的就是多线程。MySQL 不会每有一个连接就创建一个线程,因为线程过多会带来额外的开销,其中包括创建销毁线程的开销、调度线程的开销等,同时也会降低计算机的整体性能。这个正是多线程会遇到的难点。

此外多线程系统中通常会存在被多线程同时访问的共享资源,比如一个共享的数据结构,当有多个进程要修改这个共享资源时,为了保证共享资源的正确性,就需要有额外的机制进行保证,而这个额外的机制,也会带来额外的开销。还是以 MySQL 举例,MySQL 引入了锁机制来解决这个问题。

从上面不难看出,多线程开发中并发访问控制是个难点,需要精细的设计才能处理。如果只是简单地处理,比如简单地采个粗粒度互斥锁,只会出现不理想的结果。即便增加了线程,系统吞吐率也不会随着线程的增加而增加,因为大部分线程还在等待获取访问共享资源的互斥锁。而且,大部分采用多线程开发引入的同步原语保护共享资源的并发访问,也会降低系统代码的易调试性和可维护性。

而正是以上这些问题,才让 Redis 采了单线程模式。

三、redis是单线程为什么还这么快

  • 内存型数据库
  • 简单的数据结果
  • 单线程
  • IO多路复用

1、内存数据库

redis完全是基于内存的,绝大部分请求是纯粹的内存操作,所以非常快速。

2、简单的数据结构

redis目前支持5种数据类型(string、list、hash、set、zset),数据结构相对简单,操作起来也相对快速。

对于string来说,redis采用SDS方式来组织数据

redis的有序集合,采用的跳跃表的数据结构,通过层来加快访问其他节点

3、单线程

redis采用单线程模型,单线程的好处在于避免了多线程对数据竞争的问题、加锁的问题、上下文切换的问题。

4、IO多路复用

redis采用了非阻塞的IO多路复用技术。redis本身就是一个事件驱动程序,redis把socket抽象成文件事件。这里说的IO多路复用就是文件事件处理器以单线程的方式,来监听相关的套接字(accept、read、write、close)。

由于IO多路复用程序是一个单线程,那么当多个socket到来时,肯定要排队,它们总是以队列的方式顺序地处理。

C10K问题

在没有IO多路复用的时候,假设现在有10000个客户端连接(fd1-10000),但是只有1个客户端有发数据,然而计算机并不知道哪个fd有数据,只能遍历10000次,每次都要陷入内核,开销比较大,而且实际上9999次都是浪费的。

IO多路复用

IO多路复用的意思就是多个网路IO即为多个TCP连接 复用一个进程或者线程,这种模型最大的好处就是不用为每个连接创建一个进程或者线程。比较经典的模型就是 select、poll、epoll。

  • select:select(fds),一次性把fds交给内核,然后内核告诉哪些fd可读可写(内核自己遍历,而不用用户遍历,将多次的系统调用变成1次系统调用)。fds最大是1024,这也决定了select模型最大并发是1024。
  • poll:和select差不多,只不过并发不止1024了,可以更多
  • epoll: select和poll的缺点是内核遍历的时间复杂度是O(n),虽然用户态不用遍历了,减少了陷入内核的次数,但是内核还是要遍历的。epoll的优点就是内核也不需要遍历了,当用户把fds传给内核时,然后依赖硬件中断,比如当网卡有数据到来时,就会中断告诉cpu,cpu就知道哪个fd有数据到达了。

redis默认采用epoll,除非系统不支持。

四、redis是如何使用跳表来存储的

redis的有序集合zset就使用到了跳表这个概念。

跳表:跳表是一个随机化的数据结构,实质就是一种可以进行二分查找的有序链表。跳表在原有的有序链表上面增加了多级索引,通过索引来实现快速查找。跳表不仅能提高搜索性能,同时也可以提高插入和删除操作的性能。

跳表图例:出自文章https://news.ycombinator.com/item?id=1171423

  • 单链表:查询时间复杂度O(n)
  • level-2单链表:每隔一个节点为一个level-2节点,每个level-2节点有2个后继指针,分别指向单链表中的下一个节点和下一个level-2节点。查询时间复杂度为O(n/2)
  • level-3单链表:每隔一个节点为一个level-2节点,每隔4个节点为一个level-3节点,查询时间复杂度O(n/4)
  • 指数式单链表:每2^i个节点的level为i+1,查询时间复杂度为O(log2N)
  • 跳跃表:各个level的节点个数同指数式单链表,但出现的位置随机,查询复杂度是O(logN)

采用跳表不采用红黑树的原因:

可以看到redis选择跳跃表而非红黑树作为有序集合实现方式的原因并非是基于并发上的考虑,因为redis是单线程的,选用跳跃表的原因仅仅是因为跳跃表的实现相较于红黑树更加简洁。

 五、redis没有设置过期时间,为什么redis自动删除了

原因:当redis的已用内存大于maxMemory的时候,触发主动清理策略。

主动清理策略在Redis4.0之前有6种,在4.0之后又8种

  • 针对设置了过期时间的key做处理
  1. volatile-ttl:在筛选时,会针对设置的过期时间的键值对,根据过期时间的先后顺序进行删除,越早过期的越先删除。
  2. volatile-random:在设置了过期时间的key中,进行随机删除。
  3. volatile-lru:会使用LRU算法进行删除 LRU(Least Recently Used) 就是最少使用的key中,时间维度
  4. volatile-lfu:会使用LFU算法进行删除 LFU(Least Frequently Used,最不经常使用),通过使用频率这个维度来删除
  • 针对所有key做处理
  1. allkeys-ttl:不管key是否设置了过期,淘汰即将过期的key
  2. allkeys-random:不管key是否设置了过期,随机淘汰
  3. allkeys-lru:不管key是否设置了过期,淘汰最近最少访问的key
  • 不处理
  1. noeviction:不淘汰任何key,满容后再写入直接报错。

六、删除redis的key会阻塞redis么,如何删除一个大key

关于redis大健,我们主要从来个维度来说

  1. 时间复杂度 一个包含1000W个数据的list 我们通过删除这个list会阻塞redis十几秒甚至几十秒。
  2. 空间复杂度 比如一个string 内存空间占用100MB 。

因为内存空间复杂性处理耗时都非常小,测试 del 100MB String键耗时约0.5毫秒,而删除一个含有1000w个数据的List,却会阻塞Redis进程数十秒。

说明:Redis是单线程处理。执行一个耗时过大的命令,导致阻塞其他命令,容易引起应用程序雪崩或Redis集群发生故障切换。所以避免在生产环境中使用耗时过大命令是非常关键的。

那如果线上环境需要处理大key的删除,我们应该怎么处理呢?

使用分批删除的思想

  • hash key:通过hscan命令,每次获取500个字段,再用hdel命令;
  • set key:使用sscan命令,每次扫描集合中500个元素,再用srem命令每次删除一个元素;
  • list key:删除大的List键,未使用scan命令; 通过ltrim命令每次删除少量元素。
  • sorted set key:删除大的有序集合键,和List类似,使用sortedset自带的zremrangebyrank命令,每次删除top 100个元素。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

redis精选面试题2022(持续更新) 的相关文章

  • 使用 sidekiq 处理两个独立的 Redis 实例?

    下午好 我有两个独立但相关的应用程序 他们都应该有自己的后台队列 阅读 单独的 Sidekiq 和 Redis 进程 然而 我希望偶尔能够将工作推给app2的队列来自app1 从简单的队列 推送的角度来看 如果app1没有现有的 Sidek
  • 如何清理redis中不活跃的玩家?

    我正在制作一个使用 redis 来存储游戏状态的游戏 它可以很好地跟踪位置和玩家 但我没有一个好的方法来清理不活跃的玩家 每当玩家移动时 这是一个半慢速移动游戏 想想每秒 1 5 帧 我就会用新位置更新哈希并删除旧位置键 跟踪活跃玩家的最佳
  • Spring RedisTemplate:8次调用后方法键挂起

    我使用 Spring RedisTemplate spring data redis 1 7 1 与 Redis 进行通信 我需要通过正则表达式获取然后删除键 例如 context user1 我用的方法 RedisTemplate key
  • Node.js 上通过套接字连接 Redis

    由于共享托管 目标主机上的我的 redis 服务器不在端口上运行 而是在非常特定的套接字上运行 可以通过套接字文件连接到该套接字 只有我的用户可以访问 但是 我还没有找到如何通过套接字指定连接node redis and connect r
  • 仅当尚未设置时才进行原子设置

    仅当尚未在 Redis 中设置时 是否有办法执行原子设置 具体来说 我正在创建一个像 myapp user user email 这样的用户 并且希望 Redis 在 user email 已被占用时返回错误 而不是默默地替换旧值 比如声明
  • PooledRedisClientManager 未释放连接

    我将 json 数据列表存储在 redis 中并使用 ServiceStack c 客户端访问它 我本质上是在管理自己的外键 我在其中存储zrangeid 我使用应用程序内部的接口从zrange然后从 Redis 获取底层 json 对象并
  • 如何在节点redis客户端上设置读取超时?

    在 github 上我没有看到读取超时的选项 https github com NodeRedis node redis https github com NodeRedis node redis There s connect timeo
  • 如何统计 Redis 流中未读或已确认的消息?

    使用 Redis 5 0 3 假设我们创建一个名为streamy和一个消费群体consumers XGROUP CREATE streamy consumers MKSTREAM 然后向其中添加一些消息 XADD streamy messa
  • 库存管理系统的 SQL 与 NoSQL

    我正在开发一个基于 JAVA 的网络应用程序 主要目的是拥有在多个称为渠道的网站上销售的产品的库存 我们将担任所有这些渠道的管理者 我们需要的是 用于管理每个渠道的库存更新的队列 库存表 其中包含每个通道上分配的正确快照 将会话 ID 和其
  • 是否有可嵌入的 Java 替代 Redis?

    根据这个线程 https stackoverflow com questions 3047010 best redis library for java 如果我想从Java中使用Redis Jedis是最好的选择 然而 我想知道是否有任何库
  • Redis hash写入速度非常慢

    我面临一个非常奇怪的问题 使用 Redis 时 我的写入速度非常糟糕 在理想的情况下 写入速度应该接近 RAM 上的写入速度 这是我的基准 package redisbenchmark import redis clients jedis
  • 如果另一个键中的计数器低于零,则从集合中原子删除一个项目?

    雷迪斯2 0 3 在我的 Redis DB 中 我有一组项目 每个项目都有一个与其关联的计数器 MULTI SADD items set foo INCRBY items foo 10000 EXEC 新项目会以随机间隔添加到集合中 当用户
  • redis-cli 重定向到 127.0.0.1

    我在PC1上启动Redis集群 然后在PC2上连接它 当需要重定向到另一个集群节点时 它会显示Redirected to slot 7785 located at 127 0 0 1 但应该显示Redirected to slot 7785
  • Caffeine Expiry 中如何设置多个过期标准?

    我正在使用 Caffeine v2 8 5 我想创建一个具有可变到期时间的缓存 基于 值的创建 更新以及 该值的最后一次访问 读取 无论先发生什么都应该触发该条目的删除 缓存将成为三层值解析的一部分 The key is present i
  • 如何设置和获取Redis中存储的对象?

    我试图在 redis 中存储一个对象 当我获取该对象时 它似乎不起作用 I tried u User new u name blankman redis set test u x redis get test x name error 我想
  • 为什么 Redis TimeSeries 不捕获聚合中的最后一个元素?

    我试图了解 Redis 的时间序列规则创建的工作原理 但我很困惑为什么 Redis 会忽略聚合中的最后一项 并想知道这是否是预期的行为 我在中创建了示例代码redis cli为了显示 127 0 0 1 6379 gt FLUSHALL O
  • redis - 使用哈希

    我正在使用 redis 为我的 Web 应用程序实现社交流和通知系统 我是 redis 的新手 我对哈希值及其效率有一些疑问 我读过这篇很棒的文章Instagram 帖子 http instagram engineering tumblr
  • Redis+Docker+Django - 错误 111 连接被拒绝

    我正在尝试使用 Redis 作为使用 Docker Compose 的 Django 项目的 Celery 代理 我无法弄清楚我到底做错了什么 但尽管控制台日志消息告诉我 Redis 正在运行并接受连接 事实上 当我这样做时 docker
  • 如何将“.csv”数据文件导入Redis数据库

    如何将 csv 数据文件导入 Redis 数据库 csv 文件中包含 id 时间 纬度 经度 列 您能否向我建议导入 CSV 文件并能够执行空间查询的最佳方法 这是一个非常广泛的问题 因为我们不知道您想要什么数据结构 您期望什么查询等等 为
  • 如何使用 Redis 自动删除与模式匹配的键

    在我的 Redis DB 中 我有很多prefix

随机推荐