岗位题目
后端开发相关面试题第 2 页
Redis 如何删除和淘汰过期数据?
Redis 过期数据的处理分两层:先通过惰性删除和定期删除清理已经到期的 key;当内存仍然超过 maxmemory 时,再按淘汰策略驱逐候选数据。面试回答要区分“过期删除”和“内存淘汰”,再说明为什么 Redis 采用抽样和渐进式清理。
Redis 内存满了会发生什么?
Redis 内存满了之后的行为取决于 maxmemory 和 maxmemory-policy:可能触发淘汰并继续写入,也可能拒绝会增加内存的写命令。回答时要说明触发时机、策略分类、近似算法以及对业务的影响。
Redis 为什么速度快?
Redis 快的原因不是单点魔法,而是内存访问、高效数据结构、单线程事件循环、I/O 多路复用、协议简单和工程优化共同作用。回答时要避免只说“因为单线程”,还要说明单线程为什么反而减少了锁竞争。
Redis ZSet 底层如何实现?
ZSet 的底层实现要抓住两点:它同时需要按 member 快速查分数,又需要按 score 有序范围查询。因此典型实现是字典加跳表,小规模时使用紧凑编码以节省内存。
聚簇索引和非聚簇索引有什么区别?
聚簇索引和非聚簇索引的核心区别在于索引叶子节点是否直接保存完整数据行。聚簇索引决定数据行的组织方式,非聚簇索引是额外的查找结构,通常还需要再定位到真实数据。
数据库索引为什么能加速查询?
数据库索引能加速查询,是因为它用有序的数据结构把全表扫描变成定向查找,减少需要读取、比较和回表的数据量,并且可以利用有序性优化范围查询、排序和覆盖查询。
MVCC 的实现原理是什么?
MVCC 是多版本并发控制,通过为数据保留多个历史版本,让读操作基于一致性快照判断可见版本,从而减少读写互相阻塞。常见实现依赖隐藏版本字段、undo log、ReadView 和可见性规则。
MySQL 索引失效有哪些常见场景?
MySQL 索引失效通常不是索引真的损坏,而是查询写法、类型转换、联合索引顺序、模糊匹配、否定条件或优化器成本判断导致索引无法高效使用,甚至被执行计划放弃。
MySQL 查询为什么可能不走索引?
MySQL 不走索引通常不是“索引失效”这么简单,而是优化器认为使用索引的总代价不划算,或者查询条件的写法让索引无法被有效利用。面试中要从索引可用性、选择性、代价模型、联合索引规则和执行计划验证几层回答。
MySQL B+ 树索引的实现
这题考察 InnoDB 如何用页式 B+ 树降低磁盘 I/O,并把等值查询、范围扫描、聚簇索引、二级索引和回表串成一条完整链路。
同题还出现在 2 个公司岗位
Java 双亲委派机制是什么,目的是什么?
双亲委派机制是 Java 类加载器的一种委派模型:一个类加载请求先交给父加载器尝试加载,父加载器加载不了时,子加载器才自己加载。它的目的主要是保证核心类库安全、避免类重复加载,并维持类身份的一致性。
Java 常见集合有哪些,各自适合什么场景?
Java 常见集合可以按接口体系理解:List 适合有序可重复数据,Set 适合去重,Map 适合键值映射,Queue 和 Deque 适合队列、栈和任务调度。回答时要结合底层结构、复杂度、是否有序、是否线程安全和典型场景。
自定义对象的 hashCode 应该如何计算?
自定义对象的 hashCode 应该基于 equals 使用的关键字段来计算,核心目标是满足“相等对象必须有相同哈希值”,同时尽量让不同对象分布均匀、计算稳定,并避免使用会频繁变化的字段。
为什么 equals 和 hashCode 要保持一致?
equals 和 hashCode 必须保持一致,是因为哈希容器依赖 hashCode 定位桶,再依赖 equals 判断对象是否相等。如果两个相等对象的 hashCode 不同,它们会被放到不同查找路径上,导致重复存储、查找失败和删除失败。
Java Object 类有哪些常见方法?
Java Object 类是所有类的根类,常见方法可以分为对象身份相关、运行时类型相关、字符串表示、克隆、线程协作和对象生命周期几类。面试回答要说明方法用途,也要点出 equals/hashCode、wait/notify、clone、finalize 的关键边界。
ReentrantLock 如何基于 AQS 实现可重入锁?
ReentrantLock 的核心不是自己从零管理阻塞队列,而是把获取锁、释放锁、排队、唤醒、可中断等待、条件队列等通用同步能力交给 AQS。它用 AQS 的 state 表示重入次数,用独占线程记录锁持有者,通过公平或非公平策略决定是否允许新线程插队。
AQS 是什么?
AQS 是 Java 并发包中的基础同步框架,用一个 volatile state 表示同步状态,用一个 FIFO 等待队列管理竞争失败的线程,并通过模板方法把通用排队唤醒流程和具体同步语义分离。
Java 线程池有哪些核心参数?
Java 线程池的核心参数来自 ThreadPoolExecutor:corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、RejectedExecutionHandler。真正要掌握的是它们如何共同决定线程创建、任务排队、扩容、回收和拒绝策略。
synchronized 和 ReentrantLock 有什么区别?
synchronized 是 JVM 层面的内置锁,语法简单,自动加锁释放;ReentrantLock 是 JUC 提供的显式锁,基于 AQS,功能更丰富。两者都可重入,都能保证互斥和可见性,但在使用方式、等待能力、公平性、条件队列和可中断性上差异明显。
RabbitMQ 如何实现消费者负载均衡?
RabbitMQ 的消费者负载均衡主要依赖同一队列上的多个竞争消费者。队列把每条消息投递给其中一个消费者,再通过 manual ack 和 prefetch 控制未确认消息数量,让处理更快的消费者获得更多后续消息。
RabbitMQ 仲裁队列的消费组如何工作?
RabbitMQ 仲裁队列是基于 Raft 的复制队列,解决的是队列高可用和数据一致性问题;它本身不是 Kafka 式消费组。消费者仍然通过订阅同一个队列竞争消费,消息由当前 leader 负责投递和确认推进。
RabbitMQ 延时队列通常如何实现?
RabbitMQ 延时队列常见实现有两类:使用 TTL 加死信交换机间接延迟,或使用 delayed message exchange 插件直接按延迟投递。前者通用但有顺序阻塞等细节,后者语义更直观但依赖插件。
Kafka 如何保证消息顺序性?
Kafka 的顺序性建立在分区日志之上:单个分区内消息按追加顺序存储和消费;跨分区不保证全局顺序。要保证同一业务维度有序,关键是把相同业务键路由到同一分区,并控制生产、消费和重试策略。
Redis 缓存雪崩、击穿和穿透分别是什么,如何解决?
缓存雪崩、击穿和穿透都是缓存失效后流量打到后端的风险,但触发条件不同:雪崩是大面积不可用,击穿是热点 key 瞬时失效,穿透是请求的数据本来就不存在。回答时要先分类,再分别给出限流、降级、互斥重建、空值缓存、布隆过滤器和 TTL 随机化等治理手段。
Cursor 上下文过长时为什么可能出现效果下降?
Cursor 上下文过长时效果下降,通常不是因为模型突然失效,而是有效注意力、检索质量、指令优先级和噪声比例同时变差。上下文窗口越大,越需要选择性提供信息,否则相关代码被无关内容稀释,模型更容易漏掉关键约束或沿用错误线索。
大模型输出不符合指令时如何处理?
大模型输出不符合指令时,应该先判断是指令不清、上下文冲突、能力不足、格式约束不强、还是后处理缺失,再选择提示词修正、结构化约束、示例引导、检索补充、模型切换、自动校验、重试修复或人工介入。成熟做法是把不合规输出当作工程质量问题,而不是只责怪模型。
CLOSE_WAIT 和 TIME_WAIT 分别是什么?
CLOSE_WAIT 和 TIME_WAIT 都是 TCP 连接关闭过程中的状态,但含义完全不同。CLOSE_WAIT 出现在被动关闭方,表示对端已经发来 FIN,本端已经确认,但应用还没有调用 close 结束连接;TIME_WAIT 出现在主动关闭方,表示本端完成主动关闭后仍保留连接一段时间,用于处理延迟报文和保证对端收到最后的 ACK。
Prompt ETL 管道如何设计?
Prompt ETL 管道是把原始业务输入经过清洗、切分、增强、模型处理、结构化解析、校验和落库的工程化流程。它的目标不是单次调用模型,而是稳定、可追踪、可回放地把非结构化或半结构化信息转成可用数据。
HTTP 和 HTTPS 有什么区别?
HTTP 和 HTTPS 的核心区别在于 HTTPS 是运行在 TLS 之上的 HTTP。HTTP 明文传输,默认不提供身份认证、加密和完整性保护;HTTPS 通过证书、密钥协商、对称加密和完整性校验,提供防窃听、防篡改和服务器身份认证能力。
HTTP 502 状态码表示什么?
HTTP 502 Bad Gateway 表示网关或代理服务器从上游服务器收到了无效响应。它通常不是客户端请求格式本身的错误,而是代理、负载均衡、应用服务、网络链路或协议转换之间出现问题。
TCP 为什么需要四次挥手?
TCP 需要四次挥手,是因为 TCP 是全双工协议,两个方向的数据流要分别关闭。主动关闭方发送 FIN 只表示自己不再发送数据,对方收到后先 ACK 确认,但对方可能还有未发送完的数据,等它也发送完后再发 FIN,主动关闭方再 ACK,因此通常是四个报文段。
TCP 如何进行拥塞控制?
TCP 拥塞控制通过动态调整拥塞窗口 cwnd 来控制网络中未确认数据量,目标是在尽量提高吞吐的同时避免把网络打爆。经典机制包括慢启动、拥塞避免、快速重传和快速恢复,现代实现还会结合 RTT、丢包、带宽估计和不同拥塞控制算法优化。
netty有哪些组件?
Netty 的组件可以按启动配置、连接抽象、线程模型、事件流转、数据缓冲、编解码、业务处理、异步通知来理解。回答时不应只罗列类名,而要把 ServerBootstrap 启动服务端、EventLoopGroup 分配线程、Channel 承载连接、Pipeline 串联 Handler、ByteBuf 承载数据、Future 返回异步结果这条链路讲清楚。
常见 JVM 垃圾收集器分别适合什么场景?
垃圾回收器适用场景不能只背名称,而要围绕吞吐量、停顿时间、堆大小、对象生命周期、CPU 余量和运行时版本来选择。Serial 适合小堆和低资源环境,Parallel 适合吞吐优先任务,CMS 是旧版本低停顿方案但有碎片和并发失败风险,G1 是较新主流服务端的平衡型选择,ZGC 与 Shenandoah 面向更严苛的低停顿和大堆场景,Epsilon 只适合测试、压测和短生命周期实验。
什么情况服务器会有大量的TIME_WAIT连接?
服务器出现大量 TIME_WAIT,本质上说明这台机器在大量 TCP 连接中充当了主动关闭方,并且连接关闭后需要保留 2MSL 时间以保证最后一个 ACK 可重传、旧报文不会污染后续连接。常见诱因包括短连接比例高、高并发请求快速结束、服务端主动断开 HTTP 连接、未开启或未复用 keep-alive、反向代理到后端使用短连接、客户端或上游连接池配置不合理,以及瞬时流量尖峰。大量 TIME_WAIT 本身不一定是故障,但如果伴随本地端口耗尽、连接建立失败、CPU 软中断升高或 accept/connect 异常,就需要结合业务关闭方向、连接复用、系统参数和网络路径做定位。
进程通信方式分别适合什么场景?
这道题考察的不是背诵 IPC 名词,而是能否根据进程关系、通信方向、数据量、时延、可靠性、同步需求和部署范围选择合适方案。回答时应先按场景分类:父子进程简单字节流用管道,本机无亲缘进程可用 FIFO、消息队列或本地 Socket,高吞吐大块数据用共享内存配合同步原语,事件通知用信号,跨主机通信用网络 Socket,文件或 mmap 适合持久化、低频共享和大文件映射。
JVM 堆内存通常如何分区?
JVM 堆可以从两条线理解:按对象生命周期分为年轻代和老年代,年轻代内部又分 Eden 和两个 Survivor;按具体垃圾收集器实现,传统分代收集器更强调连续代空间,G1、ZGC、Shenandoah 等更强调 region 或分区化管理。回答时要特别说明:元空间和线程栈不属于堆,TLAB 是 Eden 中给线程预分配的私有分配缓冲区,大对象可能直接进入老年代或被特殊 region 管理。
TopK,如果k很小,n很大,用什么方法?
当 k 很小、n 很大时,TopK 的标准精确解通常是维护大小为 k 的堆,而不是全排序。求最大 TopK 用小顶堆,求最小 TopK 用大顶堆,复杂度 O(n log k)、空间 O(k)。如果数据可放入内存且允许原地修改,可以补充 quickselect;如果值域小、数据海量或允许近似,还要分别考虑计数桶、分片合并和近似频率算法。
堆上的元素满了会发生什么?
JVM 堆满通常指堆内存中可用于对象分配的空间不足。对象分配失败后,虚拟机会先尝试触发 Young GC 或 Full GC 回收空间;如果回收后仍无法满足分配,或者老年代晋升失败、连续 GC 效率过低,就可能抛出 OutOfMemoryError。回答时要围绕对象分配、GC 尝试、晋升失败、OOM 类型、排查与预防展开,不要误讲成优先队列里的堆元素满了。
用zk做注册中心,那zk数据结构是怎样的?
ZooKeeper 做注册中心时,核心数据结构不是表或哈希,而是一个层级命名空间的 znode 树。每个服务、分组、版本和实例都可以映射成路径节点,服务实例通常用临时节点或临时顺序节点表示。客户端通过 watch 订阅子节点变化,实例会话失效后节点自动删除,从而实现服务发现与上下线通知。
数组和链表有什么区别?
数组和链表都是线性表,但底层组织方式不同:数组用连续内存存放元素,链表用离散节点通过引用连接。这个差异决定了数组随机访问快、缓存友好,但插入删除和扩容成本可能高;链表插入删除在已定位节点时很快,但查找慢、缓存局部性差、额外指针开销大。工程上不能只背复杂度,要说明复杂度成立的前提,并结合 Java 的 ArrayList 和 LinkedList 对照。
同题还出现在 1 个公司岗位
红黑树和平衡二叉树的区别?
红黑树和平衡二叉树都是为了避免普通二叉搜索树退化成链表而设计的自平衡二叉搜索树,核心区别在于平衡标准和维护成本。面试中说的平衡二叉树通常特指 AVL 树:它要求每个节点左右子树高度差最多为 1,平衡非常严格,所以查询路径更短;红黑树用颜色规则约束黑色节点数量和红色节点相邻关系,允许一定程度的不完全平衡,因此插入、删除时旋转和调整更少,更适合频繁更新的工程场景。
cpp的虚函数以及实现原理?
虚函数是 C++ 实现运行时多态的核心机制。它允许通过基类指针或引用调用派生类重写后的函数,把调用哪个函数的决定从编译期推迟到运行期。主流编译器通常通过虚函数表和对象中的虚表指针实现:多态类有虚表,对象保存指向虚表的指针,虚调用时先取虚表指针,再按固定槽位找到函数地址并间接调用。
C++多态怎么实现?
C++ 多态本质上是同一接口在不同类型上表现出不同行为。它分为编译期多态和运行期多态:编译期多态由函数重载、运算符重载、模板等在编译阶段完成选择;运行期多态依赖 virtual 虚函数、继承、重写以及基类指针或引用,在程序运行时根据对象真实类型进行动态绑定。回答不能只停留在虚函数表四个字,还要讲清楚触发条件、对象内存模型、构造析构规则、虚析构必要性、对象切片和多继承下的边界问题。
Redis的List底层实现原理?
Redis List 的底层实现经历过从 ziplist 与 linkedlist 的组合,到 quicklist 统一承载的演进。核心目标不是单纯追求某一种操作最快,而是在两端插入删除、内存占用、缓存友好性和中间位置修改之间做平衡。回答时要讲清:List 是有序、可重复、按插入顺序组织的线性结构,适合队列、栈、简单消息流等场景,但索引访问和中间元素定位不是它的强项。
为什么要用redis?
Redis 的核心价值不是更快的数据库,而是把高频、低延迟、可短期容忍一致性差异的数据放到内存侧处理,从而缩短读写链路、削峰填谷、降低关系型数据库压力,并利用丰富数据结构、原子命令、过期机制和高可用能力支撑缓存、计数、排行榜、限流、会话、队列等典型场景。
Redis 缓存淘汰策略有哪些?
这题要先区分两个概念:过期删除是针对设置了 TTL 的键,缓存淘汰是 Redis 在内存达到 maxmemory 限制后,为了腾出空间而选择删除哪些键。回答时不能只背策略名称,还要说明 allkeys 与 volatile 的区别、LRU/LFU/TTL/random/noeviction 的选择逻辑,以及这些策略在读多写多、冷热数据明显、TTL 管理严格等场景下的取舍。
Redis 为什么高可用?
Redis 的高可用不是单一能力,而是复制、故障检测、自动切换、分片容灾、持久化、客户端重连和监控治理共同组成的体系。主从复制提升读扩展和副本冗余,哨兵负责发现主节点故障并自动完成主从切换,Cluster 通过分片和多主多从避免单机容量与单点问题,AOF/RDB 持久化降低进程或机器故障后的数据损失。真正回答这道题时,要同时说明 Redis 能恢复服务、能减少数据丢失,但不能承诺在所有极端场景下零丢失,尤其要讲清复制延迟、脑裂和一致性取舍。