提问总结2
1. SQL 优化怎么做的?
做过多次 SQL 优化,核心思路是 “减少无效数据扫描,提升查询效率”,具体手段:
- 索引优化:给频繁查询的字段(如订单号、用户 ID)建 B + 树索引;避免给低基数字段(如性别)建索引;联合索引遵循 “最左前缀原则”(比如
where a=? and b=?
,建(a,b)
索引而非(b,a)
);定期用show index from 表名
检查索引碎片,用optimize table
重建。 - SQL 语句优化:避免
select *
,只查需要的字段;减少join
表数量(尽量控制在 3 张内),复杂关联用子查询拆分成单表查询;不用or
(改用union
)、not in
(改用left join ... is null
);like
避免前缀模糊(%xxx
会导致全表扫描)。 - 执行计划分析:用
explain
查看 SQL 执行计划,重点看type
(目标是ref
或range
,避免ALL
全表扫描)、key
(是否用到索引)、rows
(扫描行数,越少越好)。 - 表结构优化:大表拆分(分库分表);用
tinyint
代替int
、varchar(20)
代替varchar(255)
减少存储;避免null
(用默认值代替,null
会影响索引效率)。
2. 事务用过吗?哪些地方用了?隔离级别及选择原因?
用过,核心场景是需要保证数据一致性的操作:比如订单支付(扣库存 + 减余额 + 生成订单必须同时成功 / 失败)、用户转账(A 减钱和 B 加钱必须同时生效)。
隔离级别:MySQL 默认是可重复读(Repeatable Read)。
- 选择原因:平衡了一致性和性能。比 “读已提交” 多解决了 “不可重复读”,比 “串行化” 性能更高(串行化会锁表,并发低)。
- 解决的问题:
- 避免 “脏读”(读到未提交的数据);
- 避免 “不可重复读”(同一事务内两次读同一行,结果不同);
- MySQL 的可重复读通过 MVCC 还能避免 “幻读”(部分场景,如
insert
时加间隙锁)。
3. MySQL 事务特性及日志依赖?MVCC?
事务特性(ACID):
- 原子性(Atomicity):依赖undo 日志(记录操作反向逻辑,失败时回滚);
- 一致性(Consistency):由原子性、隔离性、持久性共同保证;
- 隔离性(Isolation):依赖锁机制(行锁、表锁)和undo 日志(MVCC);
- 持久性(Durability):依赖redo 日志(事务提交后写入,崩溃时重放恢复数据)。
MVCC(多版本并发控制):
- 作用:实现 “读写不冲突”,读不加锁,提升并发效率。
- 原理:每行数据包含隐藏列(
DB_TRX_ID
事务 ID、DB_ROLL_PTR
指向 undo 日志的指针);事务启动时生成 “Read View”(可见性规则),通过 undo 日志找到历史版本,实现 “快照读”(普通select
)。
4. Redis 基本类型(含扩展)?持久化及 AOF 策略?
基本类型:
- 核心:
string
(字符串,如缓存 token)、list
(列表,如消息队列)、hash
(哈希,如存储用户信息)、set
(集合,如去重)、zset
(有序集合,如排行榜)。 - 扩展:
Bitmap
(位图,如统计用户签到)、HyperLogLog
(基数统计,如 UV 计数)、Geospatial
(地理信息,如附近的人)。
持久化:
-
RDB:定时生成内存快照(二进制文件),优点是恢复快,缺点是可能丢数据(两次快照间的操作)。
-
AOF
:记录所有写命令(文本),通过重放命令恢复。
AOF 策略:
always
:每条命令刷盘,不丢数据但性能差;everysec
:每秒刷盘,最多丢 1 秒数据,平衡性能和安全性(默认);no
:由 OS 决定刷盘,可能丢大量数据。
5. 分布式锁?哪些地方用?分布式事务?
分布式锁:
- 实现:常用 Redis(
set key value nx ex 10
)或 ZooKeeper(临时节点)。 - 应用场景:秒杀库存扣减(防超卖)、分布式任务调度(防重复执行)、共享资源操作(如并发修改配置)。
分布式事务:
- 解决跨库 / 跨服务的事务一致性(如订单服务 + 库存服务 + 支付服务)。
- 方案:TCC(Try-Confirm-Cancel,业务侵入性高但性能好)、Saga(长事务拆分,补偿机制)、本地消息表(基于消息队列,最终一致性)。
6. Sharding、MyCat 分库分表?
分库分表:解决单库单表数据量大(如千万级以上)的问题,分为:
- 水平拆分:同表数据拆到多个表(如订单表按用户 ID 哈希拆成 10 张);
- 垂直拆分:大表按字段拆分(如用户表拆成用户基本信息表 + 用户详情表)。
MyCat:中间件,伪装成 MySQL,接收 SQL 后路由到实际分库分表,对应用透明。核心配置:schema.xml
(表拆分规则)、rule.xml
(分片算法,如范围、哈希)。
7. JVM 垃圾收集算法?
- 标记 - 清除:先标记垃圾,再清除。优点:简单;缺点:产生内存碎片,后续大对象分配可能失败。
- 复制:将内存分成两块,只使用一块,垃圾回收时复制存活对象到另一块,清空原块。优点:无碎片;缺点:内存利用率低(只用一半),适合存活对象少的区域(如新生代)。
- 标记 - 整理:标记后将存活对象移到一端,清空另一端。优点:无碎片,内存利用率高;缺点:移动对象耗时,适合存活对象多的区域(如老年代)。
8. RabbitMQ 相关?
分发策略:
- 点对点:一个消息只被一个消费者消费(绑定队列,如订单通知);
- 发布订阅:一个消息被多个消费者消费(通过交换机
fanout
类型,如日志广播)。
路由策略:
direct
:按routing key
精准匹配;topic
:按routing key
模糊匹配(*
匹配一个词,#
匹配多个);fanout
:无视routing key
,广播给所有绑定队列。
削峰填谷:用队列缓冲突发流量(如秒杀时大量下单请求),消费者按能力匀速消费,避免服务被冲垮。
消息丢失解决:
- 生产者:开启
confirm
机制,确保消息到交换机; - 队列:设置持久化(
durable=true
); - 消费者:关闭自动 ACK,处理完手动 ACK。
消息重复消费:通过 “幂等性处理” 解决,如订单 ID 作为唯一键,重复消费时判断已处理过。
9. Git 冲突解决?
冲突通常因多人分支修改同一文件同一行导致,解决步骤:
git pull origin 目标分支
拉取最新代码,触发冲突;- 打开冲突文件,找到
<<<<<<< HEAD
(本地修改)、=======
(远程修改)、>>>>>>> 分支名
标记的冲突区域; - 手动编辑并保留正确代码,删除冲突标记;
git add 冲突文件
→git commit -m "解决冲突"
→git push
。
10. 网络相关?
- 三次次挥手不行:四次挥手是因为 TCP 是全双工,双方都需要单独关闭读写通道。第三次次挥手可能导致一方方还没发完数据就被关闭,丢包。
- HTTPS 请求过程
- TCP 三次握手建立连接;
- SSL/TLS 握手:客户端发支持的加密密套件 → 服务器发证书(含公钥) → 客户端验证证书,生成随机数用公钥加密发给服务器 → 双方用随机数生成对称密钥;
- 用对称密钥加密 HTTP 数据传输;
- 服务器返回响应,TCP 四次挥手断开连接。
11. 单元测试怎么写?策略?
用 JUnit 编写,测试独立方法 / 模块,策略:
- 等价类划分:将输入划分为有效 / 无效等价类(如测试登录,有效账号密码 vs 空账号);
- 边界值分析:测试临界值(如分页查询,pageSize=0、1、最大限制);
- 路径覆盖:覆盖所有分支(如
if-else
、switch
的每个分支); - 异常测试:验证方法在异常输入时的处理(如空指针、参数越界)。
核心原则:独立、可重复、快速执行,确保改代码后能快速发现问题。