2.27-阿里云-齐天-一面
项目中用到的设计模式介绍一下
设计模式的基本原则
单一职责原则、开闭原则、里氏代换原则、依赖倒转原则、接口隔离原则、合成复用原则、迪米特法则
Java常用的集合有哪些
HashMap的底层实现
HashMap和HashSet的区别
介绍一下Java反射
反射在实际开发中的优点
Java中的堆栈有什么区别
Java中定义的变量是分配在堆还是栈中
垃圾回收机制讲一下
数据库的事务讲一下
事务的隔离级别
数据库的锁机制
如何保证没有死锁或者出现死锁如何处理
原子性怎么实现的
TCP三次握手和四次挥手,为什么握手是三次,挥手是四次
TCP拥塞控制
Redis常用的数据结构,讲一下跳表,跳表的复杂度
缓存雪崩,击穿,穿透出现场景和解决方案
讲一下实习项目
- 数据加密脱敏
- 双token机制
- 一开始如何建立安全连接,用的什么加密方法
- Redis滑动窗口,为什么选择这个方法
- 是否上线,QPS,如何做服务可用性保证,数据一致性如何保证
3.6-字节-财经-一面
拷打实习
操作系统中同步和互斥的区别
操作系统如何避免死锁
什么是段页式内存管理
段页式中,cpu取数据要访问几次内存
3 次
DNS的解析过程
可能在哪些环节出现问题,出现问题如何定位(可以使用哪些命令)
- 客户端配置环节:客户端的 DNS 服务器地址配置错误或不完整,可能导致无法正确连接到 DNS 服务器进行解析。此外,本地 DNS 缓存可能会出现数据错误或过时的情况,影响解析结果。
- 网络连接环节:从客户端到 DNS 服务器之间的网络路径出现故障,如路由器故障、网络线路中断、防火墙限制等,会导致客户端无法与 DNS 服务器通信,进而无法完成解析。
- DNS 服务器环节:DNS 服务器本身可能出现软件故障、硬件故障或配置错误。例如,DNS 服务器的区域文件配置错误,可能导致特定域名无法正确解析;服务器负载过高,也可能影响解析的效率和成功率。
- 域名注册环节:如果域名注册信息不正确、不完整或过期,域名的权威 DNS 服务器信息可能无法被正确获取,导致解析失败。
- 递归与迭代查询环节:在递归查询中,本地 DNS 服务器向其他 DNS 服务器请求解析时可能出现问题。迭代查询过程中,各级 DNS 服务器之间传递解析请求和响应时也可能出现错误,比如根 DNS 服务器、顶级域 DNS 服务器等返回错误或不完整的信息。
Linux 系统
- ping:与 Windows 系统中的 ping 命令功能类似,用于测试网络连通性。
- dig:是 Linux 系统中常用的 DNS 查询工具,功能强大,可用于查询各种 DNS 记录,如 A 记录、MX 记录等。可以指定域名、DNS 服务器等参数进行查询,查看详细的解析结果。
- nslookup:在 Linux 系统中也有该命令,用法与 Windows 系统类似,用于简单的 DNS 查询。
- host:也是一个 DNS 查询工具,可用于查询域名对应的 IP 地址等信息,相对 dig 命令更简洁。
- traceroute:与 Windows 系统中的 tracert 命令功能相同,用于跟踪网络路由路径,排查网络传输过程中的故障点。
http各版本区别
介绍一下代理模式
jdk和cglib哪种效率更高
https://www.cnblogs.com/brithToSpring/p/13356626.html
jdk6 下,在运行次数较少的情况下,jdk动态代理与 cglib 差距不明显;而当调用次数增加之后,cglib 表现稍微更快一些
jdk7,8下,jdk动态代理性能均高于cglib
Redis中哈希表发生哈希冲突如何处理
Redis数据的同步和备份是怎么做的
Redis哨兵机制
MySQL如何保证ACID
持久性为什么要用redolog而不直接刷盘
实现事务的持久性,让 MySQL有 crash-safe 的能力,能够保证 MySQL 在任何时间段突然崩溃,重启后之前已提交的记录都不会丢失
将写操作从「随机写」变成了「顺序写」,提升 MySQL 写入磁盘的性能。
执行一条SQL语句的过程
手撕:锯齿层序遍历
3.7-阿里控股-智能引擎-一面
C++和Java最典型的区别
Java内存泄露有哪些情况
长生命周期的对象持有短生命周期对象的引用 (eg.将对象放入静态集合后未及时移除)
未关闭资源
对象存入
ThreadLocal
后未调用remove()
过度使用
String.intern()
方法线程池
非静态内部类持有外部类引用
Java常见的关键字
final的作用
反射了解吗,可以修改final修饰的属性吗
https://www.cnblogs.com/noKing/p/9038234.html
反射是可以修改
final
变量的,但是如果是基本数据类型或者String
类型的时候,无法通过对象获取修改后的值,因为JVM对其进行了内联优化。即变量的值是修改成功的, 但是方法输出的内容在JVM优化后就被写死了, 所以无论是否被正确修改为其他的值, 方法始终都会打印初始值.
反射无法修改同时被static final修饰的变量,只能先去除final再修改
JVM层面对final做了哪些处理
内存模型与可见性:插入内存屏障,禁止重排序,确保
final
字段的写操作对其他线程可见。编译器优化:内联和常量折叠,优化
final
字段的访问。类加载与字节码验证:确保
final
字段在构造函数中被正确初始化。标记位与元数据:使用
ACC_FINAL
标记表示final
字段,维护字段的元数据。反射与安全性:限制反射修改
final
字段,确保对象的不变性和线程安全性。平常怎么使用线程池
使用线程池过程中有没有遇到什么问题
- 回答:ThreadLocal、参数设置
线程池参数要怎么设置
拒绝策略如果将任务重新放入线程池会有什么问题
- 回答:依然会被拒绝
那要怎么解决呢
- 回答:找个地方暂存,比如消息队列,削峰
直接放入消息队列这种做法适用于哪些场景
突发流量:系统在短时间内接收到大量任务,需要平滑处理。
异步任务:任务可以异步处理,不需要立即返回结果。
高可用性:需要保证任务不丢失,即使系统暂时无法处理。
一般你是怎么new线程池的
CompletableFuture底层用的什么线程池
ForkJoinPool 和 ThreadPoolExecuter 有什么区别,为什么 JDK 要做一个 ForkJoinPool
https://pdai.tech/md/java/thread/java-thread-x-juc-executor-ForkJoinPool.html
基于工作窃取(Work-Stealing)算法。每个线程维护一个双端队列(Deque),优先执行自己队列中的任务,空闲时可以从其他线程的队列尾部“窃取”任务。
ForkJoinPool 适用于分治任务(如递归任务)和任务之间有依赖的场景。支持任务分解(
fork()
)和合并(join()
)。ThreadPoolExecuter 是一个通用的线程池实现,它遵循传统的生产者 - 消费者模型
JDK21里的虚拟线程有了解过吗,它也是用ForkJoinPool实现的,为什么不用ThreadPoolExecuter
任务调度效率:
ForkJoinPool
的工作窃取算法适合高并发场景,能够高效调度大量虚拟线程。线程管理灵活性:
ForkJoinPool
能够动态调整线程利用率,适应虚拟线程的高并发需求。任务挂起与恢复:
ForkJoinPool
的任务分解机制能够很好地支持虚拟线程的挂起和恢复。性能优化:
ForkJoinPool
在高并发场景下的性能优于ThreadPoolExecutor
。垃圾回收了解吗
为什么Java没有用引用计数,而Python使用了引用计数,引用计数有什么缺点
如何解决循环引用的问题
可达性分析-标记清除
使用弱引用
面试官说明时间:引用计数有个问题,每次引用都要+1,涉及到并发问题,对性能有影响,只能用于对性能要求不高的语言
拷打项目
- 用到了什么设计模式
- 模式背后体现了什么思想
- 像策略模式这些都用到了继承,你觉得继承有哪些优缺点
- 面试官说明时间:继承是有很强的语义的,如果设计不好的话很容易走样
拷打实习
- 代码里的具体流程
- 请求用到了什么http库
- 如果失败了应该尝试重试
- 那么什么情况下可以重试
- 请求完OCR后,如果保存到数据库时失败了要怎么处理
3.9-美团-AI面试
HTTPS和HTTP的区别
如何查看文件内容,列举三个命令
解释数据库分片(Sharding)的概念,它有什么优势和挑战
数据库分片是一种将大型数据库拆分成多个较小、更易管理的部分(称为“分片”)的技术。每个分片独立存储在不同的服务器或节点上,共同组成完整的数据库。分片可以基于特定规则(如用户ID、地理位置等)进行数据划分,确保每个分片只包含部分数据。
数据库分片通过将数据分散到多个服务器上,提升了性能、扩展性和可用性,但也带来了管理复杂性、数据一致性等挑战。
ThreadLocal,解决了什么问题
Java封装的目的,为什么有封装
隐藏对象的内部细节,仅对外暴露必要的接口。保护数据完整性、降低耦合度
第三方支付接口超时情况下,如何设计对账补偿机制
支付接口超时,系统标记交易为“超时”。系统主动查询支付状态,若成功则更新为“成功”,否则标记为“失败”。每日对账时,系统比对记录与支付平台数据,发现异常则触发补偿机制。补偿完成后,系统通知用户交易结果。
在这种设计中,你如何外理由于网络波动或具他不可控因素导致的重复支付问题?你会采取哪些措施来防止重复支付的发生?
支付接口应支持幂等性,即同一笔交易无论请求多少次,结果都一致。可以通过在请求中传递唯一的交易号(如订单号)来实现。
3.10-阿里云-齐天-二面
常见的树的数据结构有哪些,以及使用场景
有在实际项目中用到吗
讲讲Java中的数据结构
展开讲讲B+树,解决了数据库的什么问题,没有B+树之前怎么解决的,和原来的方法有什么区别
数据库中用B+树做什么
如果需要update一个节点,这时候的树操作是什么样的
更新后的键值仍然在节点的键值范围内。无需调整树结构,更新完成。
更新后的键值超出节点的键值范围。需要调整树结构,可能涉及节点分裂或合并。
Java创建线程的方法
线程通信有哪些方式
操作系统也提供线程库,和java的库有什么不一样的地方
用户级和内核级的区别在哪
场景题
需要爬取阿里云官网的产品文档,同时处理后写到数据库,这里涉及到表结构的设计以及Java的实现(包括并发、多线程),请给出设计方案
爬虫模块:负责从阿里云官网抓取产品文档。
数据处理模块:负责解析和清洗抓取的数据。
数据库模块:负责设计表结构并将处理后的数据存储到数据库中。
并发与多线程模块:确保系统能够高效地处理大量数据。
| 字段名 | 数据类型 | 描述 |
| :——————- | :—————— | :—————- |
|id
| BIGINT (主键) | 自增ID |
|title
| VARCHAR(255) | 文档标题 |
|content
| TEXT | 文档内容 |
|publish_date
| DATETIME | 文档发布时间 |
|url
| VARCHAR(512) | 文档URL |
|created_at
| DATETIME | 记录创建时间 |
|updated_at
| DATETIME | 记录更新时间 |数据库的类型应该用什么,MySQL中varchar和text默认长度是多少
varchar: 没有默认长度,最大长度是 65,535 字节
TINYTEXT:最大长度为 255 字节(2^8 - 1)
TEXT:最大长度为 65,535 字节(2^16 - 1)
MEDIUMTEXT:最大长度为 16,777,215 字节(2^24 - 1)
LONGTEXT:最大长度为 4,294,967,295 字节(2^32 - 1)
使用线程池爬取网页时
线程池要开多大
I/O密集型任务: 2N
线程池大小 = CPU 核心数 * (1 + 等待时间 / 计算时间)
对于这么多网页,线程如何进行调度、分配
使用阻塞队列存储需要爬取的URL,如果需要优先处理某些 URL,可以使用
PriorityBlockingQueue
,并为任务设置优先级。
如果线程作为一个独立的任务,每次处理完就写入db,会导致db负载比较高,要怎么解决
将多次写入操作合并为一次批量写入,减少数据库的 I/O 操作次数。
将数据库写入操作放到单独的线程或线程池中执行,避免阻塞主线程。
使用消息队列(如 Kafka、RabbitMQ)来接收处理结果。单独的消费者服务从消息队列中读取数据并写入数据库。
如果仍然需要直接写入数据库,可以使用数据库连接池来优化数据库连接的管理。
拷打实习
SSE是一个什么样的协议,格式是什么样子的,如何表达传输开始,传输中,传输结尾
基于HTTP协议
每一次发送的信息,由若干个
message
组成,每个message
之间用\n\n
分隔。每个message
内部由若干行组成,每一行都是[field]: value\n
格式。field取值包括data、event、id、retry传输开始:客户端通过
EventSource
对象发起请求,服务器响应Content-Type: text/event-stream
,表示开始SSE连接。传输中:服务器持续发送消息,客户端通过
onmessage
或addEventListener
接收。传输结束:服务器关闭连接或客户端调用
close()
方法结束传输。遇到了什么技术挑战,技术选型怎么做的,现在重新考虑有什么可以优化的
3.10-腾讯-CDG-金融科技-一面
手撕:反转正整数
手撕:合并区间
拷打实习
RBAC模型是什么
这种将权限绑定在角色上,再给用户账号赋予角色的方式就叫做基于角色的权限管理(RBAC)
双token刷新,为什么要用两个token
https://blog.csdn.net/qq_23845083/article/details/144154192
Access Token用于访问受保护资源,有效期较短,即使泄露,攻击者也只能在短时间内使用。Refresh Token用于获取新的Access Token,有效期较长,但只在特定场景下使用,减少了泄露风险。
用户体验优化,无感刷新
权限控制灵活,可以通过控制Refresh Token的有效期,刷新次数等方式来灵活的管理用户的会话生命周期
有空了解一下微信接入,也是基于token的
通信使用的是HTTP还是HTTPS
为什么使用 WebSocket 和 SSE
HTTP和HTTPS有什么区别
为什么不直接用服务端公钥加密传输
非对称加密(如RSA)效率低,公钥加密计算量大,速度慢,不适合加密大量数据
服务器发出的给所有人的结果,都可以使用同一份公钥解密
前向安全,一旦服务端的私钥泄漏了,过去被第三方截获的通讯密文都会被破解
3.14-腾讯-PCG-应用架构-一面
手撕:LRU
手撕:最长无重复子串
内核态和用户态的区别
内核态和用户态是OS中两种不同的处理器运行状态。用户态中程序不能直接访问硬件资源和执行特权指令,内核态可以访问所有硬件资源并执行任何指令
系统调用的步骤,如何触发
频繁进行用户态和内核态的转化,对性能有影响吗
进程和线程的区别18057170522
进程和线程的通信方式
进程通信:管道/匿名管道(pipe)、有名管道(FIFO)、信号(Signal)、消息(Message)队列、共享内存(share memory)、信号量(semaphore)、套接字(socket)
线程通信:共享内存、锁(互斥锁、读写锁)、条件变量、信号量
实际上只有进程间需要通信,同一进程的线程共享地址空间,没有通信的必要,但要做好同步/互斥,保护共享的全局变量。
使用多线程和单线程对比有什么优劣
提高了并发性能,但也有线程同步、死锁等问题
为什么会产生线程安全问题,直接对一个共享变量读写是否会有问题
可见性问题
常见的同步方式
互斥锁(Mutex) 、读写锁(Read-Write Lock)、信号量(Semaphore) 、屏障(Barrier) 、事件(Event)
虚拟内存,有什么作用
- 隔离进程:物理内存通过虚拟地址空间访问,虚拟地址空间与进程一一对应。每个进程都认为自己拥有了整个物理内存,进程之间彼此隔离,一个进程中的代码无法更改正在由另一进程或操作系统使用的物理内存。
- 提升物理内存利用率:有了虚拟地址空间后,操作系统只需要将进程当前正在使用的部分数据或指令加载入物理内存。
- 简化内存管理:进程都有一个一致且私有的虚拟地址空间,程序员不用和真正的物理内存打交道,而是借助虚拟地址空间访问物理内存,从而简化了内存管理。
- 多个进程共享物理内存:进程在运行过程中,会加载许多操作系统的动态库。这些库对于每个进程而言都是公用的,它们在内存中实际只会加载一份,这部分称为共享内存。
- 提高内存使用安全性:控制进程对物理内存的访问,隔离不同进程的访问权限,提高系统的安全性。
- 提供更大的可使用内存空间:可以让程序拥有超过系统物理内存大小的可用内存空间。这是因为当物理内存不够用时,可以利用磁盘充当,将物理内存页(通常大小为 4 KB)保存到磁盘文件(会影响读写速度),数据或代码页会根据需要在物理内存与磁盘之间移动。
浏览器输入url的过程
三次握手,能否简化为两次
第三次握手丢失服务端会怎么处理
第三次握手可以携带数据吗
四次挥手,二三次能合并吗
拆箱装箱,频繁拆箱装箱是否对性能有影响
String,StringBuilder,StringBuffer
字符串拼接用什么
GC如何判断对象是否存活
内存泄露的场景
类A有一个内部类B,B被外界持有是否会造成内存泄漏
会的,非静态内部类B的对象会持有A的对象的引用
循环引用是否会造成内存泄漏
四种引用类型
HashMap是不是线程安全的
HashMap底层数据结构
HashMap的key可以为null吗
HashMap用什么类型作为key性能最好
key使用ArrayList是否会有问题
会,hashcode会变化
拷打项目
3.18-阿里控股-智能引擎-二面
拷打实习
多线程开多少线程比较好
进程、线程、协程如何理解
协程是用户态的轻量级线程,由程序自行控制调度,而非操作系统内核。协程的切换完全在用户态完成,无需进入内核态,因此开销极小。
Java进程最大能用多大的堆,一般会设置多大的堆
看过哪些框架中间件的源码
Object有哪些方法
为什么要同时重写hashcode、equals
hashcode相同,equals为false会导致逻辑错误吗
wait()和sleep()的区别
所属类
wait()
:wait()
是Object
类的方法,所有对象都可以调用。sleep()
:sleep()
是Thread
类的静态方法,作用于当前线程。
调用方式
wait()
:必须在同步代码块或同步方法中调用,且调用前必须持有对象的锁(即使用synchronized
关键字)。sleep()
:可以在任何地方调用,不需要持有锁。
锁的释放
wait()
:调用wait()
后,线程会释放持有的对象锁,允许其他线程进入同步代码块。sleep()
:调用sleep()
后,线程不会释放任何锁,其他线程无法进入同步代码块。
唤醒机制
wait()
:线程可以通过notify()
或notifyAll()
方法被唤醒,或者等待超时后自动唤醒。sleep()
:线程在指定的时间结束后自动恢复执行,无法被其他线程唤醒。
用途
wait()
:通常用于线程间的协作,例如生产者-消费者模式,等待某个条件满足。sleep()
:通常用于暂停线程的执行一段时间,例如模拟延迟或定时任务。
笔试:有两个有序数组A,B,定义集合 S = {a + b | a $\in$ A,b $\in$ B},请给一种算法,求 S 中最小的 K 个元素。
例如A = {1, 1, 4},B = {1,3},则 S = {2, 4, 5, 7},则最小的3个元素为{2,4,5}
3.19-美团-核心本地商业-一面
- 拷打实习
- 线程池介绍一下,是怎么用的,参数是怎么配置的
- 垃圾回收算法
- 垃圾回收器
- 讲一下Spring Ioc,AOP(项目相关)
- 有用到过MQ吗,解决了什么问题
- 手撕:K个一组反转链表
- 手撕:二叉树的最大路径和
3.20-字节-广告业务-一面
拷打实习
限流机制
Sentinel限流怎么实现的
责任链模式,滑动窗口实现
漏桶、令牌桶怎么实现的
漏桶算法的核心思想是将请求看作水,漏桶以一个固定的速率漏水(处理请求)。如果桶满了(请求过多),则丢弃多余的请求。(消息队列的限流本质上就是漏桶算法)
令牌桶算法的核心思想是系统以固定的速率向桶中添加令牌,每个请求需要从桶中获取一个令牌。如果桶中没有足够的令牌,则触发限流。
线程池大小怎么设置的
最佳线程数 = N(CPU 核心数)*(1 + WT(线程等待时间)/ST(线程计算时间))
WT(线程等待时间)= 线程运行总时间 - ST(线程计算时间)
WebFlux内部是怎么实现的
WebFlux 通过 Reactor 和响应式编程模型实现非阻塞、异步处理,适合高并发场景。其核心在于事件循环、响应式流
- 异步和同步针对调用者,调用者发送请求,如果等着对方回应之后才去做其他事情就是同步,如果发送请求之后不等着对方回应就去做其他事情就是异步。
- 阻塞和非阻塞针对被调用者,被调用者受到请求之后,做完请求任务之后才给出反馈就是阻塞,受到请求之后马上给出反馈然后再去做事情就是非阻塞。
响应式和非响应式的区别
响应式编程是一种基于数据流和变化传递的声明式编程范式。
非响应式:同步阻塞,每个请求分配一个线程
响应式:异步非阻塞,事件循环,支持背压,消费者可以控制数据流的速度,避免生产者发送过多数据。
stream流底层是怎么实现的
基于流水线和惰性求值
每个中间操作会创建一个新的
Stream
对象(就是ReferencePipeline
),并将其链接到流水线中,形成一个双向链表。它会从最后一个中间操作开始自下而上进行Sink封装,每个Sink的downstream属性来保存下一个阶段需要执行的操作,第一个封装的Sink的downstream引用的是结束操作,直到所有中间操作封装完成后,返回一个Sink套娃
对Sink套娃从第一个开始,执行
begin
方法做好数据准备- 之后通过
accept
方法对元素进行处理,中间操作会不断调用该方法 - 最后
end
方法告知Sink所有操作执行完毕
使用stream进行不同集合类型间的转化和直接写有什么区别
主要区别在于代码风格、可读性、灵活性和性能
线程可见性的问题,JMM
联系到硬件上的存储结构,寄存器、Cache、内存
CPU读内存数据的流程
手撕:两数相加II
3.21-蚂蚁-国际-一面
用英语介绍项目、自我介绍
介绍一下在学校中最有成就感的事情
拷打项目
- 真实上线的话,目前的秒杀实现能支持吗
- Redis出现故障怎么办
- 缓存和数据库的数据一致性如何保证
- 库存扣减时,数据库出现故障怎么办
线程的同步方式
Java实现多线程的方式,你最常用哪种,为什么
线程池如何设置线程数
索引的作用,优缺点
索引要依据什么去加,保证索引是有效的
数据库的乐观锁和悲观锁是什么
如果有多个连接同时对MySQL中的一条数据做更新操作,如何使用悲观锁的方式保证安全
一锁二判三更新
一锁:在操作数据之前,先锁定资源(例如通过分布式锁或数据库行锁)。
二判:判断当前数据是否符合预期条件(例如版本号、状态等)。
三更新:如果条件满足,则执行更新操作;否则放弃或重试。
重写和重载
动态代理的机制是什么样的
Java类的加载过程
了解过分库分表吗
了解过分布式事务吗
有维护过线上的程序吗,如果程序挂掉了如何排查
场景题:设计一个登陆系统
- 加密相关的问题,MD5不安全
- 如何防止机器模拟登录(人机验证,IP拦截)
场景题:设计一个数据预热系统,支持各类数据,数据量比较大
3.21-携程-一面
拷打项目
- Redis使用场景
- 如何使用RabbitMQ异步发放
Redis常见的数据类型
zset可以做哪些功能
Redis为什么快
Redis分布式锁怎么实现,锁的过期时间怎么设置
setnx 不支持设置过期时间,如果分成两个命令不支持原子性
使用set是原子性的
set key value [EX seconds] [PX milliseconds] [NX|XX]
Redission了解吗
分布式锁可以直接用Redission的RLock,不显式声明过期时间会自动续期,默认过期时间30s,每 过期时间/3 看门狗执行一次续期
MySQL索引为什么查询效率快
MySQL三种日志的作用
数据库两张表关联查询,时间很慢可能是哪些原因
索引失效:隐式类型转换、编码不同
线程池哪些参数,如何设置
Spring Bean的生命周期
JVM的内存模型
哪些区域可能发生内存溢出
出现内存溢出怎么定义问题
什么情况会导致Full GC
突然出现多次Full GC,同时老年代空间很充足,可能是什么原因
Dubbo了解过吗
ThreadLocal实现原理
项目中用ThreadLocal封装全局上下文如何避免内存泄漏
覆盖索引和回表查询的区别
volatile的作用
基于单例模式谈谈volatile
ArrayList和LinkedList底层实现,适用场景
ConcurrentHashMap底层实现
synchronized和ReentrantLock
幂等性了解吗,怎么实现接口的幂等性
RPC接口超时时间怎么设置的
websocket
设计模式了解哪些,工厂模式一般可以解决什么问题
3.24-腾讯-PCG-QQ-一面
拷打实习
- 什么是SSE
- 限流
手撕:合并两个有序列表(空间复杂度O(1))
手撕:LRU
拷打项目
分段锁
库存的最终一致性怎么做的
这里有没有必要使用延迟队列
RabbitMQ怎么处理消息丢失、消费失败、消费重复
如何保证幂等性
- 数据库唯一索引实现幂等性
- 防重表机制,与唯一索引机制是相同的原理
- 数据库乐观锁实现幂等性(版本号)
- 悲观锁实现幂等性
- 防重Token令牌实现幂等
- 分布式锁
- 状态机
访问一个网站的过程
HTTPS的步骤
TCP在握手阶段如何管理客户端的连接
用户态和内核态区别
什么时候会发生切换
系统调用(Trap):用户态进程 主动 要求切换到内核态的一种方式,主要是为了使用内核态才能做的事情比如读取磁盘资源。系统调用的机制其核心还是使用了操作系统为用户特别开放的一个中断来实现。
中断(Interrupt):当外围设备完成用户请求的操作后,会向 CPU 发出相应的中断信号,这时 CPU 会暂停执行下一条即将要执行的指令转而去执行与中断信号对应的处理程序,如果先前执行的指令是用户态下的程序,那么这个转换的过程自然也就发生了由用户态到内核态的切换。比如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后续操作等。
异常(Exception):当 CPU 在执行运行在用户态下的程序时,发生了某些事先不可知的异常,这时会触发由当前运行进程切换到处理此异常的内核相关程序中,也就转到了内核态,比如缺页异常。
fork()调用,子进程会拷贝哪些资源,不拷贝哪些
会拷贝的资源
- 进程地址空间:子进程会得到父进程地址空间的一个副本,这涵盖了代码段、数据段、堆和栈。不过,采用了写时复制(Copy-on-Write, COW)技术,也就是在父进程或子进程对内存进行写入操作之前,父子进程实际上共享同一块物理内存,仅在发生写入操作时才会复制内存页。
- 文件描述符:子进程会复制父进程的文件描述符表。这意味着父子进程可以访问相同的文件、管道和网络套接字等。
- 信号处理函数:子进程会继承父进程的信号处理函数设置。不过,子进程中的信号掩码(被阻塞的信号集合)会被清空。
- 环境变量:子进程会复制父进程的环境变量,这使得子进程能够拥有和父进程相同的环境配置。
- 进程属性:子进程会继承父进程的一些属性,像用户 ID、组 ID、工作目录、根目录等。
不会拷贝的资源
- 进程 ID:子进程会有一个全新的、唯一的进程 ID(PID),和父进程的 PID 不同。
- 父进程 ID:子进程的父进程 ID(PPID)会被设定为创建它的父进程的 PID。
- 进程时间:子进程会有自己独立的 CPU 时间统计,和父进程的时间统计无关。
- 挂起的信号:子进程不会继承父进程挂起的信号。
- 锁:父进程持有的文件锁、记录锁等不会被子进程继承。
父进程kill掉,对子进程有什么影响
孤儿进程:一个进程的父进程已经终止或者不存在,但是该进程仍在运行。这种情况下,该进程就是孤儿进程。孤儿进程通常是由于父进程意外终止或未及时调用 wait()或 waitpid()等系统调用来回收子进程导致的。为了避免孤儿进程占用系统资源,操作系统会将孤儿进程的父进程设置为 init 进程(进程号为 1),由 init 进程来回收孤儿进程的资源
向HashMap添加元素过程
假如key是User对象,这个对象需要做什么特殊处理吗
重写hashcode、equals
java加锁的形式有几种
synchronized可以用在哪些位置,粒度是什么样的
修饰实例方法 (锁当前对象实例)
修饰静态方法 (锁当前类)
修饰代码块 (锁指定对象/类)
volatile的作用
可见性是怎么实现的
对于编译器来说,发现一个最优布置来最小化插入屏障的总数几乎是不可能的,为此,JMM 采取了保守的策略。
- 在每个 volatile 写操作的前面插入一个 StoreStore 屏障。
- 在每个 volatile 写操作的后面插入一个 StoreLoad 屏障。
- 在每个 volatile 读操作的后面插入一个 LoadLoad 屏障。
- 在每个 volatile 读操作的后面插入一个 LoadStore 屏障。
内存屏障,属于JVM层面,JVM用内存屏障的概念统一描述所有平台的可见性保证
具体到x86架构下,对 volatile 修饰的共享变量进行写操作的时候会多出 lock 前缀的指令,lock 前缀的指令在多核处理器下会引发两件事情:
- 将当前处理器缓存行的数据写回到系统内存。
- 写回内存的操作会使在其他 CPU 里缓存了该内存地址的数据无效。(缓存一致性协议,如MESI)
lock前缀指令本身会隐含完整的内存屏障效果
类加载的过程
JVM的GC算法
GC Root有哪些
线程Thread对象,引用线程栈桢中的方法参数、局部变量等
系统类加载器加载的
java.lang.Class
对象,引用类中的静态变量监视器对象,用来保存同步锁synchronized关键字持有的对象
本地方法调用时使用的全局对象
mysql索引从数据类型看有哪些
B+树和B树的区别
聚簇索引和非聚簇索引的区别
数据库发生慢sql如何分析
索引失效的原因
mysql的事务隔离级别,分别解决了什么问题
不可重复读和幻读具体是什么
mysql的三种日志
redo log具体是怎么运作的
崩溃恢复是怎么实现的,如何判断是否要做恢复
- 如果redo log里面的事务是完整的,也就是已经有了commit标识,则直接提交;
- 如果redo log里面的事务只有完整的prepare,则判断对应的事务binlog是否存在并完整(这里是通过redolog和binlog共有的字段XID进行查找):
- 如果是,则提交事务
- 否则,回滚事务。
两阶段提交
Redis中的数据类型、数据结构
Redis的数据持久化
Redis的集群模式
Redis哨兵机制
Redis内存淘汰
3.25-腾讯-PCG-QQ-二面
手撕:被三整除求最大和
服务上线后达到千万日活,为了保证高可用,有哪些关键点要考虑
分布式与微服务化
- 服务拆分:将单体服务拆解为微服务,避免单点故障扩散(如用户服务、订单服务独立部署)。
- 无状态设计:服务无状态化(如Session存储到Redis),便于水平扩展和故障转移。
- 冗余部署:多机房/多可用区(AZ)部署,利用负载均衡(如Nginx、AWS ALB)实现流量分发。
容错与降级
- 熔断机制:通过Hystrix、Sentinel等工具在依赖服务故障时快速失败,避免雪崩。
- 降级策略:非核心功能降级(如关闭推荐算法,返回静态兜底数据)。
- 限流保护:针对API配置QPS限流(如Redis+Lua、Guava RateLimiter)。
数据一致性
- 最终一致性:通过消息队列(Kafka、RocketMQ)实现异步解耦。
- 分库分表:数据库按业务分片(如ShardingSphere),避免单库瓶颈。
数据库
- 主从复制:MySQL主从同步+读写分离,从库可读(如ProxySQL路由)。
- 集群化:MongoDB/Cassandra多节点分片,ETCD/Raft协议选主。
- 备份与恢复:定期全量备份+Binlog增量备份,演练恢复流程。
缓存
- 多级缓存:本地缓存(Caffeine)+分布式缓存(Redis Cluster)。
- 穿透保护:缓存空值或布隆过滤器(Bloom Filter)防击穿。
全链路监控
- 指标采集:Prometheus+Grafana监控QPS、延迟、错误率。
- 日志分析:ELK或ClickHouse日志中心,快速定位问题。
- 链路追踪:Jaeger/SkyWalking跟踪请求链路,识别瓶颈。
服务CPU突然飙高,排查思路是怎么样的
1. 快速确认现象
- 检查监控系统:确认CPU飙高的时间点、持续时间、是否伴随其他指标异常(如内存、磁盘IO、网络流量)。
- 范围确认:是单台机器还是集群整体?是某个服务还是所有服务?
2. 定位高CPU进程/线程
找到占用CPU最高的进程:
1
2
3top -c # 实时进程监控,按CPU排序(显示完整命令)
htop # 更友好的交互式工具(需安装)
ps -aux --sort=-%cpu | head -n 10 # 静态快照分析进程内的线程
1
2top -H -p <PID> # 查看指定进程的线程CPU占用
ps -T -p <PID> # 查看线程详情
3. 深入分析原因(根据进程类型)
java
获取线程堆栈:
jstack <PID> > jstack.log # 输出线程栈
常见问题:
- 死循环:线程卡在某个方法(如while(true))。
- 锁竞争:大量线程阻塞在
BLOCKED
状态(如synchronized
或ReentrantLock
)。 - GC问题:频繁Full GC导致CPU高(需结合GC日志分析)。
辅助工具:
1
2jstat -gcutil <PID> 1000 5 # 查看GC情况(1秒间隔,输出5次)
arthas async-profiler # 火焰图分析(需安装)
数据库
- 检查慢查询和锁
4. 关联性分析
- 时间关联:CPU飙高的时间点是否与以下事件重合?
- 代码发布、配置变更、流量突增(如活动开始)、定时任务触发。
- 日志检索:检查应用日志(如Error日志)、中间件日志(Nginx、Kafka)。
5. 临时缓解措施
- 限流降级:通过网关或服务网格(如Istio)限制流量。
- 重启策略:优先重启问题实例(避免雪崩),但需保留现场:
6. 根因定位与优化
高频原因
- 代码缺陷:
- 死循环、正则表达式灾难性回溯、算法复杂度突变。
- 资源竞争:
- 线程池配置不合理(如
Executors.newCachedThreadPool()
无限制创建线程)。 - 数据库连接池耗尽(如HikariCP等待连接)。
- 线程池配置不合理(如
- 外部依赖:
- 下游服务超时未设置,导致线程阻塞。
- 缓存击穿(如Redis失效后大量请求直达数据库)。
线上突然出现大量状态码500错误,如何定位问题
1. 紧急止血:流量降级、回滚策略、扩容备用节点
2. 快速确认问题范围
- 检查监控指标
- 错误类型分布:
- 确认500错误是统一错误(如全部
NullPointerException
)还是混合错误。 - 检查错误码细分(如502/503/504可能指向不同问题)。
- 确认500错误是统一错误(如全部
- 关联指标:
- 流量变化:是否伴随请求量突增(DDoS或真实流量?)。
- 资源水位:CPU、内存、磁盘IO、网络带宽是否异常。
- 依赖服务:数据库、缓存、第三方API是否正常。
- 错误类型分布:
- 日志快速检索
3. 根因定位(分场景排查)
- 代码缺陷:同一接口集中报错,日志中出现明确异常栈(如
NullPointerException
) - 依赖服务故障:错误日志中出现
ConnectionTimeout
、SocketException
等网络相关错误。 - 资源耗尽:监控显示CPU 100%、内存OOM、磁盘写满。
- 数据问题:数据库查询超时、主从延迟、死锁。
4. 深度分析工具
- 日志聚合分析:使用ELK或Grafana Loki对错误日志聚类
- 链路追踪:通过Jaeger/SkyWalking查看异常请求的完整链路
- 流量回放:如果是特定请求触发,使用工具(如GoReplay)捕获流量在测试环境复现
5. 修复与验证
热修复:
- 紧急修复后通过Arthas或JMX动态加载类(仅限简单逻辑)。
验证手段:
灰度发布:先修复1%的节点观察效果。
压测验证:用JMeter模拟故障场景。
- 检查监控指标
设计一个通用的缓存组件,要考虑哪些维度
1. 缓存层级设计
| 层级 | 实现方式 | 特点 | 适用场景 |
| :——————- | :—————————- | :—————————————- | :————————————- |
| 本地缓存 | Caffeine/Guava Cache | 零网络开销,但无法跨进程共享 | 高频读、数据量小的场景 |
| 分布式缓存 | Redis/Memcached | 跨进程共享,容量大 | 多实例共享数据的场景 |
| 多级缓存 | 本地+分布式组合 | 兼顾性能和一致性 | 高并发读场景(如商品详情) |设计要点:
- 支持自动分层(如优先读本地,未命中再查分布式缓存)
- 本地缓存需有容量淘汰策略(如LFU/LRU)
2. 缓存读写策略
| 策略 | 实现方式 | 优缺点 |
| :———————— | :———————————————- | :———————————- |
| Cache-Aside | 应用层主动读写缓存 | 灵活,但可能缓存不一致 |
| Read-Through | 缓存组件自动读DB(如JCache API) | 代码简洁,但实现复杂 |
| Write-Through | 写DB时同步更新缓存 | 强一致,但写入延迟高 |
| Write-Behind | 异步批量更新DB(如Kafka+消费者) | 高性能,但有数据丢失风险 |设计要点:
- 默认实现Cache-Aside模式
- 通过接口抽象支持其他策略的可插拔
3. 缓存过期与淘汰
| 机制 | 实现方式 | 特点 |
| :—————- | :——————————————————- | :————————- |
| TTL过期 | 设置固定过期时间(Redis EXPIRE) | 简单但可能缓存雪崩 |
| 动态过期 | 根据热点动态调整过期时间 | 提高缓存利用率 |
| 淘汰策略 | LRU/LFU/FIFO(Redis maxmemory-policy) | 避免内存溢出 |设计要点:
- 支持全局默认TTL和单个Key自定义TTL
- 添加随机抖动防止同一时间大批量Key失效(如
基础TTL + random(0, 300s)
)
4. 缓存一致性
| 方案 | 实现方式 | 时延 | 复杂度 |
| :———————— | :——————————————————— | :——- | :——- |
| 失效通知 | 数据库Binlog监听(如Canal+MQ) | 秒级 | 高 |
| 延迟双删 | 1. 删缓存 → 2. 更新DB → 3. 延迟再删缓存 | 毫秒级 | 中 |
| 版本号/时间戳 | 缓存Value带版本号,读写时校验 | 即时 | 低 |设计要点:
- 关键业务使用版本号校验(如商品库存)
- 普通业务可用延迟双删(平衡性能与一致性)
5. 高可用设计
| 风险点 | 解决方案 |
| :—————- | :————————————————————- |
| 缓存穿透 | 布隆过滤器+空值缓存(设置短TTL) |
| 缓存雪崩 | 多级缓存+过期时间随机化 |
| 缓存击穿 | 分布式锁(Redis SETNX)或热点Key永不过期 |
| 集群故障 | Redis Cluster分片+哨兵机制/Proxy层自动切换 |设计要点:
- 内置熔断机制(如错误率超阈值直连DB)
- 支持降级开关(可动态关闭缓存层)
6. 监控与治理
| 维度 | 监控指标 | 工具链示例 |
| :———- | :————————————- | :————————- |
| 性能 | 命中率、平均耗时、QPS | Prometheus+Grafana |
| 资源 | 内存使用、连接数、网络流量 | Redis INFO命令 |
| 业务 | 热点Key识别、大Value预警 | 自定义Agent+ELK |设计要点:
- 暴露
/cache/metrics
端点输出监控数据 - 支持命令行/API动态查询缓存内容(如
GET /cache/key/{key}
)
7. 高级功能扩展
| 功能 | 实现思路 |
| :——————- | :—————————————————————— |
| 热点探测 | 滑动窗口统计Key访问频次,自动升级为本地缓存 |
| 多租户隔离 | 通过Namespace前缀分离不同业务线缓存 |
| 缓存预热 | 启动时加载预设Key,或基于历史访问模式预测加载 |
| 透明压缩 | 对大Value自动Snappy压缩(需权衡CPU/带宽) |高可用有哪些考虑维度
面对瞬时流量高峰场景,系统应该如何设计
架构设计原则
无状态化: 服务去Session化,用户状态存储到Redis/DB,便于水平扩展
异步解耦: 通过消息队列(Kafka/RocketMQ)削峰填谷,同步调用改为异步任务(如订单创建)
冗余设计: 多可用区(AZ)部署+跨地域容灾,避免单点故障
极限弹性: 云原生架构(K8s+HPA)实现秒级扩容,支持0→1000节点的快速伸缩
流量管控体系
前端: 静态资源加速、请求合并与延迟加载、客户端限流
接入层:
| 技术方案 | 作用 | 实现示例 |
| :————————— | :——————————————— | :————————————————————————- |
| 负载均衡 | 均匀分发流量到后端集群 | Nginx加权轮询/一致性哈希,AWS ALB跨AZ流量分配 |
| DNS轮询+Anycast | 分散地理区域流量 | Cloudflare全球智能路由 |
| Web防火墙 | 拦截恶意请求(CC攻击、SQL注入) | AWS WAF规则:IP黑名单、速率限制(每个IP≤100次/秒) |
| 四层/七层限流 | 控制入口流量 | Nginx限流模块:limit_req_zone
;Envoy全局限流 |后端:
- 缓存优化:多级缓存、预热热点key
- 数据库保护:读写分离、分库分表
- 降级熔断
拷打项目
- Spring框架核心点有哪些
- 有做什么优化吗
使用AI上有什么心得
总结一下自己的优势,体现在哪些方面
3.25-美团-核心本地商业-二面
拷打项目
秒杀场景针对突增流量是怎么设计的
- 前端优化
- 静态化页面:提前生成静态页面,减少服务端压力
- CDN加速:分发静态资源
- 本地缓存:商品详情等不变数据缓存在客户端
- 按钮防重复点击:点击后立即禁用按钮
- 接入层设计
- 负载均衡:多台Nginx服务器分流
- 限流措施:
- 令牌桶/漏桶算法控制请求速率
- IP/用户ID级别限流
- 恶意请求过滤:识别并拦截刷单行为
- 服务层设计
- 缓存策略:多级缓存(本地缓存+分布式缓存)、预加载库存数据到缓存
- 库存管理:采用Redis原子操作扣减库存、分段库存减少锁竞争
- 异步下单:请求进入消息队列、后台服务按处理能力消费
- 数据层设计
- 数据库保护:读写分离、分库分表、热点数据单独处理
- 最终一致性:先扣缓存库存,再异步同步到数据库、采用柔性事务
极端情况下流量很多怎么做
服务降级:
- 关闭非核心功能(如评价、推荐)
- 简化业务流程
熔断机制:
- 当系统负载达到阈值时,拒绝部分请求
- 返回友好提示而非错误页面
弹性扩容:
- 云环境下自动扩容计算资源
- 预先准备备用资源池
流量调度:
- 将部分用户引导到备用系统
- 错峰处理不同地区请求
预案准备:
- 提前准备多个应对方案
- 建立快速响应机制
假如QPS有100w,但是库存只有10w,除了分段库存还能怎么优化
答:只选择部分请求通过,限流算法
你了解的限流组件有哪些,底层的实现是怎么样的
RateLimiter:https://zhuanlan.zhihu.com/p/60979444
有没有遇到过线上的问题,比如某个时间段耗时很高
频繁full gc,如何定位问题,从哪些方面考虑
答:流量是否是高峰、内存泄漏、参数配置
什么情况会出现full gc,什么时候对象会进入到老年代
ZGC有了解吗
设计数据结构:容器可以实时显示已放入数字的中位数,时间复杂度和空间复杂度
最大堆+最小堆
时间复杂度:O(logn)
空间复杂度:O(n)
为什么说快速排序是最快的排序算法,而不是堆排
常数因子较小,分区操作简单高效,仅需比较和交换操作,且实际代码的指令数较少。
分区操作通常访问连续的内存区域(如数组首尾指针向中间移动),缓存命中率高。
设计数据结构:存储n个英文单词,当给出m个字母时,返回由这些字母组成的单词,时间复杂度和空间复杂度
全排列 + 前缀树,时间复杂度
Map<String, List<String>>
,O(m)
最左匹配原则,底层是什么样的
假如你是一个图书管理员,图书馆进了10w本书,需要你把这些书的信息录入到图书管理系统,你要怎么处理
提高并发:找人
使用OCR提取信息自动录入系统,但是需要训练模型
通过获取网站上的信息(例如电商网站)进行录入
最近有看过什么技术博客吗
arthas了解吗
平时周末会做些什么
反问:部门是信息安全部-智能决策引擎,做风险识别
3.27-蚂蚁-国际-二面
拷打项目
实习编码过程中遇到了哪些技术问题,如何解决的
线上环境如何确保代码是没有异常的,有异常如何处理
预防措施:严格测试、代码审查、预发布环境、渐进式发布
异常监控与处理
- 实时监控系统
- 应用性能监控(APM)工具(如New Relic, Datadog)
- 错误日志集中收集(如ELK, Sentry)
- 关键指标告警(错误率、响应时间等)
- 优雅降级策略
- 非核心功能故障不影响主流程
- 缓存兜底数据
- 默认值处理
- 异常处理机制
- 全局异常捕获
- 事务回滚机制
- 请求重试策略(对可重试异常)
- 熔断机制防止级联故障
异常发生后的处理流程
- 快速响应
- 告警通知到值班人员
- 根据预案初步处理
- 评估影响范围
- 问题诊断
- 收集错误日志、堆栈跟踪
- 分析监控数据
- 复现问题场景
- 恢复措施
- 热修复或回滚版本
- 扩容或重启服务
- 数据修复(如需要)
- 事后复盘
- 实时监控系统
需要设计一个监控平台,你会采购现有的还是自主设计,如果需要你去调研,哪些特征会让你选择它
采购or自主设计
- 选择采购现有方案的情况:团队规模小,监控非核心竞争力、缺乏专业的可观测性工程团队、预算充足但开发资源有限、需要快速上线,时间紧迫
- 选择自主开发的情况:监控是业务核心能力(如云服务提供商)、现有方案无法满足特殊业务需求、有足够专业团队和长期投入计划
核心能力维度
- 数据采集能力
- 支持的协议和格式、无侵入式采集能力、多语言/多框架支持、自定义指标和日志支持
- 存储与查询
- 时间序列数据库性能、日志检索效率(全文搜索、模糊查询)、数据保留策略和压缩能力、分布式存储扩展性
- 可视化分析
- 仪表板定制灵活性、预置行业模板、动态下钻分析能力、多维度数据关联
- 告警管理
- 多通道通知(邮件/SMS/钉钉/企业微信等)、告警抑制和降噪、动态阈值设置、告警依赖关系配置
非功能性维度
- 性能指标
- 单节点处理能力(EPM - Events Per Minute)、集群扩展方案、查询响应时间(百万级指标查询延迟)
- 可靠性
- 数据持久化保证、多副本机制、灾备恢复方案
- 安全性
- 数据传输加密、细粒度访问控制(RBAC)、审计日志完整性
- 可观测性成熟度
- Metrics/Logs/Traces三位一体支持、分布式追踪能力、服务拓扑自动发现
成本维度
- 定价模型
- 按主机/容器/指标量计费、数据保留周期费用、增值功能收费点
- TCO比较
- 3年总体拥有成本估算、隐性成本(培训、集成开发等)
- 厂商锁定风险
- 数据导出便利性、API开放程度、替代方案迁移成本
你在项目过程中有了解大模型相关的内容吗
你是否学习过 Spring 及业界常用的分布式组件
Spring 和 Spring Boot 的核心区别,Spring Boot 提供了哪些新的特征
自动配置、Starter 依赖、内嵌服务器、Actuator 监控、简化配置、增强的开发工具、简化的测试
学习 Spring 和 Spring Boot 过程中遇到过哪些复杂的技术问题
分布式缓存了解吗
Redis的关键机制,如何实现缓存,有哪些关键的特征
高性能、高并发
为什么Redis要使用单线程
避免锁竞争和上下文切换
内存操作很快,CPU不是瓶颈
I/O多路复用
Redis持久化机制
Redis持久化有哪些缺陷
RDB
- 数据安全性低,RDB是间隔一段时间进行持久化,若期间redis发生故障,可能发生数据丢失。
- fork进程的时候,会占用一定的内存空间。
AOF
- AOF的持久化文件比RDB大,恢复速度慢
- 写入性能开销
如何设计支付的幂等性机制
大促场景下,支付的qps非常高,如何保证支付系统的安全性
分布式架构 + 水平扩展
MQ削峰填谷 + 限流
缓存优化
例如对于5000qps,我如何知道应该部署多少台实例支持大促的洪峰
压测得到单机负载、冗余
如何预估一个系统的处理能力是怎么样的
理论计算 → 单机压测 → 依赖验证 → 全链路压测 → 优化迭代。
了解过mysql的优化机制吗,是否做过mysql的慢优化
有没有经验,哪些类型的sql语句更容易出现慢sql
- 全表扫描型查询
- 无索引或索引失效的查询:
WHERE
条件列没有索引或索引未被使用 - 使用
SELECT *
:特别是大表查询所有字段 - 隐式类型转换:如
WHERE varchar_col = 123
导致索引失效
- 复杂JOIN操作
- 多表关联(特别是大表JOIN):超过3个表的JOIN容易出问题
- 笛卡尔积查询:忘记写JOIN条件或条件不充分
- JOIN字段类型不匹配:导致无法使用索引
- 子查询和派生表
- IN/EXISTS子查询:特别是外层表大而子查询结果集大的情况
- FROM子句中的子查询:MySQL需要先物化派生表
- 相关子查询:对每行外部查询执行一次子查询
- 排序和分组操作
- 无索引的ORDER BY:特别是大结果集的排序
- GROUP BY无索引列:需要临时表和排序
- DISTINCT大结果集:去重操作消耗资源
- 分页查询问题
- 大偏移量LIMIT:如
LIMIT 100000, 20
- 错误的分页写法:未优化COUNT(*)查询
- 函数操作列
- 对索引列使用函数:如
WHERE DATE(create_time) = '2023-01-01'
- 计算列查询:如
WHERE price*2 > 100
- 锁相关慢查询
- 长时间运行的事务:持有锁时间过长
- 锁等待:被其他事务阻塞
- 其他高风险操作
- 大表UPDATE/DELETE:无合适索引的单条操作影响多行
- 大批量INSERT:未使用批量插入或LOAD DATA
有哪些典型的慢SQL,哪些写法可能天然就是一个慢SQL
请以你了解的一种RPC框架,解释系统间通讯基于RPC和基于HTTP的异同点是什么
| 特性 | Dubbo RPC | HTTP通信 |
| :——————- | :——————————————- | :—————————————- |
| 协议设计 | 二进制私有协议(默认dubbo协议) | 文本协议(HTTP/1.1等) |
| 序列化效率 | 高效二进制序列化(hessian2等) | 文本序列化(JSON/XML) |
| 服务发现 | 内置服务注册中心(如Zookeeper) | 通常依赖外部DNS或网关 |
| 调用方式 | 透明化远程调用(像本地方法调用) | 显式的请求构造和解析 |
| 性能 | 更高(节省头部开销,二进制编码) | 相对较低(头部冗余,文本解析) |
| 适用场景 | 内部高性能服务调用 | 对外API或异构系统集成 |为什么企业要包装一层使用RPC,而不是直接使用HTTP,有哪些好处
通信效率提升、序列化优化、透明化远程调用…
你有使用过哪些RPC
找实习你更关注哪些方面,希望获得哪些东西
除了技术更看重哪些方面
你在个人学习中是如何学习技术的,大概保持什么样的频率
3.28-快手-一面
- HashMap底层结构和工作原理
- HashMap扩容机制
- HashTable和ConcurrentHashMap区别
- synchronized修饰普通方法和静态方法的区别
- JVM垃圾回收过程
- MySQL事务隔离级别
- 串行化实现原理
- MCVV原理
- Spring Bean生命周期
- Redis内存淘汰机制
- 手撕:LRU
- 手撕:编辑距离
3.29-阿里云-齐天-三面
拷打项目
- 如何保证OCR提取的内容是正确的
- 滑动窗口
Spring AOP怎么实现的
AspectJ怎么实现的,利用了Java什么原理
Spring事务和Mybatis事务管理上的区别
- 事务管理层次
- Spring事务管理:
- 工作在业务逻辑层(Service层)
- 提供声明式事务(@Transactional)和编程式事务(TransactionTemplate)
- 可以管理多种数据访问技术(JDBC、Hibernate、MyBatis等)
- MyBatis事务管理:
- 工作在持久层(Dao层)
- 主要提供编程式事务(SqlSession.commit()/rollback())
- 仅管理MyBatis自身的数据库操作
- 事务控制方式
- Spring事务:
- 通过AOP实现
- 支持丰富的事务传播行为(PROPAGATION_REQUIRED, PROPAGATION_REQUIRES_NEW等)
- 支持事务隔离级别配置
- 支持回滚规则配置
- MyBatis事务:
- 基于SqlSession实现
- 默认自动提交(false),需要手动控制
- 传播行为简单,不支持复杂场景
- 隔离级别依赖数据库默认设置
- 集成与协作
- 单独使用MyBatis:
- 需要手动获取SqlSession
- 通过SqlSessionFactory.openSession()获取会话
- 显式调用commit()/rollback()
- Spring集成MyBatis:
- Spring管理SqlSession的创建和释放
- 通过Spring事务管理器(如DataSourceTransactionManager)统一管理
- MyBatis事务被Spring事务包装
Spring事务传递和事务只在方法内部要怎么做
1
2
3
4
5
6
7
8
9
10@Transactional(propagation = Propagation.REQUIRED) // 默认值
public void method() {
// 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void independentTransactionMethod() {
// 此方法总是启动新事务,不受外部事务影响
// 如果已有事务,则挂起当前事务,创建新事务
}MySQL出现死锁怎么排查
查看最近死锁信息:
SHOW ENGINE INNODB STATUS;
介绍一下红黑树,Java什么数据结构用到了,Linux哪里用到了
3.29-拼多多-一面
拷打实习
- 线程池怎么设置的
- EventBus
- 限流组件
手撕:双线程交替打印0-100
手撕:三线程交替打印0-100
OSI 7层模型
SYN攻击
假设攻击者短时间伪造不同IP地址的SYN报文,服务端每接收到一个SYN报文,就进入SYN_RCVD状态,但服务端发送出去的ACK+SYN报文,无法得到未知IP主机的ACK应答,久而久之就会占满服务端的半连接队列,使得服务端不能为正常用户服务。
哲学家就餐问题
死锁的四个必要条件,怎么解决
迪杰斯特拉最短路径算法
两个文件,分别有100亿URL,内存只有1G,怎么取交集
ZSET是大key,怎么删除
渐进式删除,使用
zremrangebyrank
命令,每次删除 top 100个元素。ZSCAN:
ZSCAN
命令通过游标方式分批获取 ZSET 成员,配合ZREM
实现渐进式删除,避免一次性操作造成 Redis 阻塞。- UNLINK异步删除
HashSet如何实现去重的
线上SQL执行比较慢,怎么排查
EXPLAIN关注哪些指标
4.1-饿了么-一面
- 拷打实习
- 有没有碰到线上问题
- 模型识别结果出错有处理吗
- websocket语音转文字
- Redis限流,技术选型
- 有哪些可完善的地方
- 流量提高后,负载均衡、应用层、db层怎么优化
- DB扩展可以怎么做
- 分库分表怎么做,分的时候需要注意哪些点,分完会有哪些副作用
- Redis缓存三剑客
- 数据一致性怎么保证的
- 事务、索引使用上会关注哪些
- 建立索引要注意哪些
- MySQL隔离级别
- 可重复读有哪些问题
- 拷打项目
- 难点
- 单体和微服务的区别,优缺点
- 使用微服务架构哪些成本会比较高
- 搭过微服务吗,有没有遇到什么问题
- java比起其他语言哪些特性比较突出
- java垃圾回收机制,不同版本默认的垃圾回收器
- java内存溢出的情况
- 实习更看重哪些
- 大模型有了解过吗,会对java开发有哪些影响
4.1-拼多多-二面
- 拷打实习
- 双token刷新
- websocket和http的区别
- 能使用http模拟websocket的效果吗
- 哪些场景会用到websocket,聊天室是怎么实现的
- Redis限流
- Redis是个什么样的数据库
- Redis过期策略
- Redis数据结构
- Redis数据类型适用场景
- Redis持久化
- 拷打项目
- 设计模式了解哪些
- 手撕:单例模式
- MySQL索引介绍一下
- 实际开发中用到的索引、事务
- 联合索引怎么用、底层
- 手撕:合并k个有序链表,时间复杂度
- git了解哪些命令,cherry-pick、rebase了解吗
- linux命令了解哪些,都做过什么
- docker底层实现了解吗,怎么做的容器隔离
4.3-字节-飞书-一面
拷打实习
Redis限流
zset底层结构
跳表是个什么样的结构,查询过程
库存防超卖,分段锁的必要性
Redis分布式锁注意事项
什么时机会触发续期操作,判断条件是什么
每 过期时间/3 会执行一次续期
看门狗线程作为守护线程运行,会随用户线程终止而自动结束
为什么不设置一个时间比较长的加锁时间
Spring的理解,循环依赖怎么解决的
代理对象怎么理解,为什么用二级缓存解决不了
使用其他名称来指代代理对象,可以用二级缓存解决吗
MySQL有哪几种索引
b+树有什么特点
什么场景、怎么建索引
MySQL是怎么选择要使用哪个索引
EXPLAIN使用
手撕SQL: 查询每门课程成绩最高的人,要求返回课程名称、人名、成绩
t_student: sid, sname
t_course: cid, cname
t_score: sid, cid, score
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20SELECT
c.cname AS 课程名称,
s.sname AS 学生姓名,
sc.score AS 最高成绩
FROM
t_score sc
JOIN
t_student s ON sc.sid = s.sid
JOIN
t_course c ON sc.cid = c.cid
WHERE
(sc.cid, sc.score) IN (
SELECT
cid,
MAX(score)
FROM
t_score
GROUP BY
cid
);手撕:字符串乘法
4.7-阿里云-HR面
- 自我介绍、绩点、排名
- 拷打项目
- 团队分工
- 项目难点
- 对未来工作的公司、类型有没有规划
- 城市选择
- 实习的成长
- 如何推动他人做事情
- 做过哪些关键的选择
- 性格受谁影响比较大
- 性格怎么样,如何应对压力
4.9-字节-飞书-二面
拷打实习
多租户怎么实现的
租户ID是在哪里,怎么校验的
为什么使用RBAC,而不是ABAC,MAC,DAC
双token刷新,token保存了哪些属性(访问权限?登录态?)
后端有没有类似session的机制,为什么要存储两份
token用户端存储在哪
如果token被盗会发生什么,怎么防止被盗
有了解过前后端交互的攻击场景吗,CSRF之类的
怎么防止CSRF
- 验证用户会话:在服务器端对用户会话进行验证,确保请求的会话标识符与当前会话标识符匹配。这样可以防止攻击者伪造会话标识符。
- 使用双重验证:除了会话验证,还可以使用其他验证方式,例如验证码、签名验证等。这些验证方式可以增加攻击的难度。
- 防止跨站请求:通过设置CSP(内容安全策略)来防止跨站请求,限制网页中可执行的脚本源,减少攻击者诱导用户执行恶意操作的可能性。
- 避免使用自动提交表单:禁用默认的自动提交功能,要求用户在提交表单前确认操作,防止攻击者诱导用户在未经授权的情况下提交表单。
- 强制Referer头部:在服务器端检查请求的Referer头部,确保请求来自可信来源。
拷打项目
- 如何保证库存和奖品的一致性,即扣了库存一定会发奖
- Redis扣库存和落Task表是怎么保证一致性的
- Redis和db库存是怎么保证最终一致性
- 如果Redis宕机怎么办
- 库存是怎么设计的,如何支撑高并发,有用过压测工具测试过吗
- 如果限制用户每天只能抽三次奖,怎么实现
- 系统性能会不会因为读db而收到限制
HTTPS怎么保证安全
HTTPS会被第三方窃听或劫持吗
Spring @Autowired怎么实现
默认Spring注入的Bean是线程安全的吗
如果希望Bean的一个属性被共享,怎么变成线程安全的
使用ThreadLocal会有什么坑吗
Spring提供服务的Handler线程池是复用的还是每次new新的
用volatile会变成线程安全吗,能保证原子性、一致性吗
锁升级
Spring事务,A方法调用B方法,如何只回滚B而不回滚A
平常用的java什么版本,有哪些新特性
虚拟线程的理解,和普通的线程、进程有什么区别
做项目的过程中有遇到过印象比较深、有挑战的问题吗,怎么解决的
手撕:括号生成
4.10-字节-飞书-三面
- 拷打实习
- 前端如何保存token
- 是否存在被xss的风险,xss的原理
- 为什么使用双token而不是cookie
- 解释下CSRF原理,不同域名不会携带cookie
- 了解OAuth吗
- SSE的原理,和websock传输有什么区别
- Redis滑动窗口,还有哪些限流机制
- 拷打项目
- 有哪些特色的设计方案
- 扣库存逻辑
- 用户抽奖次数限制怎么实现,有没有做过并发测试
- 场景:一个订单用户支付成功后,订单状态要更新并且要发一个MQ消息,状态和MQ如何保证最终一致性
- 手撕:给一个字符串“123456789”,在任意一位数字前插入“-”或”+“,要求表达式最终计算结果为100,返回所有可能的表达式
4.10-拼多多-三面
- 询问基本情况
- 拷打实习
- RBAC介绍
- 双token介绍,为什么要用双token
- 多租户怎么实现的
4.11-字节-飞书-HR面
- 自我介绍
- 实习的目标、期待是什么
- 什么样的成果觉得ok,有没有衡量标准
- 花了很多精力但是结果不理想,你怎么考虑
- 期望在哪些维度有成长
- 技术层面上有哪些地方不太擅长
- 实习有哪些帮助
- 上线项目和自己项目有哪些区别,对你比较大的指引是什么
- 本科成绩,学习上有什么心得
- 和前5%的学生差距在哪里
- 参加过什么比赛吗
- 如何学习新领域
- 过往感觉压力最大、最低谷的事情是什么
- 过往有没有放弃,现在感觉遗憾的事情
- 设置过的最有挑战性的目标
- 最有成就感的事情
- 喜欢技术吗
- 做事的驱动点是什么
- 一到三年的规划
- 和同事方案有分歧要怎么做
- 如果项目使用的B方案,你想到了A方案,会怎么做
- 对前三面试官的感觉怎么样
4.18-拼多多-HR面
主要了解个人情况
4.21-蚂蚁-信贷-一面
- Spring Bean生命周期
- 类、静态类、对象的关系和使用方式
- static什么时候使用
- final什么场景使用
- 拷打项目
- 解决超卖
- 为什么用Redis
- Redis为什么性能好
- Redis常见数据类型
- Redis内存淘汰
- LRU的原理
- 拷打实习
- 有没有使用AI工具
- 线程池拒绝策略
- 线程池参数作用
- 除了EventBus有没有考虑过使用别的解耦手段
- 为什么用WebSocket而不是轮询
- 三次握手,为什么不是二、四次握手
- 业务:花呗借呗的一些决策上的工程平台,处理额度之类的
阿里云面试分析(wf)
- CPU占用比较高有哪些情况
感觉还是应该先正面回答一下面试官的问题,后面作为补充可以说一下实习的经历。比如可以说实习中是什么样的,可能没有这种情况,但是如果我遇到了我会按哪些方式排查,大概有哪几种情况
可能的回答:先使用top查看进程CPU占用情况,找到CPU占用最高进程后再用ps查看线程详情。
如果是java进程,用jstack获取到线程堆栈信息
- 内部:死循环、锁竞争、频繁full gc
- 外部:突发流量
如果是数据库
- 慢查询
上面这些可能就是会啥说啥,面试官可能会就其中几个点继续追问
- 内存占用比较高有哪些情况,怎么排查
首先有一些内存监控工具,比如Prometheus + Grafana、arthas、VisualVM
可能情况包括内存泄漏、突发流量、加载大量数据
发生了OOM打印一下内存快照,然后使用各种内存分析工具进行分析,再定位到代码中的问题
限流怎么实现
其他限流算法
固定窗口、滑动窗口、漏桶、令牌桶
ThreadLocal底层实现
SpringCloud GateWay介绍
微服务有哪些优缺点
可以讲一下微服务各自可以使用不同语言实现,只需要使用同一种协议交互
- 怎么过滤恶意ip段
除了转化为数字,我感觉还有前缀树?布隆过滤器(允许误判的话)?
单个IP段转成数字怎么比对
能不能用二分的思路
java垃圾回收
java判断对象存活
static变量会被回收吗
类对象被回收的话
双亲委派机制
动态代理
CGLIB底层实现
asm框架,通过操作字节码实现,但这个应该算是扩展问题
三次握手
进程线程区别
进程间的通信方式
SQL注入
修复SQL注入
手撕:力扣227简化版
其他
什么是慢查询,如何定位慢查询
慢查询(Slow Query)是指执行时间超过预期阈值的数据库查询操作。这类查询可能会消耗大量数据库资源(如CPU、内存、磁盘I/O),导致数据库性能下降,甚至影响整个系统的稳定性。
启用慢查询日志,使用
mysqldumpslow
分析慢查询日志- 增删查改
- 对于插入可以把多个语句合并成一个语句批量处理。
- 对于删除,因为删除是假删除(减少B+树合并访问磁盘的开销),如果有太多地方没有使用,B+树层数虚高,增加了访问磁盘的速度,并且全表扫描也会扫描到很多无用数据,可以在数据库空闲的时候通过alter table来重建表使数据排列更紧凑
- 对于频繁修改的数据
- 利用最左匹配原则减少索引数量
- 如果有唯一索引可以考虑改成普通索引,避免修改时为了维护唯一性导致change buffer失效。
- 对于查询:
- 使用缓存, 早期mysql开启查询缓存,mysql8.0没有查询缓存,业务层可以使用缓存例如redis;或者可以把一些特殊的语句定期执行然后保存,后面查保存的数据,不用执行sql,比如我之前做的一个预测成本的功能,每月只需要预测一次,那就可以写一个定时任务每个月定时预测一次保存到另一张表,后续查询直接查这张表就可以了,就不用执行复杂SQL了。
- 对于大数据量的场景,可以读写分离,分库分表
- EXPLAIN分析SQL语句的执行计划,主要关注以下几个字段
- 如果
type
为ALL
,说明进行了全表扫描,考虑是否可以通过增加索引来优化。- All(全表扫描);index(全索引扫描);range(索引范围扫描);ref(非唯一索引扫描);eq_ref(唯一索引|扫描);const(结果只有一条的主键或唯一索引扫描)
- 分析
possible_keys (可能使用到的索引)
和key(实际使用的索引)
,确保相关的列上建立适当的索引并且正确选取索引。 - 使用覆盖索引来避免回表
- 使用复合索引来提高多条件查询的性能(索引下推)。
- 利用最左匹配原则尽可能的建立更少的索引
- 分析有没有没有正确选取索引
- 可能会错误的使用全表扫描的场景
- 对字段使用了函数:将函数写在判断条件上面,避免对字段使用函数。
- 字段隐式类型转换(str->int, 字段使用了utf8但是字符是utf8mb4),需要保证查询目标与字段类型一致
- 如果没有出现上述问题,还有一种解决思路:使用force index强制使用索引 | order by .. limit 1。
- 因为选取索引是优化器的工作,优化器会分析选取索引的扫描行数加上回表的代价是否比主键全表扫描少,这里采用采样分析,因为全表分析代价太大,在多个事务的时候,因为是假删除而且多个事务的时候MVCC多版本数据在undo-log里面,这个时候采样分析会把已经删除的数据也考虑到总量里面去,比如实际上总量是1000行,考虑成了2000行导致考虑的扫描行数翻倍。所以采样分析针对高并发和大数量的场景是非常不准的
- MySQL临时表, CTE会破坏索引结构(例如group后使用了临时表):想办法优化掉临时表,或者减少临时表的查询、JOIN操作
rows
字段,表示查询的结果集行数。我们要尽可能的减少rows的数量,以下是一些思路- 确保查询条件尽可能具体, 例如在WHERE子句中使用更严格的条件。
- 对于确定的数量(例如只需要查询一个结果)使用limit
- in 替换成 exists,in 是 双重匹配,exists匹配到了一个后就会提前返回
- count字段看可不可以替换成count( * ), count(1)
- Extra字段,记录一些额外信息
- 如果有Using filesort,表示使用了文件排序。
- 可以考虑给需要排序的字段加上索引,因为索引使用的B+树本来就是排序好的,可以减少排序时间。 或者可以把单次排序的内存
sort_buffer_size
设置大一点,因为排序是取磁盘里的部分数据到内存进行排序最后合并, 把单次排序内存设置大一点这样减少IO次数 - 如果有Using Join Buffer, 说明Join没有使用索引。没有索引join会用到Block Nested-Loop Join算法,时间复杂度很高,可以看作两层遍历,实际上更复杂一点,考虑到数据很多不能全读到内存里,mysql使用了join_buffer来存一部分数据,可能会因为 join_buffer 不够大,需要对被驱动表做多次全表扫描。
- 在需要Join的字段加上索引
- 如果
- 增删查改
Redis中hash结构如何实现
https://www.xiaolincoding.com/redis/data_struct/data_struct.html#%E5%93%88%E5%B8%8C%E8%A1%A8
使用listpack,哈希表实现
哈希表是一个数组(dictEntry** table),数组的每个元素是一个指向「哈希表节点(dictEntry)」的指针。
使用链式哈希解决哈希冲突
rehash过程
- 给「哈希表2」分配空间,一般会比「哈希表1」大一倍
- 将「哈希表1」的数据迁移到「哈希表2」中
- 迁移完成后,「哈希表1」的空间会被释放,并把「哈希表2」设置为「哈希表1」,然后在「哈希表2」新创建一个空白的哈希表,为下次rehash做准备
如果「哈希表1」的数据量非常大,那么在迁移至「哈希表2」的时候,因为会涉及大量的数据拷贝,此时可能会对Redis造成阻塞,无法服务其他请求。
渐进式rehash
- 给「哈希表2」分配空间
- 在rehash进行期间,每次哈希表元素进行新增、删除、查找或者更新操作时,Redis除了会执行对应的操作之外,还会顺序将「哈希表1」中索引位置上的所有key-value迁移到「哈希表2」上
- 随着处理客户端发起的哈希表操作请求数量越多,最终在某个时间点会把「哈希表1」的所有key-value迁移到「哈希表2」,从而完成rehash操作。
触发rehash操作的条件,主要有两个:
- 当负载因子大于等于1,并且Redis没有在执行bgsave命令或者bgrewiteaof命令,也就是没有执行RDB快照或没有进行AOF重写的时候,就会进行rehash操作。
- 当负载因子大于等于5时,此时说明哈希冲突非常严重了,不管有没有有在执行RDB快照或AOF重写,都会强制进行rehash操作。
set和bitmap如何实现点赞和签到
点赞:key 是文章id,value 是用户id
SADD article:1 uid:1
签到:假设我们要统计ID 100的用户在2022年6月份的签到情况
SETBIT uid:sign:100:202206 2 1
(2是偏移量,代表3号签到)BITCOUNT uid:sign:100:202206
RabbitMQ保证消息的可靠性
不丢失:
生产者到 RabbitMQ:事务机制和 Confirm 机制,注意:事务机制和 Confirm 机制是互斥的,两者不能共存,会导致 RabbitMQ 报错。
RabbitMQ 自身:持久化、集群、普通模式、镜像模式。
RabbitMQ 到消费者:消费者确认机制,失败重试机制
不重复:
幂等性:唯一ID、业务状态判断
兜底机制:定时任务扫描状态未完成的任务
innodb的自增id是怎么实现的
https://www.xiaolincoding.com/mysql/lock/mysql_lock.html#auto-inc-%E9%94%81
epoll过程
- 服务器调用 listen(fd, backlog) 监听端口,全连接队列初始化。
- 服务器调用 epoll_create() 创建 epoll 实例。
- 服务器调用 epoll_ctl(EPOLL_CTL_ADD, listen_fd, EPOLLIN) 将监听socket加入epoll。
- 客户端发起连接,完成三次握手,内核将连接放入全连接队列。
- epoll_wait() 返回,通知应用程序 listen_fd 可读(有新连接)。
- 服务器调用 accept() 从全连接队列取出连接,得到 conn_fd。
- 服务器调用 epoll_ctl(EPOLL_CTL_ADD, conn_fd, EPOLLIN) 将 conn_fd 加入 epoll,监听数据到达。
- 客户端发送数据,epoll_wait() 返回 conn_fd 可读,服务器调用 read() 读取数据。