全方位)
Java 后端开发面试题整理从集合、并发到 Doris 与 Kafka本文整理了一套面向 Java 后端开发工程师的常见面试题覆盖 Java 集合、多线程、JVM、Spring、MyBatis、数据库、Doris、Kafka、Redis、Kubernetes 和计算机网络等核心知识点。内容更偏向面试口述表达不仅说明“是什么”也强调“为什么”和“实际项目中怎么用”。文章目录Java 后端开发面试题整理从集合、并发到 Doris 与 Kafka阅读建议集合1. HashMap 底层结构是什么2. HashMap 为什么线程不安全3. ConcurrentHashMap 怎么保证线程安全4. ArrayList 和 LinkedList 区别5. HashMap 扩容机制6. 为什么 HashMap 容量是 2 的幂多线程1. synchronized 和 ReentrantLock 区别2. volatile 保证什么不能保证什么3. 线程池核心参数有哪些4. 线程池拒绝策略有哪些5. 生产环境线程池怎么配置6. CountDownLatch、Semaphore、CyclicBarrier 区别JVM1. JVM 内存区域有哪些2. 堆和栈区别3. 对象什么时候进入老年代4. 常见 GC 算法5. Full GC 频繁怎么排查6. OOM 怎么排查Spring1. Spring Bean 生命周期2. IOC 和 AOP 原理3. Spring 事务失效场景4. Transactional 为什么有时不生效5. Spring Boot 自动装配原理6. Feign 调用超时怎么排查7. 拦截器、过滤器、AOP 区别MyBatis / MyBatis-Plus1. #{} 和 ${} 区别2. MyBatis 一级缓存、二级缓存3. 动态 SQL 怎么防注入4. 分页插件原理5. MyBatis-Plus Wrapper 优缺点6. 批量插入怎么优化高频重点题Transactional 什么时候会失效MySQL / PostgreSQL1. BTree 索引原理2. 联合索引最左前缀原则3. 索引失效场景4. explain 看哪些字段5. 慢 SQL 怎么排查6. count(*)、count(1)、count(column) 区别7. left join 条件写在 on 和 where 的区别8. MySQL 和 PostgreSQL 在统计查询上的差异Doris1. Doris 适合什么场景2. Doris FE 和 BE 分别做什么3. Doris 分区和分桶怎么设计4. Duplicate Key、Aggregate Key、Unique Key 区别5. Routine Load 原理6. Doris 查询慢怎么排查7. 小文件/小批量导入过多有什么问题8. Doris 和 MySQL 的区别SQL 场景题统计查询突然很慢你怎么排查Kafka1. Kafka 为什么吞吐高2. Topic、Partition、Consumer Group 是什么3. 如何保证消息不丢4. 如何保证消息不重复消费5. 如何保证顺序消费6. 消息积压怎么处理7. offset 提交方式8. Kafka 和 RocketMQ 区别Redis1. Redis 常用数据结构2. 缓存穿透、击穿、雪崩3. Redis 分布式锁怎么实现4. Redis 持久化 RDB/AOF 区别5. Redis 为什么快6. 热 key 怎么处理Kubernetes1. Pod、Deployment、Service 区别2. K8s 如何做滚动发布3. 服务挂了怎么排查4. 怎么看日志5. livenessProbe 和 readinessProbe 区别6. ConfigMap 和 Secret 区别网络1. HTTP 缓存机制是什么2. 强缓存和协商缓存有什么区别强缓存协商缓存3. Last-Modified 和 ETag 有什么区别4. HTTP 缓存的执行顺序是什么总结阅读建议第一轮先掌握每道题的核心结论。第二轮补充底层原理、适用场景和常见陷阱。第三轮结合自己的项目经历组织口述答案。面试时避免只背概念尽量使用“原理 场景 排查思路”的结构作答。重点建议优先准备 HashMap、ConcurrentHashMap、线程池、JVM 内存区域、OOM、GC 日志、Spring 事务、索引、慢 SQL、Doris 查询优化和 Kafka 消息可靠性。集合1. HashMap 底层结构是什么HashMap在 JDK 8 及之后版本底层是数组 链表 红黑树。数组中的每个位置称为一个桶桶里存放的是 Node 节点。当发生哈希冲突时多个元素会挂到同一个桶下最开始是链表结构。当单个桶中的链表长度达到 8并且数组长度大于等于 64 时链表会转换成红黑树用来提升查询效率。如果数组长度还小于 64一般不会直接树化而是优先扩容因为这时冲突可能是容量太小导致的。所以HashMap的查询平均时间复杂度接近 O(1)但在冲突严重时链表是 O(n)红黑树可以优化到 O(log n)。2. HashMap 为什么线程不安全HashMap线程不安全主要是因为它内部没有加锁也没有保证并发修改时的原子性和可见性。比如多个线程同时 put 时可能同时计算桶位置、同时修改链表或红黑树结构导致数据覆盖、数据丢失。另外扩容时也比较危险因为扩容会创建新数组然后迁移旧数据。如果多个线程同时触发扩容可能导致数据迁移异常。所以在并发场景下一般不能直接使用HashMap可以使用ConcurrentHashMap。3. ConcurrentHashMap 怎么保证线程安全ConcurrentHashMap在 JDK 1.7 和 1.8 的实现不太一样。JDK 1.7 主要是 Segment 分段锁每个 Segment 继承ReentrantLock不同 Segment 之间可以并发操作减少锁竞争。JDK 1.8 取消了 Segment底层变成数组 链表 红黑树主要通过 CAS synchronized来保证线程安全。插入空桶时使用 CAS通过比较内存中的当前值与预期值决定是否更新发生哈希冲突时会对桶的头节点加synchronized锁。这样锁的粒度比Hashtable更小性能更好。读操作大多数情况下不加锁写操作只锁当前桶所以并发性能比较高。4. ArrayList 和 LinkedList 区别ArrayList底层是动态数组LinkedList底层是双向链表。ArrayList支持根据下标随机访问所以查询效率高时间复杂度是 O(1)。但是如果在中间插入或删除元素需要移动后面的元素性能相对较低。LinkedList不支持高效随机访问查询某个位置需要从头或尾遍历时间复杂度是 O(n)。但是如果已经定位到节点插入和删除只需要修改前后指针效率比较高。实际开发中如果查询多优先用ArrayList如果频繁在头尾插入删除可以考虑LinkedList但大部分业务场景ArrayList用得更多。5. HashMap 扩容机制HashMap默认初始容量是 16默认负载因子是 0.75。当元素数量超过容量乘以负载因子时就会触发扩容。每次扩容容量会变成原来的 2 倍。扩容时会创建一个新的数组然后把旧数组中的元素迁移到新数组里。在 JDK 1.8 中因为容量是 2 的幂所以扩容后元素的位置要么保持原位置要么移动到原位置 oldCap 的位置不需要完全重新计算索引这样效率更高。6. 为什么 HashMap 容量是 2 的幂主要是为了提高取模效率和让元素分布更均匀。正常计算数组下标需要 hash % length但取模运算相对慢。HashMap使用的是 (length - 1) hash前提就是 length 必须是 2 的幂这样才能等价于取模。另外扩容时也更方便因为扩容为原来的 2 倍后只需要看 hash 的某一位是 0 还是 1就能判断元素是在原位置还是移动到原位置 oldCap。多线程1. synchronized 和 ReentrantLock 区别synchronized是 Java 关键字由 JVM 层面实现使用简单自动加锁和释放锁。ReentrantLock是 JUC 包下的类需要手动 lock 和 unlock功能更灵活。它支持公平锁、非公平锁、可中断锁、尝试加锁、条件队列 Condition 等。一般简单同步场景用synchronized就可以如果需要超时等待、中断、多个条件队列就可以用ReentrantLock。2. volatile 保证什么不能保证什么volatile主要保证两个点可见性和禁止指令重排序。可见性是指一个线程修改了volatile变量其他线程可以马上看到最新值。禁止重排序主要用于一些并发场景比如双重检查锁单例。但是volatile不能保证复合操作的原子性。比如 i 实际包含读取、加一、写回三个步骤即使用volatile修饰也不能保证线程安全。这个时候需要synchronized、Lock 或AtomicInteger。3. 线程池核心参数有哪些线程池核心参数主要有 7 个corePoolSize核心线程数maximumPoolSize最大线程数keepAliveTime空闲线程存活时间unit 时间单位workQueue阻塞队列threadFactory线程工厂handler拒绝策略。执行流程是任务来了先创建核心线程核心线程满了进入队列队列满了再创建非核心线程达到最大线程数后还放不下就执行拒绝策略。4. 线程池拒绝策略有哪些常见有四种。AbortPolicy直接抛异常默认策略。CallerRunsPolicy让提交任务的线程自己执行。DiscardPolicy直接丢弃任务不抛异常。DiscardOldestPolicy丢弃队列中最老的任务再尝试提交新任务。生产环境一般不建议直接静默丢弃最好结合业务场景记录日志、告警或者做降级处理。5. 生产环境线程池怎么配置线程池参数不能随便写要结合任务类型。如果是 CPU 密集型任务比如计算类任务线程数一般设置为 CPU 核数或 CPU 核数 1。如果是 IO 密集型任务比如调用接口、查询数据库、文件读写可以设置得比 CPU 核数大一些因为线程大部分时间在等待 IO。生产环境还要注意队列不要无限大线程名称要有业务含义拒绝策略要记录日志最好加监控比如线程数、队列长度、任务耗时、拒绝次数。6. CountDownLatch、Semaphore、CyclicBarrier 区别CountDownLatch是倒计时器适合一个线程等待多个线程执行完成比如主线程等多个任务都结束后再汇总。它不能重复使用。Semaphore是信号量主要用来控制并发数量比如限制最多 10 个线程同时访问某个资源。CyclicBarrier是循环屏障适合多个线程互相等待等所有线程都到达某个点后再一起继续执行而且可以重复使用。JVM1. JVM 内存区域有哪些JVM 运行时内存主要包括堆、虚拟机栈、本地方法栈、方法区、程序计数器。堆主要存放对象实例是 GC 重点管理的区域。虚拟机栈存放方法调用过程中的局部变量表、操作数栈、方法出口等。方法区主要存放类信息、常量、静态变量等。程序计数器记录当前线程执行到哪一行字节码。本地方法栈主要服务 native 方法。2. 堆和栈区别堆主要存对象线程共享生命周期由 GC 管理。栈主要存方法调用过程中的局部变量、方法参数、返回地址等每个线程都有自己的栈线程私有。堆容易出现 OOM栈容易出现 StackOverflowError比如递归太深。3. 对象什么时候进入老年代常见情况有几种。第一对象在新生代经过多次Minor GC仍然存活年龄达到阈值后会晋升到老年代。第二大对象可能直接进入老年代比如很大的数组或字符串。第三动态年龄判断如果 Survivor 区中某一批年龄对象占用空间过大也可能提前晋升。第四Minor GC后 Survivor 放不下的对象也可能进入老年代。4. 常见 GC 算法常见 GC 算法有标记-清除、复制、标记-整理、分代收集。标记-清除会先标记存活对象再清除垃圾对象缺点是会产生内存碎片。复制算法把内存分成两块每次只用一块回收时把存活对象复制到另一块适合新生代。标记-整理会把存活对象整理到一端避免碎片适合老年代。分代收集是根据对象生命周期不同新生代和老年代采用不同算法。5. Full GC 频繁怎么排查我会先看 GC 日志确认Full GC触发原因比如老年代空间不足、元空间不足、大对象分配、System.gc调用等。然后看堆内存使用情况确认是内存泄漏还是正常流量过大。可以通过jstat、jmap、jvisualvm、MAT等工具分析对象占用。如果是内存泄漏要看哪些对象一直无法回收比如缓存没有过期、集合一直增长、线程池或ThreadLocal没有释放。如果是参数不合理就需要调整堆大小、新生代比例、垃圾收集器参数或者优化代码减少大对象创建。6. OOM 怎么排查OOM 我一般会先确认是哪种 OOM比如Java heap space、Metaspace、GC overhead limit、Direct buffer memory、unable to create new native thread。如果是堆 OOM先保留 dump 文件用MAT或jvisualvm分析大对象和引用链看是不是集合、缓存、队列、Map 没有释放。如果是元空间 OOM可能是类加载太多比如动态代理、热部署导致类无法卸载。如果是线程创建失败要看线程数是否过多、线程池是否失控、系统资源是否不足。总体思路是先看错误类型再看日志和 dump最后定位代码或参数问题。阶段性重点OOM 排查、线程池参数、HashMap、ConcurrentHashMap、JVM 内存模型与 GC 日志。OOM 排查、线程池参数、HashMap、ConcurrentHashMap、JVM 内存模型、GC 日志。Spring1. Spring Bean 生命周期Spring Bean 生命周期大致是实例化、属性填充、Aware 回调、BeanPostProcessor前置处理、初始化方法、BeanPostProcessor后置处理最后 Bean 可以使用。容器关闭时会执行销毁方法。如果实现了InitializingBean会调用afterPropertiesSet()如果配置了init-method也会执行对应初始化方法。实际开发中我们可以通过PostConstruct、InitializingBean、BeanPostProcessor等方式在 Bean 初始化阶段做一些扩展。2. IOC 和 AOP 原理IOC 是控制反转把对象的创建和依赖管理交给 Spring 容器不需要我们手动 new 对象。这样可以降低代码耦合。AOP 是面向切面编程把一些通用逻辑比如日志、事务、权限、监控从业务代码中抽离出来通过代理方式增强目标方法。Spring AOP 底层主要是动态代理。如果目标类实现了接口一般用 JDK 动态代理如果没有接口可以用 CGLIB 生成子类代理。3. Spring 事务失效场景常见事务失效有几类。第一方法不是 public。第二同一个类内部方法互相调用因为没有经过代理对象。第三异常被 catch 掉了没有继续抛出。第四抛出的是检查异常但没有配置rollbackFor。第五数据库本身不支持事务比如 MySQL MyISAM。第六多线程或异步方法中事务不会自动传播。第七多数据源场景事务管理器配置不正确。4. Transactional 为什么有时不生效因为 Transactional 是基于 Spring AOP 代理实现的只有通过代理对象调用方法事务才会生效。如果在同一个类里面 this.xxx() 调用事务方法其实没有经过代理对象所以事务不会生效。另外如果异常被 catch 了或者抛出的不是RuntimeException也可能不会回滚。所以实际开发中要注意方法调用方式、异常抛出方式和rollbackFor配置。5. Spring Boot 自动装配原理Spring Boot 自动装配主要是通过启动类上的 SpringBootApplication 实现的其中包含 EnableAutoConfiguration。Spring Boot 启动时会读取自动配置类把符合条件的配置加载到容器中。很多自动配置类里面会使用 ConditionalOnClass、ConditionalOnMissingBean、ConditionalOnProperty 等条件注解。简单理解就是Spring Boot 根据当前项目引入的依赖和配置自动帮我们创建需要的 Bean减少手动配置。6. Feign 调用超时怎么排查我会从几个方面排查。第一看 Feign 的连接超时时间和读取超时时间配置是否合理。第二看被调用服务是否正常接口是否本身执行慢。第三看网络、网关、注册中心、负载均衡是否有问题。第四看调用链路日志确认请求有没有到达下游。第五看下游是否有慢 SQL、线程池耗尽、连接池不足等问题。如果是偶发超时还要结合监控看当时的流量、CPU、内存、GC 和数据库情况。7. 拦截器、过滤器、AOP 区别Filter 是 Servlet 规范里的过滤器作用范围最大在请求进入DispatcherServlet之前执行常用于编码、跨域、登录校验。Interceptor 是 Spring MVC 的拦截器在请求进入 Controller 前后执行可以拿到 Handler 信息常用于权限校验、日志、接口耗时统计。AOP 是方法级别的增强不只限于 Web 请求可以对Service方法、Mapper 方法等做日志、事务、权限、监控等增强。MyBatis / MyBatis-Plus1. #{} 和 ${} 区别#{} 是预编译参数会使用PreparedStatement可以防止 SQL 注入。${} 是字符串直接拼接不能防止 SQL 注入。所以普通参数值一定要用 #{}。只有表名、字段名、排序字段这种无法预编译的场景才可能用 ${}但必须做白名单校验。2. MyBatis 一级缓存、二级缓存一级缓存默认开启作用范围是SqlSession。同一个SqlSession中执行相同查询可能会直接从缓存中取。二级缓存作用范围是 Mapper namespace需要手动开启多个SqlSession可以共享。但实际项目中二级缓存用得不多因为数据更新后缓存一致性不好控制。更多时候会使用 Redis 做业务缓存。3. 动态 SQL 怎么防注入动态 SQL 防注入主要有两个原则。第一参数值使用 #{} 或 MyBatis-Plus 的参数绑定不直接拼接用户输入。第二字段名、排序字段、表名这类不能参数化的内容要使用枚举或白名单映射不能直接使用前端传入值。比如统计平台中前端传入排序字段时后端应该校验这个字段是否在允许列表中再拼接 SQL。4. 分页插件原理MyBatis 分页插件一般是通过拦截器拦截 SQL 执行过程在 SQL 执行前根据数据库类型改写 SQL。比如 MySQL 会追加 limitOracle 会使用 rownum。通常还会额外执行一条 count SQL用来查询总数。所以分页查询要注意 count SQL 的性能复杂 join 场景下 count 也可能很慢。5. MyBatis-Plus Wrapper 优缺点优点是写动态条件比较方便不用写大量 XML和 Java 代码结合更自然适合条件比较多的查询场景。缺点是复杂 SQL 可读性可能不如 XML而且如果封装不好代码会比较长。另外对于多表 Join、复杂聚合、窗口函数这类 SQL还是要结合 XML、自定义 SQL 或动态 SQL Provider不一定全部用Wrapper。6. 批量插入怎么优化批量插入可以从几个方面优化。第一不要一条一条 insert可以使用批量 insert。第二控制每批数量比如 500 到 1000 条一批避免 SQL 太长。第三使用 MyBatis batch 模式或 JDBC batch。第四减少不必要的索引和唯一校验影响。第五如果是 Doris、ES、文件类数据可以使用更适合的批量导入方式比如Stream Load、Routine Load。另外要注意事务大小避免一次事务太大导致回滚成本高。高频重点题Transactional什么时候会失效回答要点方法不是 public同类内部方法调用异常被 catch 没有抛出抛出的是非 RuntimeException且没有配置 rollbackFor数据库引擎不支持事务多数据源事务未正确配置异步线程中事务不传播。MySQL / PostgreSQL1. BTree 索引原理MySQL InnoDB 默认使用BTree索引。BTree是一种多路平衡树非叶子节点只存索引键叶子节点存完整数据或者主键值并且叶子节点之间通过链表连接。它适合范围查询和排序因为叶子节点是有序的。相比二叉树BTree层级更低一次磁盘 IO 可以读取更多索引数据所以查询效率比较高。2. 联合索引最左前缀原则联合索引比如 (a, b, c)查询条件需要从最左边的 a 开始匹配才能充分利用索引。比如 where a ? and b ? 可以用到索引where b ? 一般用不到这个联合索引。如果遇到范围查询比如 a ? and b ? and c ?通常 c 就无法继续用于索引查找。所以设计联合索引时要把区分度高、查询频率高、等值匹配多的字段放在合适位置。3. 索引失效场景对索引列使用模糊搜索时依通配符开头使用联合索引时没有按照最左匹配原则使用k1, k2, k3 where value k2使用 OR 条件的时候处理不当比如有两个索引k1,k2。在使用的时候如果时 where v1 ‘v1’ or v2 ‘v2’ 的时候如果两边的列索引不一致对索引字段使用函数或者表达式会导致索引失效索引字段时varchar类型但是使用的时候用的是数字类型会导致索引失效因为mysql会把字符串转换成数字来进行比较。数据分布倾斜导致优化器弃用索引如果一个索引的某个值的占比比较高则优化器会考虑使用全表扫描而非使用索引进行查询因为使用全表扫描要比索引回表查询要快。在join时两个表的列字符集不通会导致隐式转换索引失效。4. explain 看哪些字段explain 主要看 type、possible_keys、key、rows、filtered、Extra。type 反映访问类型最好是 const、ref、range尽量避免 ALL。key 看实际使用了哪个索引。rows 看预估扫描行数。Extra 里面如果出现Using filesort、Using temporary要重点关注说明可能有排序或临时表。如果possible_keys有索引但 key 为空说明优化器没有选择索引需要分析条件、数据分布或索引设计是否合理。5. 慢 SQL 怎么排查慢 SQL 我一般先看执行计划 explain确认是否走索引、扫描行数大不大、有没有临时表和文件排序。然后看 SQL 本身是否 select 字段过多、join 表过多、where 条件是否合理、是否对索引列使用函数、是否有大范围 group by、order by、count distinct。再看表结构和索引设计联合索引顺序是否合理字段类型是否一致。如果是统计类查询可以考虑使用汇总表、宽表、预聚合避免每次都查明细表实时聚合。6. count(*)、count(1)、count(column) 区别count(*) 和 count(1) 都是统计行数一般差别不大MySQL 优化器会做优化。count(column) 是统计某一列不为 null 的行数如果这个字段有 null结果会小于总行数。所以如果要统计总行数一般用 count(*)。7. left join 条件写在 on 和 where 的区别对 left join 来说条件写在 on 后面是控制右表如何匹配左表数据仍然会保留。条件写在 where 后面是对 join 之后的结果再过滤。如果 where 中限制了右表字段不为空可能会把左表中没有匹配上的数据过滤掉导致 left join 变成类似 inner join。所以如果是右表匹配条件一般写在 on如果是最终结果过滤条件要根据业务确认是否写在 where。8. MySQL 和 PostgreSQL 在统计查询上的差异MySQL 更常用于OLTP业务系统简单查询和事务场景比较多。PostgreSQL 在复杂 SQL、多表 Join、窗口函数、CTE、执行计划等方面能力比较强很多统计分析类查询写起来更灵活。但实际项目里如果数据量继续变大或者涉及OLAP多维聚合分析MySQL 和 PostgreSQL 都可能遇到性能瓶颈这时候 Doris、ClickHouse这类OLAP数据库会更合适。我之前项目中也是从 MySQL 到 PostgreSQL再到 Doris主要就是根据统计查询复杂度和数据量做架构演进。Doris1. Doris 适合什么场景Doris 适合OLAP分析场景比如报表统计、多维分析、明细宽表查询、大数据量聚合、实时数仓等。它是列式存储、MPP 架构比较适合大宽表、group by、聚合查询。但它不适合高并发小事务更新也不适合替代 MySQL 做强事务OLTP系统。2. Doris FE 和 BE 分别做什么FE 是 Frontend主要负责元数据管理、SQL 解析、查询规划、权限管理、任务调度等。BE 是 Backend主要负责数据存储和计算执行。查询真正扫描数据、聚合计算、导入数据大部分是在 BE 上完成。简单说FE 更像大脑负责计划和调度BE 更像执行节点负责存储和计算。3. Doris 分区和分桶怎么设计分区一般按时间字段设计比如按天、按月分区这样查询时可以做分区裁剪减少扫描数据量。分桶一般选择高频过滤或 join 的字段比如订单 ID、用户 ID、套餐 ID、渠道 ID 等。分桶要考虑数据分布避免数据倾斜。比如埋点表可以按 event_time 做日期分区再按 event_id 或 user_id 分桶。统计表可以按 create_date 分区再按 contract_id、channel_id 等业务字段分桶。4. Duplicate Key、Aggregate Key、Unique Key 区别Duplicate Key是明细模型允许相同 Key 的多行数据同时存在适合日志、埋点、订单明细这类场景。Aggregate Key是聚合模型相同 Key 的数据会按照聚合函数合并比如 sum、max、min适合预聚合统计表。Unique Key是唯一键模型相同 Key 的数据会被更新覆盖适合需要按主键更新的场景。实际选择时要看业务是保留明细、做预聚合还是需要更新覆盖。5. Routine Load 原理Routine Load是 Doris 持续消费 Kafka 数据的一种导入方式。它会创建一个常驻导入任务按照配置的 topic、partition、offset、字段映射等信息从 Kafka 中持续拉取数据然后批量写入 Doris 表。它适合实时或准实时数据导入比如用户行为埋点、日志数据等。如果导入失败一般要看任务状态、错误日志、字段类型、JSON 格式、脏数据比例、Kafkaoffset等。6. Doris 查询慢怎么排查Doris 查询慢我一般先看 SQL 是否命中分区裁剪过滤条件有没有用到分区字段。然后看Profile分析耗时主要在扫描、Join、聚合、排序还是数据交换。再看表设计比如分区分桶是否合理、是否有数据倾斜、是否查询了太多列、是否做了大范围 count distinct 或 order by。还要看 BE 节点负载、内存、磁盘 IO、Compaction 情况。优化方式包括减少扫描列、加分区过滤、建设 DWS 汇总表、调整分桶、减少大 Join、使用预聚合或物化视图。7. 小文件/小批量导入过多有什么问题小批量导入太多会导致 Doris 生成大量小版本和小文件增加元数据压力和 Compaction 压力。长期这样可能导致导入变慢、查询变慢甚至影响集群稳定性。所以实际导入时要控制批次大小尽量批量写入。Kafka 场景可以调整Routine Load的 batch 参数文件导入可以合并文件后再导入。8. Doris 和 MySQL 的区别MySQL 主要是OLTP数据库适合事务型业务系统比如订单创建、用户信息维护、状态更新。Doris 是OLAP数据库适合大数据量统计分析比如报表、多维聚合、宽表查询。MySQL 是行式存储为主Doris 是列式存储和 MPP 架构。所以 MySQL 适合高并发小事务Doris 适合大批量数据分析和聚合查询。SQL 场景题统计查询突然很慢你怎么排查回答要点看 SQL 是否走了分区裁剪看过滤条件是否命中索引/分区字段看 Join 数据量是否过大看是否在字段上用了函数导致无法下推看是否 count distinct、大排序、大 group byDoris 中看 Profile看 BE 节点负载、内存、Compaction优化方式预聚合、宽表、分区分桶、减少 Join、调整查询口径。Kafka1. Kafka 为什么吞吐高Kafka 吞吐高主要有几个原因。第一它是顺序写磁盘顺序写比随机写快很多。第二它利用了操作系统Page Cache。第三Kafka 按Partition分区可以并行读写。第四它支持批量发送和批量消费。第五它使用零拷贝技术减少数据在内核态和用户态之间的拷贝。所以 Kafka 很适合高吞吐日志、埋点、消息流场景。2. Topic、Partition、Consumer Group 是什么Topic是消息主题可以理解为一类消息的逻辑分类。Partition是Topic的分区一个Topic可以有多个Partition用来提高并发能力。Consumer Group是消费者组同一个组内多个消费者共同消费一个Topic。一个Partition同一时间只能被同一个消费者组里的一个消费者消费。不同消费者组之间互不影响可以各自独立消费同一份数据。3. 如何保证消息不丢生产端可以设置acksall开启重试保证消息写入 Kafka。Broker 端要配置副本数和最小同步副本数避免单节点故障导致数据丢失。消费端不要在业务处理前就提交offset最好处理成功后再提交。如果是入库场景比如 Kafka 到 Doris还要关注导入任务状态和失败重试机制。4. 如何保证消息不重复消费Kafka 很难绝对保证不重复更多是业务上做幂等。比如消息里带唯一 ID消费端入库时根据唯一 ID 去重或者使用唯一键约束。offset提交也要注意业务处理成功后再提交offset。如果处理成功但提交offset失败可能会重复消费所以消费者逻辑要支持幂等。5. 如何保证顺序消费Kafka 只能保证单个Partition内消息有序不能保证整个Topic全局有序。如果要保证某一类业务消息有序比如同一个订单的消息有序就要让相同订单 ID 的消息发送到同一个Partition。消费时同一个Partition只能由一个消费者线程顺序处理这样才能保证顺序。6. 消息积压怎么处理我会先看消费端是否报错确认是不是业务处理失败导致消费停止。然后看消费速度和生产速度的差距看是否需要增加消费者实例或 Partition 数。如果下游是 Doris要看Routine Load状态、导入错误、脏数据比例、BE 写入压力。临时处理可以扩容消费者、提高批量消费能力长期要优化消费逻辑、批量写入参数、下游存储性能和告警机制。7. offset 提交方式Kafkaoffset提交主要有自动提交和手动提交。自动提交比较简单但可能出现消息还没处理完offset已经提交的情况导致消息丢失风险。手动提交更可控一般在业务处理成功后再提交offset。对可靠性要求高的场景我更倾向于手动提交并结合业务幂等处理。8. Kafka 和 RocketMQ 区别Kafka 更偏日志流和大吞吐场景比如埋点、日志采集、数据管道。RocketMQ更偏业务消息场景对事务消息、延迟消息、顺序消息等支持比较完善。如果是数据采集和实时数仓链路我更倾向 Kafka如果是订单、支付、交易这类业务消息RocketMQ也很合适。Redis1. Redis 常用数据结构Redis 常用数据结构有String、Hash、List、Set、ZSet。String常用于缓存简单值、计数器。Hash适合存对象。List可以做简单队列。Set适合去重。ZSet带分数排序适合排行榜、延迟队列等场景。另外 Redis 还有Bitmap、HyperLogLog、Stream等结构。2. 缓存穿透、击穿、雪崩缓存穿透是查询一个缓存和数据库都不存在的数据导致请求一直打到数据库。可以用缓存空值、布隆过滤器解决。缓存击穿是某个热点 key 过期大量请求同时打到数据库。可以用互斥锁、逻辑过期、热点 key 不过期解决。缓存雪崩是大量 key 同时过期或者 Redis 故障导致请求集中打到数据库。可以设置随机过期时间、限流降级、Redis 高可用来解决。3. Redis 分布式锁怎么实现基本实现是使用SET key value NX EX seconds只有 key 不存在时才能设置成功并且设置过期时间防止死锁。value 要设置成唯一值比如UUID释放锁时先判断 value 是否是自己的再删除避免误删别人的锁。删除操作最好用Lua脚本保证判断和删除的原子性。生产中如果要求更高可以使用Redisson。4. Redis 持久化 RDB/AOF 区别RDB是快照持久化会在某个时间点把内存数据生成快照文件恢复速度快但可能丢失最近一段时间的数据。AOF是追加日志会记录每次写命令数据安全性更好但文件更大恢复速度可能慢一些。实际生产中可以结合使用既保证恢复速度也尽量减少数据丢失。5. Redis 为什么快Redis 快主要有几个原因。第一数据存储在内存中。第二采用高效的数据结构。第三单线程处理命令避免频繁线程切换和锁竞争。第四使用 IO 多路复用模型处理网络连接。第五很多操作时间复杂度很低。需要注意的是Redis 单线程主要指命令执行模型后续版本中一些网络 IO、持久化等也有多线程优化。6. 热 key 怎么处理热 key 是某个 key 被大量访问导致 Redis 单点压力过高。处理方式有本地缓存、热点 key 拆分、读写分离、增加副本、限流降级。如果是配置类数据可以本地缓存一份减少每次访问 Redis。如果是大 key也要考虑拆分避免一次读取太多数据影响 Redis 性能。Kubernetes1. Pod、Deployment、Service 区别Pod是 K8s 中最小的调度单位里面可以运行一个或多个容器。Deployment用来管理Pod比如副本数、滚动更新、故障恢复。Service用来给一组Pod提供稳定访问入口因为PodIP 可能变化Service可以通过标签选择器转发到对应Pod。2. K8s 如何做滚动发布一般通过Deployment做滚动发布。更新镜像版本后Deployment会逐步创建新Pod同时逐步下线旧Pod。如果配置了readinessProbe只有新Pod就绪后才会接收流量。如果发布后有问题可以通过 rollout undo 回滚到上一个版本。3. 服务挂了怎么排查我会先看Pod状态比如 Running、CrashLoopBackOff、Pending。然后使用kubectl logs查看应用日志使用kubectl describe pod查看事件比如镜像拉取失败、探针失败、资源不足。如果是启动失败看配置、环境变量、数据库连接、端口冲突。如果是运行中异常看 CPU、内存、GC、接口日志和依赖服务状态。4. 怎么看日志一般使用下面的命令查看日志kubectl logspod-name如果Pod中有多个容器需要加-c container-name。如果要看实时日志可以加-f。如果Pod重启过可以用--previous查看上一次容器日志。5. livenessProbe 和 readinessProbe 区别livenessProbe是存活探针用来判断容器是否还活着。如果失败K8s 会重启容器。readinessProbe是就绪探针用来判断容器是否可以接收流量。如果失败Pod不会被加入Service后端但不一定会重启。简单说liveness 管重启readiness 管流量。6. ConfigMap 和 Secret 区别ConfigMap用来保存普通配置比如应用参数、配置文件。Secret用来保存敏感信息比如密码、token、证书。两者都可以通过环境变量或挂载文件的方式注入到Pod中。但Secret只是做了编码和权限控制不代表绝对安全生产环境还要结合权限管理和密钥管理方案。网络1. HTTP 缓存机制是什么HTTP 缓存是浏览器和服务器基于请求头、响应头共同实现的一套资源复用机制。其核心目的是减少重复请求、降低服务器压力、节省网络带宽并提升页面加载速度。HTTP 缓存通常分为两类强缓存浏览器直接使用本地缓存不向服务器发送请求。协商缓存浏览器向服务器发起请求由服务器判断本地缓存是否仍然有效。2. 强缓存和协商缓存有什么区别强缓存强缓存主要通过以下响应头控制Expires使用绝对时间表示缓存失效时间容易受到客户端时间不准确的影响。Cache-ControlHTTP/1.1 中更常用例如max-age3600表示资源在 3600 秒内有效。命中强缓存时浏览器不会向服务器发送请求通常会显示from memory cache或from disk cache。协商缓存协商缓存需要向服务器发送请求常见的两组控制方式是Last-Modified与If-Modified-Since。ETag与If-None-Match。如果服务器判断资源没有变化会返回304 Not Modified浏览器继续使用本地缓存如果资源发生变化则返回200 OK和最新资源。3.Last-Modified和ETag有什么区别Last-Modified根据资源最后修改时间判断是否变化精度通常只能达到秒级。如果资源在一秒内多次修改或者内容未变化但修改时间发生变化判断可能不够准确。ETag是服务器为资源生成的唯一标识能够更精确地判断资源内容是否变化但生成和比较ETag会带来一定计算开销。当两者同时存在时通常优先使用ETag / If-None-Match。4. HTTP 缓存的执行顺序是什么浏览器请求资源时通常按照下面的顺序判断判断强缓存是否有效。强缓存有效时直接读取本地缓存。强缓存失效后携带协商缓存标识向服务器发起请求。资源未变化时服务器返回304。资源已变化时服务器返回200和最新内容。对于带有内容哈希的静态资源例如app.a1b2c3.js通常可以设置较长的强缓存HTML 文件一般需要更谨慎避免用户长时间拿不到最新资源。总结这套题目覆盖了 Java 后端面试中最常见的知识体系。真正有效的准备方式不是逐字背诵答案而是形成以下表达结构先给结论一句话回答问题。再讲原理说明底层机制或关键流程。补充场景说明在什么情况下使用。指出风险说明常见失效、性能或一致性问题。结合项目用真实经历证明自己实际使用过。面试官通常不只关注你“知不知道”更关注你能否把知识应用到真实问题的定位、设计与优化中。