一、线上问题排查实战场景(代码示例)
1. 场景一:CPU使用率飙升到99%
问题代码示例:
// 错误示例:死循环导致CPU飙升
public class CpuProblem {
public static void main(String[] args) {
while (true) { // 没有终止条件
// 模拟高CPU操作(如复杂计算)
for (int i = 0; i < 1000000; i++) {
Math.pow(i, 2);
}
}
}
}
排查步骤:
- 使用
top
找到Java进程PID。 jstack PID
查看线程堆栈,定位到CpuProblem.main
中的死循环代码。
修复方案:
// 修复:增加循环终止条件
while (someCondition) { // 例如根据业务逻辑设置条件
// ...
}
2. 场景二:频繁Full GC(内存泄漏示例)
问题代码示例:
// 错误示例:静态Map未清理导致内存泄漏
public class MemoryLeak {
private static Map<Long, byte[]> cache = new HashMap<>();
public void addData(long id) {
// 不断向静态Map添加大对象(如1MB的byte数组)
cache.put(id, new byte[1024 * 1024]);
}
public static void main(String[] args) {
MemoryLeak leak = new MemoryLeak();
for (long i = 0; i < 100000; i++) {
leak.addData(i);
}
}
}
排查工具:
jmap -histo:live PID
查看对象数量,发现byte[]
对象堆积。jmap -dump:format=b,file=heap.hprof PID
导出堆内存,用MAT分析发现cache
占内存。
修复方案:
// 修复:使用WeakHashMap或定期清理缓存
private static Map<Long, byte[]> cache = new WeakHashMap<>();
3. 场景三:MySQL慢查询(索引失效示例)
问题SQL示例:
-- 错误示例:对字段使用函数导致索引失效
SELECT * FROM user WHERE DATE(create_time) = '2023-10-01';
-- 未使用索引的查询
EXPLAIN SELECT * FROM user WHERE age + 1 > 20; -- 对字段进行运算
优化方案:
-- 修复:避免对字段使用函数或运算
SELECT * FROM user WHERE create_time BETWEEN '2023-10-01 00:00:00' AND '2023-10-01 23:59:59';
-- 添加联合索引
ALTER TABLE user ADD INDEX idx_age_name (age, name);
-- 覆盖索引示例(直接通过索引返回数据)
SELECT age, name FROM user WHERE age > 20; -- 若索引是 (age, name),则无需回表
二、MySQL优化代码示例
1. 分页查询优化
低效分页:
SELECT * FROM orders ORDER BY id LIMIT 1000000, 10; -- 扫描前100万行
高效分页:
-- 使用游标分页(记录上次查询的ID)
SELECT * FROM orders WHERE id > 1000000 ORDER BY id LIMIT 10;
2. 乐观锁实现
// 使用版本号解决并发更新问题
public boolean updateOrder(Order order) {
int version = order.getVersion();
order.setVersion(version + 1);
String sql = "UPDATE orders SET amount = ?, version = ? WHERE id = ? AND version = ?";
int rows = jdbcTemplate.update(sql, order.getAmount(), order.getVersion(), order.getId(), version);
return rows > 0; // 如果更新失败(版本号不匹配),返回false
}
三、Redis优化代码示例
1. 内存优化(Hash存储对象)
低效方案:
// 存储用户对象为多个String类型
redis.set("user:1001:name", "张三");
redis.set("user:1001:age", "25");
高效方案:
// 使用Hash存储对象,减少Key数量
Map<String, String> user = new HashMap<>();
user.put("name", "张三");
user.put("age", "25");
redis.hset("user:1001", user); // 单个Key存储所有字段
2. 缓存击穿解决方案(互斥锁)
public String getData(String key) {
String value = redis.get(key);
if (value == null) {
// 尝试获取分布式锁(如Redisson)
if (redis.setnx(key + ":lock", "1")) {
redis.expire(key + ":lock", 10); // 防止死锁
value = db.query(key); // 查数据库
redis.set(key, value, 60); // 写入缓存
redis.del(key + ":lock");
} else {
// 等待其他线程加载数据
Thread.sleep(100);
return getData(key); // 重试
}
}
return value;
}
四、面试加分项代码示例
1. 分布式ID生成(雪花算法)
public class SnowflakeIdGenerator {
private long workerId;
private long sequence = 0L;
private long lastTimestamp = -1L;
public synchronized long nextId() {
long timestamp = System.currentTimeMillis();
if (timestamp < lastTimestamp) {
throw new RuntimeException("时钟回拨!");
}
if (timestamp == lastTimestamp) {
sequence = (sequence + 1) & 0xFFF; // 12位序列号
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
return ((timestamp - 1288834974657L) << 22) | (workerId << 12) | sequence;
}
}
2. 延迟双删保证缓存一致性
public void updateData(Data data) {
// 1. 先删除缓存
redis.del("data:" + data.getId());
// 2. 更新数据库
db.update(data);
// 3. 延迟再次删除(如1秒后)
new Thread(() -> {
Thread.sleep(1000);
redis.del("data:" + data.getId());
}).start();
}
总结
通过代码示例可以更直观地理解问题场景和优化方案。实际面试中,结合代码解释逻辑(如为什么用Hash替代String、如何避免索引失效),能显著提升回答说服力。建议在本地环境模拟这些场景,加深印象。