面试题-面试经历

1、如何将1千万的值插入Mysql数据库

插入千万级数据到 MySQL,优先用 JDBC 批处理 + 批量 SQL + 关闭自动提交,配合 MyISAM / 关闭索引 / 禁用 binlog 等优化,避免单条循环插入。 
java层:JDBC批量
核心要点:
  • 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_sizebulk_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!=ornot 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
  • 热点服务独立部署,水平扩容

 

 

posted @ 2026-03-27 18:31  暮商  阅读(11)  评论(0)    收藏  举报