1. MySQL 事务机制(阿里巴巴)
它确保了一组数据库操作要么全部成功执行,要么全部失败回滚。
事务特性:
- 原子性:把多个操作打包成一个整体。(要么全都做,要么都不做)
- 一致性:事务执行前后,数据库从一个一致状态转换到另一个一致状态,数据完整性约束不被破坏。
- 隔离性:多个事务并发执行时,彼此的操作互不干扰,每个事务都感觉不到其他事务的存在。
- 持久性:事务一旦提交,其修改会永久保存到数据库中,即使系统崩溃也不会丢失。
2. 有做过 SQL 优化的实现么(阿里巴巴)
- 优化联表查询,使用 JOIN
-- 优化前
SELECT name FROM users WHERE id IN (SELECT user_id FROM orders WHERE status = 1);
-- 优化后
SELECT u.name FROM users u JOIN orders o ON u.id = o.user_id WHERE o.status = 1;
- 避免使用
SELECT *,只查询自己需要的字段
-- 优化前
SELECT * FROM users WHERE age > 30;
-- 优化后
SELECT id, name, email FROM users WHERE age > 30;
- 为 WHERE、JOIN、ORDER BY 字段建立索引
-- 为查询条件字段建索引
CREATE INDEX idx_user_age ON users(age);
-- 为 JOIN 关联字段建索引
CREATE INDEX idx_orders_user_id ON orders(user_id);
-- 为排序字段建索引
CREATE INDEX idx_articles_create_time ON articles(create_time);
3. Nacos 底层是如何实现注册中心功能的(阿里巴巴)
其底层实现核心围绕服务注册、服务发现、健康检查三大核心功能展开。
服务注册机制
服务提供者启动时,通过 Nacos 客户端向 Nacos Server 发送注册请求,携带服务名、IP、端口、权重、健康检查参数等元数据。Nacos Server 接收到请求后,将服务信息存储在内存注册表中。
服务发现机制
- 服务消费者启动时:主动从 Nacos Server 拉取目标服务的实例列表,并缓存到本地。
- 服务变更时:Nacos Server 主动推送变更通知给消费者(减少轮询开销),消费者收到通知后再主动拉取最新实例列表。
健康检查机制
Nacos Server 会定期向服务实例发送健康检查请求,若实例连续失败,则标记为不健康,暂时从可用实例列表中移除;也可以是服务实例定期向 Nacos Server 发送健康心跳包。
4. RocketMQ 如何持久化(阿里巴巴)
RocketMQ 实现持久化依靠交换器的持久化、队列的持久化和消息的持久化。
5. 介绍一下 WebSocket(阿里巴巴)
WebSocket 是一种协议,用于在客户端和服务器之间建立持久的双向通信连接,广泛应用于需要实时数据交换的应用程序。
核心特点:
- 持久连接:一旦建立连接,客户端和服务器保持长期通信状态,无需频繁创建和关闭连接。
- 双向通信:服务器可以主动向客户端推送数据,客户端也可以随时向服务器发送数据,打破了 HTTP 中只能由客户端发起请求的限制。
- 低开销:连接建立后,数据传输时无需携带大量 HTTP 头部信息,减少带宽消耗。
6. 如何判断是 HTTP 长连接还是短连接,怎么设置长连接(阿里巴巴)
在 HTTP 协议中,长连接和短连接的核心区别在于一次 TCP 连接是否可以被多次 HTTP 请求复用,可以通过协议版本和请求头字段来判断。
- HTTP/1.0:默认是短连接。
- HTTP/1.1:默认是长连接。
通过查看响应头中 Connection,可以查看是什么连接,如果没有则按照默认值处理。
设置长连接,即改变响应头中的 Connection 字段。
7. HashMap 的实现原理(快手)
HashMap 的底层结构是数组(桶数组)+ 链表 + 红黑树的组合。
- 首先对键的 hashcode 进行高低位异或,混合高低位信息,减少哈希冲突(因为哈希表的下标是通过哈希值 & (数组长度 - 1) 计算的,如果只用低位,高位未参与,导致冲突可能性高)。
- 然后对桶索引进行计算(哈希值 & (数组长度 - 1),等价于哈希值 % 数组长度,此处使用位运算是因为哈希表对性能要求很高)。需要保证桶数组的长度必须是 2 的幂次方(创建哈希表时会处理传入参数,使得桶数组长度一定是 2 的幂次方且大于等于传入参数)。
- 插入数据时采取链表 + 红黑树的组合结构解决哈希冲突,并通过扩容机制优化插入查找效率。
- 插入键值对:如果桶为空,直接在该桶插入新节点;如果非空,若桶中是红黑树,按红黑树规则插入;若桶中是链表,遍历链表,找到相等的键则覆盖值,否则插入链表尾部。插入后若链表长度≥8,将链表转为红黑树。
- 扩容判断:若元素总数超过阈值(capacity * loadFactor,负载因子默认 0.75),则执行扩容操作,桶数组长度扩容为原来的 2 倍,并重新计算所有键的索引。
- 查询键值对:计算键的哈希值和桶索引,若桶中是红黑树,按红黑树规则查询;若桶中是链表,遍历链表查找相等的键,返回对应值(未找到则返回 null)。
- 对比 JDK 1.7,JDK 1.8 进行了一些改变:首先扩容的时候,JDK 1.8 对扩容进行了优化,通过
(e.hash & oldCap)判断节点是否需要迁移到新桶,减少重哈希开销;其次在插入键值对的时候,JDK 1.7 采取头插法,而 JDK 1.8 采用的是尾插法,这样做的好处是可以避免多线程扩容死循环。

8. HashMap 承载的元素越来越少,什么时候会退化成链表,为什么两者设置的这个值不对称(快手)
当 HashMap 中红黑树的节点数≤6 时,会退化为链表。
两者阈值设置不对称(8→树、6→链)是为了临界防抖动——避免节点数在阈值附近波动时(如 7→8→7)导致链表与红黑树频繁转换,减少性能开销。
9. MySQL 和 Redis 的一致性怎么保证的(快手)
1. 最终一致方案
- 读取数据时,先查 Redis;若 Redis 有数据,直接返回。
- 若 Redis 无数据,查 MySQL,将结果写入 Redis 后返回。
- 更新数据时,先更新 MySQL,再删除 Redis 中的旧缓存(而非直接更新 Redis)。
关键原因:删除缓存可避免'更新 MySQL 和更新 Redis'的原子性问题,后续读取时会自动从 MySQL 加载新数据到 Redis,实现最终一致。
2. 强一致方案(分布式锁 + 原子操作)
- 获取分布式锁,确保同一时间只有一个线程操作该数据。
- 开启 MySQL 事务,更新 MySQL 数据。
- 事务提交成功后,立即更新或删除 Redis 缓存。
- 释放分布式锁。
关键原因:分布式锁避免了并发更新导致的'数据覆盖'问题,MySQL 事务确保了数据更新的可靠性,两者结合实现强一致。
10. 数据库有哪些隔离级别,默认的隔离级别是什么(快手)
- 读未提交:一个事务可以读取另一个事务未提交的数据,一致性最低。
- 读已提交:一个事务只能读取另一个事务已提交的数据,避免脏读。
- 可重复读:同一事务内多次读取同一数据,结果始终一致,避免不可重复读。
- 串行化:事务完全串行执行,避免所有并发问题,一致性最高。
MySQL:默认隔离级别是可重复读。 SQL Server:默认隔离级别是读已提交。
补充概念:
- 脏读:事务 A 修改了数据但未提交,事务 B 读取了该未提交数据,之后事务 A 回滚,事务 B 读取的是无效数据。
- 不可重复读:事务 A 内多次读取同一数据,事务 B 在期间修改并提交了该数据,导致事务 A 多次读取结果不一致。
- 幻读:事务 A 内多次执行同一查询(如统计记录数),事务 B 在期间插入/删除了符合条件的记录,导致事务 A 多次查询结果行数不一致。
11. 缓存击穿、穿透、雪崩如何解决(快手)
概念:
- 缓存击穿:某个热点 Key 失效,请求穿透到 DB。
- 缓存穿透:请求不存在的 Key,持续穿透到 DB。
- 缓存雪崩:大量 Key 同时过期 / Redis 宕机,请求全量穿透。
解决方法:
- 缓存穿透:
- 使用布隆过滤器先判定 key 是否存在,再真正查询。
- 针对数据库上也不存在的 key,也存储到 Redis 中,比如 value 就随便设成"",避免后续频繁访问数据库。
- 缓存雪崩:不给 key 设置过期时间或者设置过期时间的时候添加随机时间因子。
- 缓存击穿:热点 key 永不过期。
知识补充:什么是布隆过滤器
布隆过滤器(Bloom Filter)是一种空间效率极高的概率型数据结构,核心作用是快速判断'一个元素是否存在于集合中',能有效解决缓存穿透问题,但存在极小的'假阳性'(误判元素存在),且不支持删除操作。
12. 当 Redis 作为库存存储也扛不住大量请求时,如何处理?(快手)
1. 使用 Redis 集群,提高承载能力(横向扩容 + 读写分离)
- 分片部署:每个节点只存储部分商品的库存,分散单节点压力。
- 读写分离:读操作走从节点,写操作走主节点。
2. 限流:前端限流,网关(nginx, gateway)限流,使用 MQ 进行流量控制。
3. 熔断降级:用 Sentinel (哨兵) 监控 Redis 的响应时间(如连续 5 次超时),触发熔断。
- 读请求:直接返回本地缓存的旧值。
- 写请求:将扣减请求暂存到本地队列(如内存队列),Redis 恢复后异步同步。
13. sleep 和 wait 区别(阿里巴巴)
- 使用前提:sleep 无需持有锁;wait 必须拥有当前对象的锁。
- 对锁影响:sleep 不会释放当前持有的锁(若持有锁,仍保持);wait 会主动释放持有的对象锁,让其他线程可获取该锁。
- 唤醒方式:sleep 时间到后自动唤醒;或被
interrupt()中断;wait 需被其他线程调用notify()唤醒;或等待超时;或被interrupt()中断。 - 用途:sleep 目的是用来阻塞线程;wait 目的是为了等待其他线程唤醒的。
14. RabbitMQ 是如何保证消息不丢失的(快手)
- 生产者到 RabbitMQ 服务器:开启发送方确认(confirm 模式保证生产者->exchange 和 return 模式保证 exchange->queue)。
- 中间件 MQ 保存消息:持久性(队列持久性,交换器持久性,消息持久性),确保发生故障重启后消息不丢失。
- 消息消费环节:确保消息被正确处理不丢失。
- 消息确认机制:关闭自动确认,使用手动确认,并配置消息重试次数与死信队列。
内容补充:死信队列
死信 (dead message) 简单理解就是因为种种原因,无法被消费的信息,就是死信。有死信,自然就有死信队列。当消息在一个队列中变成死信之后,它能被重新发送到另一个交换器中,这个交换器就是 DLX (Dead Letter Exchange),绑定 DLX 的队列,就称为死信队列 (Dead Letter Queue,简称 DLQ)。
消息变成死信一般是由于以下几种情况:
- 消息被拒绝(Basic.Reject/Basic.Nack),并且设置 requeue 参数为 false。
- 消息过期。
- 队列达到最大长度。
死信队列的场景应用:
- 消息重试:将死信消息重新发送到原队列或另一个队列进行重试处理。(配合 TTL 特性可以实现延迟队列的效果(有瑕疵))
- 消息丢弃:直接丢弃这些无法处理的消息,以避免它们占用系统资源。
- 日志收集:将死信消息作为日志收集起来,用于后续分析和问题定位。
15. 算法题:LeetCode 23 合并两个升序链表(快手)
这道题乍一看去挺简单的,还是 LeetCode 原题。
如果你是第一次做这道题你或许会想到像 21. 合并两个有序链表 这道题,首先想着用暴力的方法去俩俩合并,但是如果我们仔细一想,似乎这样的话,时间复杂度有点高了,我们每次都要对 k 个链表进行比较,时间复杂度是 O(k^2*n),所以我们想着去优化一下算法,我们可以想到使用优先级队列(堆),然后排小根堆的方式,将每次最小的节点取出,然后将节点的下一个存入。


16. 讲一下 Redis replication 的原理(快手二面)
Redis Replication(主从复制)通过将主节点(Master)的数据同步到从节点(Slave),实现数据冗余备份和读请求分流。
- 主节点(Master):接收写请求,负责将数据同步到从节点;可同时拥有多个从节点。
- 从节点(Slave):只读节点,不处理写请求(默认配置)。
原理:
- 保存主节点(master)的信息(ip 地址,端口号)。
- 当从节点发现存在新的主节点后,会尝试与主节点建立基于 TCP 的网络连接(验证读写功能是否正常)。
- 同步数据集:对于首次建立复制的场景,主节点会把当前持有的所有数据全部发送给从节点,这步操作基本是耗时最长的,所以又划分成两种情况:全量同步和部分同步。
- 命令持续复制:当从节点复制了主节点的所有数据之后,针对之后的修改命令,主节点会持续的把命令发送给从节点,从节点执行修改命令,保证主从数据的一致性。
17. Redis 的哪些数据结构是你比较了解的,可以应用到哪些场景(快手)
- String(字符串):应用:缓存热点数据,分布式锁,计数器。
- Hash(哈希):[key field value],应用:存储对象数据,购物车。
- List(列表):应用:消息队列。
- Set(集合):应用:去重,社交场景 - 共同好友/共同关注,抖音推荐。
- ZSet(有序集合):应用:排行榜,延迟任务。
18. 讲一讲 SQL 优化,加了索引之后还很慢会是什么原因,扫描非常多行会 (快手)
SQL 加了索引后仍很慢,且扫描行数多,核心原因是索引未被有效使用或索引设计不合理。
核心原因 1:索引失效(索引存在但没被用上)
- 索引列参与函数/表达式运算。
- 索引列使用不等于(!=、<>)、NOT IN、IS NOT NULL。
核心原因 2:索引设计缺陷(索引能用,但效率低)
- 索引选择性差(索引列重复值多)。
- 索引列是大字段(如 TEXT、VARCHAR (2000))。
19. RabbitMQ 如何在业务中保证消息的顺序消息 (快手二面)
RabbitMQ 的顺序性分为局部顺序性和全局顺序性。
全局顺序性很难保障而且使用场景很少,我们可以使用连续的序号和消费者缓冲区来实现,通过序号在消费者缓冲区来排列实现有序。
那么局部有序就比较好保障,我们可以使用单队列单消费者 + 分区消费解决。
基础方案:单队列单消费者 —— RabbitMQ 原生保障局部有序
RabbitMQ 的单个队列天然具备 FIFO(先进先出)特性:生产者向队列发送消息时,消息会按发送顺序依次存入队列;消费者从队列获取消息时,会按队列存储顺序依次消费(默认 prefetch=1 时,处理完一条再取下一条)。
优化方案:分区消费 —— 用'ID 分组'提升吞吐量,本质仍是单队列单消费者
第一步:引入'分组 ID',按 ID 分区路由
例:用 userID % 3 将用户消息分为 3 组,userID=1、4、7 的消息路由到队列 Q1,userID=2、5、8 的路由到 Q2,userID=3、6、9 的路由到 Q3。
第二步:每个分区队列配专属消费者
为 Q1、Q2、Q3 分别分配一个消费者(或一个单线程消费池),每个消费者只处理对应队列的消息。
20. Linux 查看日志的命令(快手二面)
grep [选项] "要搜索的字符串" 文件名
常用选项:
-i:忽略大小写(如搜索 "Error" 和 "error" 都匹配)。-n:显示匹配行的行号。-v:反向匹配(显示不包含该字符串的行)。
1. 搜索单个日志文件中包含某字符串的行
例如,在 app.log 中搜索包含 "error" 的行。
grep "error" app.log # 基础搜索
grep -in "error" app.log # 忽略大小写,显示行号
2. 实时查看日志并搜索(动态日志)
用 tail -f 实时跟踪日志文件,同时用 grep 筛选特定内容(如实时监控错误日志):
tail -f app.log | grep "ERROR" # 实时显示包含 ERROR 的新日志
tail -f app.log | grep -i "error" # 忽略大小写,实时监控所有错误
如果查看某个 docker 容器的日志呢
实时跟踪日志
docker logs -f myapp # 实时跟踪 myapp 容器的日志
查看最近 N 行日志
docker logs -n 100 myapp # 只显示 myapp 容器的最后 100 行日志
21. Linux 找某个端口被哪个服务占用的命令(快手二面)
netstat -tuln | grep 端口号
举个例子:查看 3306 端口号。
netstat -tuln | grep 3306
22. 了解 Java 中异常的哪些子类(快手)
Checked Exception
- IOException
- SQLException
- ClassNotFoundException
受检查异常(文件读写异常,sql 语句异常,类不存在异常)。
RuntimeException
- NullPointerException
- IndexOutOfBoundsException
- ArithmeticException
运行时异常(空指针异常,数组越界异常,算数异常)。
23. 操作数据库有关的异常(快手)
- sql 语句语法错误异常
- 数据库连接失败异常
- 违反数据库约束异常
- 数据类型不匹配或数据超出范围异常
24. redis 的 decr 操作(快手)
Redis 的 DECR 命令用于对存储在指定键的数值进行原子性减 1 操作,若键不存在则先初始化为 0 再减 1,返回操作后的结果。它是 Redis 中用于实现'计数器递减''库存扣减'等场景的核心命令,具有原子性(线程安全)和高性能的特点。
25. https 的底层工作原理(字节跳动)
- https 和 http 的区别是什么
- 加密的整个流程
HTTPS 也是一个应用层协议。是在 HTTP 协议的基础上引入了一个加密层 (和 HTTP 的区别)。
在讲下面加密流程之前,我们先要有一些前置知识。
加密就是把明文 (要传输的信息) 进行一系列变换,生成密文。解密就是把密文再进行一系列变换,还原成明文。
- 对称加密:加密和解密依赖同一把密钥。
- 非对称加密:加密和解密依赖一对关联但不同的密钥。
1. 客户端发起请求
用户在浏览器中输入一个 HTTPS URL(如 https://www.example.com)。浏览器向服务器发起一个 HTTPS 连接请求。
2. 服务器响应并发送证书
服务器收到请求后,将其数字证书发送给客户端。数字证书包含服务器的公钥和由可信的证书颁发机构(CA)签名的信息。
3. 客户端验证证书
客户端收到服务器的证书后,会进行以下验证步骤:
- 验证证书的完整性和签名:使用 CA 的公钥验证证书的签名,确保证书没有被篡改。
- 检查证书的有效期:确认证书在有效期内。
- 验证证书的颁发机构:确认 CA 是受信任的机构。
- 检查证书吊销状态:通过证书吊销列表(CRL)或在线证书状态协议(OCSP)检查证书是否被吊销。
4. 生成会话密钥
如果证书验证通过,客户端会生成一个随机的对称密钥(也称为会话密钥),用于加密会话中的数据。客户端使用服务器的公钥加密这个会话密钥,并发送给服务器。
5. 服务器解密会话密钥
服务器使用其私钥解密会话密钥。此时,客户端和服务器都持有相同的对称密钥,用于加密和解密后续通信中的数据。



