一、MySQL优化

1. 索引优化细节

  • 选择合适索引列:高选择性列(区分度高的列)优先

  • 复合索引顺序:最左前缀原则,区分度高的列在前

  • 避免索引失效:避免在索引列上使用函数、计算、类型转换

  • 索引覆盖:尽量让查询只需扫描索引

  • 索引长度优化:使用前缀索引减少索引大小

2. 前缀索引原理

sql

-- 创建前缀索引,只对前10个字符建立索引
CREATE INDEX idx_name ON table_name (column_name(10));

原理:只对字段的前N个字符建立索引,减少索引存储空间,提升查询速度,但会降低选择性。

3. MySQL索引选择

对于索引a和(a,b):

  • 仅查询a:优化器可能选择较小的索引a(如果数据量小)

  • 查询a和排序/范围查询:优先选择(a,b)复合索引

  • 基于成本选择:优化器会根据统计信息选择成本最低的索引

4. 覆盖索引原理

原理:查询的所有字段都包含在索引中,无需回表查询数据行。

sql

-- 假设有索引 (a, b)
SELECT a, b FROM table WHERE a = 1; -- 覆盖索引
SELECT a, b, c FROM table WHERE a = 1; -- 需要回表查询c列

5. 判断覆盖索引是否生效

使用EXPLAIN查看执行计划:

sql

EXPLAIN SELECT a, b FROM table WHERE a = 1;

如果Extra列显示"Using index",则表示使用了覆盖索引。

二、异步编程

1. 三个耗时操作异步化

java

// Java CompletableFuture实现
CompletableFuture<Void> futureA = CompletableFuture.runAsync(() -> doA());
CompletableFuture<Void> futureB = CompletableFuture.runAsync(() -> doB());
CompletableFuture<Void> futureC = CompletableFuture.runAsync(() -> doC());

// 等待所有完成
CompletableFuture.allOf(futureA, futureB, futureC).join();

2. 先A后B、C异步执行

java

CompletableFuture.runAsync(() -> doA())
    .thenRunAsync(() -> {
        CompletableFuture.runAsync(() -> doB());
        CompletableFuture.runAsync(() -> doC());
    });

3. Future实现原理

java

public class SimpleFuture<T> implements Future<T> {
    private volatile boolean isDone = false;
    private T result;
    private final Object lock = new Object();
    
    public void setResult(T result) {
        synchronized(lock) {
            this.result = result;
            this.isDone = true;
            lock.notifyAll();
        }
    }
    
    @Override
    public T get() throws InterruptedException {
        synchronized(lock) {
            while (!isDone) {
                lock.wait();
            }
            return result;
        }
    }
}

4. ForkJoin框架

原理:工作窃取算法,将大任务拆分为小任务并行执行。

java

public class CustomRecursiveTask extends RecursiveTask<Integer> {
    @Override
    protected Integer compute() {
        // 任务拆分和合并逻辑
        return null;
    }
}

三、JVM调优经验

1. Full GC定位和原因

定位方法

bash

# 查看GC情况
jstat -gcutil <pid> 1000

# 生成堆转储
jmap -dump:format=b,file=heap.hprof <pid>

# GC日志分析
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log

Full GC原因

  • 老年代空间不足

  • System.gc()调用

  • 大对象分配失败

  • 永久代/元空间不足

2. StringBuilder实现

java

public class MyStringBuilder {
    private char[] value;
    private int count;
    private static final int DEFAULT_CAPACITY = 16;
    
    public MyStringBuilder() {
        this(DEFAULT_CAPACITY);
    }
    
    public MyStringBuilder(int capacity) {
        value = new char[capacity];
        count = 0;
    }
    
    public MyStringBuilder append(String str) {
        if (str == null) str = "null";
        int len = str.length();
        ensureCapacity(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }
    
    private void ensureCapacity(int minimumCapacity) {
        if (minimumCapacity > value.length) {
            expandCapacity(minimumCapacity);
        }
    }
    
    private void expandCapacity(int minimumCapacity) {
        int newCapacity = value.length * 2 + 2;
        if (newCapacity < minimumCapacity) {
            newCapacity = minimumCapacity;
        }
        value = Arrays.copyOf(value, newCapacity);
    }
    
    @Override
    public String toString() {
        return new String(value, 0, count);
    }
}

四、面试问题精选解答

对称二叉树

java

public boolean isSymmetric(TreeNode root) {
    return root == null || isMirror(root.left, root.right);
}

private boolean isMirror(TreeNode left, TreeNode right) {
    if (left == null && right == null) return true;
    if (left == null || right == null) return false;
    return left.val == right.val 
        && isMirror(left.left, right.right) 
        && isMirror(left.right, right.left);
}

硬币概率问题

贝叶斯定理计算

  • 正常硬币连续5次相同概率: (1/2)^4 = 1/16

  • 作弊硬币连续5次相同概率: 1

  • 先验概率: 选择正常硬币概率1/2,作弊硬币1/2

  • 后验概率 = (1/2 * 1/16) / (1/2 * 1/16 + 1/2 * 1) = 1/17

ConcurrentHashMap原理

JDK1.8采用:

  • 数组 + 链表 + 红黑树

  • synchronized + CAS 保证线程安全

  • 扩容时多线程协助

volatile数组可见性

volatile Object[] arr 只保证arr引用的可见性,不保证数组元素的可见性。需要额外的同步机制。

五、NCS面试问题补充

MyBatis #和$区别

  • #{}:预编译处理,防止SQL注入

  • ${}:字符串替换,有SQL注入风险

Spring AOP原理

基于动态代理:

  • JDK动态代理:实现接口的类

  • CGLIB代理:未实现接口的类

JVM内存模型

  • 程序计数器

  • 虚拟机栈

  • 本地方法栈

  • 方法区(元空间)

HashMap vs Hashtable

  • HashMap线程不安全,Hashtable线程安全

  • HashMap允许null键值,Hashtable不允许

  • HashMap性能更好

Logo

欢迎大家加入成都城市开发者社区,“和我在成都的街头走一走”,让我们一起携手,汇聚IT技术潮流,共建社区文明生态!

更多推荐