Java面试必备:线上问题排查与MySQL、Redis优化实战场景题

一、线上问题排查实战场景(代码示例)

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);
            }
        }
    }
}

排查步骤

  1. 使用 top 找到Java进程PID。
  2. 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、如何避免索引失效),能显著提升回答说服力。建议在本地环境模拟这些场景,加深印象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值