11.22
对本周面试的一些问题做个总结
1、 点赞功能的通用性是怎么设计的,模型是怎么建的?
通用性设计:
抽象出“点赞对象”:不绑定具体业务(如文章、评论),而是通过 targetType(目标类型) + targetId(目标 ID)标识被点赞资源。
用户维度去重:每个用户对同一目标只能点一次(联合唯一索引)。
数据库模型:
sql
CREATE TABLE user_like (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
target_type VARCHAR(32) NOT NULL, -- 'article', 'comment'
target_id BIGINT NOT NULL,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_user_target (user_id, target_type, target_id)
);
扩展性:
支持取消点赞(删除记录);
热点数据用 Redis 缓存点赞数(INCR/DECR),定时同步到 DB。
2、 你们用到怎么多的锁,那你们是多个实例也就是多节点的吗?
是的,我们服务是多节点部署(通常 2~5 个实例),通过 Nginx 或 Spring Cloud Gateway 负载均衡。
因此本地锁(如 synchronized)无效,必须使用分布式锁(如 Redis 分布式锁、ZooKeeper)或数据库乐观锁来保证跨节点一致性。
3、 多个实例用乐观锁,能保证他的一致性吗?
可以保证最终一致性,但需满足条件:
乐观锁基于版本号(version)或 CAS(compare-and-set);
更新时检查版本是否变化,若失败则重试或返回错误;
适用于冲突较少的场景(如点赞、库存扣减);
不能保证强一致性,但在高并发下性能远优于悲观锁。
4、 用过什么设计模式?
在项目中常用:单例模式、工厂模式、策略模式、模板方法、责任链模式、观察者模式。
举例:支付模块用策略模式(微信/支付宝/银联),审批流用责任链模式。
5、 介绍一下自己了解的设计模式
创建型:单例(全局唯一)、工厂(解耦对象创建);
结构型:适配器(兼容接口)、代理(增强功能);
行为型:
策略:封装算法,可切换;
模板方法:定义骨架,子类实现细节;
责任链:请求沿链传递,直到被处理;
观察者:一对多依赖,状态变化自动通知。
6、 介绍一下责任链模式
定义:将请求从链头传到链尾,每个处理器决定是否处理或传递给下一个。
优点:解耦请求发送者与接收者,新增处理器无需修改原有代码。
典型应用:权限校验链、日志过滤链、审批流程。
7、 讲一下实现责任链的基础
基础结构:
抽象处理器类(含 next 指针);
具体处理器继承并实现处理逻辑;
链的组装(手动 or Spring 自动注入)。
关键:每个处理器持有下一个处理器的引用。
8、 怎么用当前链调用下一个链的方法,要怎么才能调的到
在抽象类中定义 nextHandler 字段,并提供 setter;
处理方法中判断是否自己处理,否则调用 nextHandler.handle(request);
9、 线程池了解吗,说一下线程池的作用?
降低资源消耗(复用线程);
提高响应速度(任务到来直接执行);
提高线程可管理性(统一分配、监控、调优)。
10、 线程池的核心参数
corePoolSize:核心线程数(即使空闲也不销毁);
maximumPoolSize:最大线程数;
keepAliveTime:非核心线程空闲超时时间;
workQueue:任务队列(ArrayBlockingQueue、LinkedBlockingQueue 等);
threadFactory:线程创建工厂;
handler:拒绝策略(AbortPolicy、CallerRunsPolicy 等)。
11、 什么时候创建临时线程
当任务数 > 核心线程数 且 任务队列已满 时,才会创建临时线程(超过 corePoolSize 的线程);
临时线程空闲超过 keepAliveTime 会被回收。
12、 核心线程数和最大线程数怎么定义
核心线程数:根据 CPU 密集型 or IO 密集型估算:
CPU 密集型:≈ CPU 核数;
IO 密集型:≈ CPU 核数 × (1 + 平均等待时间 / 平均工作时间);
最大线程数:防止资源耗尽,一般设为合理上限(如 200),配合有界队列使用。
13、 怎么避免死锁
破坏死锁四个必要条件之一:
互斥 → 无法破坏;
占有并等待 → 一次性申请所有资源;
不可抢占 → 主动释放已占资源;
循环等待 → 按固定顺序加锁(如先 lock A 再 lock B);
实践建议:使用 tryLock(timeout) 设置超时,避免无限等待。
14、 用乐观锁是不是一直去循环查数据库
不是无限循环,通常:
重试次数有限(如 3~5 次);
若仍失败,返回“请稍后重试”或走补偿流程;
目的是避免阻塞,而非死循环。
15、 sleep和wait的区别
Thread.sleep() Object.wait()
所属类 Thread Object
是否释放锁 不释放 释放
唤醒方式 自动超时 notify() / notifyAll()
使用位置 任意地方 必须在 synchronized 块内
16、 是不是用的jdk1.8,用什么新特性
是的,主流项目基于 JDK 8;
常用新特性:
Lambda 表达式;
Stream API(集合操作);
Optional(避免空指针);
新日期 API(LocalDateTime);
接口默认方法(default)。
17、 函数式接口用过吗?
用过,如 Function<T,R>、Consumer
应用场景:
Stream 的 map/filter:list.stream().filter(x -> x > 0);
自定义回调逻辑:void process(Supplier
18、 Redis用过吗?缓存三兄弟?
用过,主要用于缓存、分布式锁、计数器;
缓存三兄弟(三大经典问题):
缓存穿透:查不存在的数据 → 布隆过滤器 / 缓存空值;
缓存击穿:热点 key 过期瞬间高并发 → 互斥锁 / 逻辑过期;
缓存雪崩:大量 key 同时过期 → 随机 TTL / 多级缓存。
19、 数据库性能调优的经验
SQL 层:避免 SELECT *、大分页、函数操作字段;
索引层:合理建索引、避免冗余索引、使用覆盖索引;
架构层:读写分离、分库分表(ShardingSphere);
监控:开启慢查询日志,用 EXPLAIN 分析执行计划。
20、 慢查询是怎么,怎么解决?
原因:无索引、全表扫描、JOIN 过多、数据量大;
解决:
EXPLAIN 查看执行计划;
添加合适索引;
优化 SQL(拆分复杂查询、避免子查询);
读写分离 or 缓存热点数据。
21、 什么字段适合做索引
高频查询字段(WHERE、ORDER BY、GROUP BY);
区分度高的字段(如 user_id,而非 gender);
组合索引:按查询频率和选择性排序;
避免:频繁更新字段、TEXT/BLOB 类型(除非前缀索引)。
22、 最左原则是什么?
MySQL 使用联合索引时,必须从最左列开始,且不能跳过中间列;
23、 MQ消费失败怎么处理?
自动重试:RabbitMQ 默认 3 次,失败后进死信队列(DLQ);
人工干预:监控 DLQ,分析日志,修复后重新投递;
降级处理:记录失败日志,走补偿任务(定时重试);
幂等设计:确保重复消费不影响结果。
24、 有没有消费不过来的情况?
有,常见于:
消费者处理慢(如 DB 慢、外部接口超时);
生产速度 >> 消费速度;
解决方案:
扩容消费者实例;
优化消费逻辑(异步、批量处理);
限流生产端(如 Sentinel 控制消息发送速率);
使用堆积监控告警(如 RabbitMQ Management UI)。

浙公网安备 33010602011771号