面试题-面试经历
1、如何将1千万的值插入Mysql数据库
rewriteBatchedStatements=true(必须加,否则批量无效)- 关闭自动提交
setAutoCommit(false) - 每 500~2000 条
addBatch()后executeBatch() - 统一 commit
jdbc:mysql:///db?rewriteBatchedStatements=true&useServerPrepStmts=false
SQL层:
INSERT INTO t VALUES (...),(...),...批量 values- 建议每 100~500 条一组,避免 packet 过大
- 配合
LOAD DATA INFILE(最快,DBA 首选)
数据库层面提升优化:
- 临时改为 MyISAM(插入极快)
- 导入前关闭索引、外键、唯一性检查
- 临时关闭 binlog 或设置
sync_binlog=0 - 增大
innodb_buffer_pool_size、bulk_insert_buffer_size - 使用
LOAD DATA LOCAL INFILE(比 insert 快 10~50 倍)
2、3个大表如何做表关联查询Mysql数据库
优先小表驱动大表、建立合理联合索引、控制关联字段类型一致,避免使用子查询,尽量走 JOIN 且保证 ON 条件有索引,必要时分批查询或走覆盖索引减少回表。
核心原则:
- 小表驱动大表:小表在前,大表在后,减少循环次数
- JOIN 字段必须建索引:尤其 ON 条件字段,禁止隐式转换
- 只查需要的字段,避免 SELECT *,尽量走覆盖索引
- 禁止大表
LEFT JOIN后再WHERE过滤驱动表(会失效索引)
索引怎么建:
- 每个 JOIN 的关联字段单独建索引,或根据查询字段建联合索引
- 联合索引顺序:等值匹配字段 → JOIN 关联字段 → 查询字段
写法建议:
优先用 INNER JOIN,少用子查询:
SELECT a.x,b.y,c.z FROM 小表 a JOIN 中表 b ON a.id = b.a_id JOIN 大表 c ON b.id = c.b_id WHERE a.status = 1;
查询速度慢怎么办:
- 分批次查询(按时间 / ID 分段)
- 先查小表得到结果集,再批量查大表
- 考虑宽表预聚合、定时汇总表
- 走只读从库,不影响主库
3、项目中有用过哪些设计模式,具体实现。
单例模式:
应用场景:配置类、工具类、线程池、连接池、日志对象等。
4、优化慢SQL
先通过 explain 查看执行计划定位问题,再从索引优化、SQL 改写、表结构优化、分库分表四个层面入手,优先保证命中索引、减少回表、避免全表扫描。
定位慢sql:
- 开启慢查询日志
slow_query_log - 用
explain查看执行计划,重点看:- type(是否全表扫描 ALL)
- key(是否命中索引)
- rows(扫描行数)
- Extra(Using filesort、Using temporary 等)
sql语句层面优化:
- 避免
select *,只查需要字段 - 避免
where 1=1、!=、or、not in导致索引失效 - 避免隐式类型转换(字符串数字混用)
- 避免在索引列上做函数计算、运算
- 小表驱动大表,合理使用 join
- 分页深分页优化:用
id > ?代替 limit offset
索引优化:
- 为 where、order by、group by、join 字段建索引
- 建立联合索引,遵循最左前缀原则
- 使用覆盖索引避免回表
- 删除重复、冗余、无用索引
- 控制索引数量,避免写入性能下降
表结构和数据库优化:
- 大字段拆分到副表
- 合理选择字段类型,尽量小而简单
- 对历史数据归档、分区表
- 调整 innodb_buffer_pool_size 等参数
业务架构优化:
- 热点数据加缓存(Redis)
- 读写分离,查询走从库
- 大表进行分库分表
- 统计类 SQL 用离线计算 / 宽表
5、线上内存溢出怎么排查
先保留现场导出堆 dump 文件,再通过 jstat、jmap 定位内存溢出区域,用 MAT/JProfiler 分析大对象、泄漏对象,最后结合代码定位常量池、集合泄漏、线程泄漏或未关闭资源等问题。
先保留现场,防止数据丢失:
- 开启 JVM 参数保证自动导出 dump:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/xxx - 手动导出堆快照:
jmap -dump:format=b,file=heap.hprof <pid>
初步定位哪块内存溢出:
使用 jstat 观察 GC 情况
1 jstat -gc <pid> 1000 10
重点看:
- O/OECD:老年代 / 伊甸区占用持续上涨
- FGC:频繁 FullGC,且回收后内存不下降 → 典型内存泄漏
分析dump文件:
使用工具:
- MAT(Eclipse Memory Analyzer)
- JProfiler、VisualVM
- 大对象:超大 List、Map、数组没释放
- 泄漏对象:被静态集合持有、ThreadLocal 未清理
- 线程泄漏:线程池无界、线程创建过多
- Byte[]、String[] 通常是真实业务数据泄漏
常见OOM与对应方向:
- java.lang.OutOfMemoryError: Java heap space
堆不足 → 查大对象、静态集合缓存、循环添加对象 - java.lang.OutOfMemoryError: Metaspace
元空间溢出 → 大量生成动态类(cglib、反射、jsp) - java.lang.OutOfMemoryError: unable to create new native thread
线程溢出 → 无界线程池、手动创建线程不控制 - Direct buffer memory
堆外内存溢出 → NIO 未释放、Netty 配置不当
结合代码定位问题:
- 静态 Map/List 只增不减
- ThreadLocal 用完没 remove
- 流、连接、线程池未关闭
- 递归过深、死循环创建对象
- 第三方缓存框架使用不当
解决与验证:
- 修复代码,及时释放对象
- 优化线程池、集合大小
- 合理设置 Xmx、Xms、Metaspace 大小
- 压测验证 FGC 和内存曲线是否平稳
6、Redis集群怎么保证数据同步
Redis 集群通过 主从复制 + 异步同步 保证数据一致,主节点负责写并异步传播命令到从节点,从节点只读并重放主节点日志,断线重连后采用 部分重同步(PSYNC2) 避免全量复制。
-
主从架构
集群里每个槽对应一个主节点,主节点挂从节点,主写从读。 -
异步复制
客户端写主节点成功后立即返回,主节点异步把写命令发给所有从节点,不阻塞主线程。 -
同步流程
- 首次连接:全量同步,主生成 RDB 发给从,从加载并重放缓冲区增量数据
- 断线重连:使用 PSYNC 部分重同步,通过 replid(复制 ID)+ offset(偏移量) 对比,只同步缺失数据,不用全量重传
-
一致性保证
- 最终一致性,不是强一致
- 可通过
WAIT命令实现同步写,牺牲性能换取强一致
7、redis分布式锁如何设计的
Redis 分布式锁通过 SET key value NX EX 原子命令加锁,设置过期时间防止死锁,用唯一值避免误删别人锁,解锁时用 Lua 脚本保证原子性,高可用场景可结合 Redlock 或 Redisson 实现可重入、自动续期。
核心加锁命令(原子性):
使用:
1 SET lock_key unique_value NX PX 30000
- NX:仅 key 不存在时才设置(互斥)
- PX 30000:设置 30 秒过期,防止死锁
- unique_value:每个请求唯一,用于识别是不是自己的锁
解锁设计(必须原子):
不能直接 DEL,否则可能删别人锁。
用 Lua 脚本保证 “判断 + 删除” 原子:
if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end
关键细节:
- 必须设置过期时间:避免服务宕机导致锁无法释放
- 锁值必须唯一:防止业务执行超时,锁被自动释放后,误删其他线程的锁
- 加锁解锁必须原子:加锁用 SET 命令,解锁用 Lua 脚本
进阶:Redisson实现方案(生产常用)
- 可重入锁:通过 hash 结构记录重入次数
- watchdog 自动续期:业务未执行完,自动延长锁过期时间
- 分布式可靠性:主从切换可能丢锁,可用 Redlock 算法增强
保证交易的幂等性:
1、唯一索引/唯一约束
建唯一索引/唯一约束(订单号、业务号)
插入时报唯一冲突,直接返回成功
适合:新增类操作
2、分布式锁
执行业务前:拿锁(key = 订单号)
执行完:释放锁
重复请求进来:拿不到锁,直接拒绝
适合:并发高、更新类操作
3、前端token机制
提交前先请求一个 globalId
提交时带上 token
后端校验 token 是否已处理
适合:表单重复提交
4、状态机幂等(状态不可逆)
订单状态:待支付 → 支付中 → 已支付
只有当前状态是 “待支付” 才能执行支付
重复请求直接判断状态,不执行
适合:订单、流程类业务
5、业务唯一号+去重表
建一张去重表,business_id 唯一
先插入去重表,成功才执行业务
利用事务保证原子性
适合:支付、转账等强一致场景
6、全局唯一id(雪花算法)
上游传入唯一业务 ID
后端先查是否处理过
适合:微服务之间调用、MQ 消息
项目性能优化
一、接口层优化
1、接口优化
合并接口,减少请求次数
接口超时控制、限流、熔断(Sentinel/Hystrix)
异步处理:CompletableFuture、消息队列解耦
二、网关优化
Nginx 负载均衡、动静分离
限流、黑名单、防刷
三、业务层优化
1、异步化
日志、通知、统计等非核心流程丢 MQ 异步处理
2、线程池优化
合理设置核心线程、队列、拒绝策略
3、减少远程调用
批量接口替代循环调用
异步并行调用多个下游
四、缓存优化
1、加缓存
热点数据放 Redis,减少 DB 访问
2、解决缓存问题
- 缓存穿透:布隆过滤器 / 缓存空值
- 缓存击穿:互斥锁 / 热点永不过期
- 缓存雪崩:过期时间随机 + 集群 + 降级
3、本地缓存
Caffeine、Guava Cache 减轻 Redis 压力
五、数据库优化
1、sql优化
- 避免 select *、避免深分页
- 避免索引失效:左模糊、函数运算、隐式转换
2、索引优化
- 建联合索引,遵循最左匹配
- 使用覆盖索引避免回表
3、分库分表
水平分表、垂直分库
4、读写分离
主库写,从库读
六、jvm优化
- 合理设置堆内存:-Xms -Xmx
- 选择 GC 算法:JDK8 默认 CMS,高并发用 G1/ZGC
- 避免 FullGC:大对象、内存泄漏、FullGC 频繁
七、中间件优化
- MQ 削峰填谷,异步解耦
- 合理设置线程数、批量消费、重试策略
- 避免消息堆积、重复消费(保证幂等)
八、架构层面
- 读写分离、分库分表
- 服务拆分,微服务化
- 多级缓存:本地缓存 → Redis → DB
- 热点服务独立部署,水平扩容

浙公网安备 33010602011771号