经典技术疑难问答场景
首页/学习模块/疑难问答

经典疑难问答 · 卡住过的都收在这

每一条都是后台被问过最多次的真问题。点开就有答案,附代码与排查思路——不是"百度一下"那种敷衍。

收录问答10 条
覆盖方向5 个
含代码示例
Q & A

点开标题,答案就展开

Answer · 一句话讲透

闭包"闭"的是对外层变量的引用,而不是值的拷贝。循环里所有回调共享同一个 i 的引用,循环结束时它已经是终值,所以你拿到的全是最后一个。

解决办法是用 let(每轮新建词法绑定),或用 IIFE 立即传入当前值:

// 错误写法:全部打印 5
for (var i = 0; i < 5; i++) {
  setTimeout(() => console.log(i), 0);
}

// 正确写法 1:let
for (let i = 0; i < 5; i++) {
  setTimeout(() => console.log(i), 0);
}

// 正确写法 2:IIFE 锁定值
for (var i = 0; i < 5; i++) {
  ((j) => setTimeout(() => console.log(j), 0))(i);
}
#JavaScript#闭包#作用域#事件循环
Answer

GIL(全局解释器锁)保证同一时刻只有一个线程执行 Python 字节码。所以对 CPU 密集任务,多线程几乎无加速;对 IO 密集任务(网络、文件),线程会在 IO 处释放 GIL,所以仍然有效。

CPU 密集请用 multiprocessing,IO 密集用 asyncio 或线程池:

# CPU 密集:用进程绕开 GIL
from multiprocessing import Pool
with Pool(4) as p:
    p.map(heavy_compute, data)

# IO 密集:线程就够
from concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor(10) as ex:
    ex.map(fetch_url, urls)
#Python#GIL#并发
Answer

常见 5 类原因:① 索引列上用了函数或隐式类型转换,导致索引失效;② LIKE 以 % 开头;③ 联合索引没遵守最左前缀;④ OR 两侧不是都有索引;⑤ 优化器认为全表扫描更便宜(数据量小或统计信息过期)。

排查第一步永远是 EXPLAIN,看 typerows

-- 隐式转换:phone 是 varchar,传了 int,索引失效
SELECT * FROM users WHERE phone = 13800000000;

-- 正确:加引号
SELECT * FROM users WHERE phone = '13800000000';

-- 看执行计划
EXPLAIN SELECT id FROM orders WHERE user_id = 100 AND status = 'paid';
#MySQL#索引#慢查询
Answer

漏掉 await 会拿到一个 Promise 而不是结果,代码继续往下跑,后面对这个值做同步操作就会出错。如果后面没用到这个值,则"看起来没事",但内部的 reject 会变成未捕获异常,进程可能崩溃。

Node.js 14+ 对未处理的 rejection 默认会终止进程。建议在 ESLint 中开启 require-awaitno-floating-promises

#JavaScript#异步#Promise
Answer

JDK 7 中扩容时可能形成环形链表,导致 get 死循环;JDK 8 改成尾插法解决了死循环,但仍会丢数据、覆盖值。本质上 put 不是原子操作,多线程并发写同一个桶会互相覆盖。

解决方案按场景选:ConcurrentHashMap(推荐)、Collections.synchronizedMap(性能差),或加锁。

#Java#HashMap#并发
Answer

拦你的是浏览器,不是服务器——服务器其实已经把响应发出去了。预检请求(OPTIONS)失败、响应头 Access-Control-Allow-Origin 与请求 Origin 不匹配、或带 Cookie 时没设 Allow-Credentials: true 且 Origin 用了 *,都会让浏览器拦截。

排查时一定要看浏览器 Network 里的 OPTIONS 请求,而不是看主请求。

#HTTP#CORS#前端
Answer

三个概念常被搞混:雪崩是大量 key 同时过期,请求全打到 DB;击穿是单个热点 key 过期瞬间被打爆;穿透是查根本不存在的数据,缓存永远建不起来。

对应方案:雪崩给过期时间加随机抖动;击穿用互斥锁或热点 key 永不过期;穿透用布隆过滤器或缓存空值。

#Redis#缓存#高可用
Answer

不写依赖数组,每次渲染后都会执行;写空数组 [] 只在挂载后执行一次(卸载时执行 return 的清理函数)。常见的坑是在空数组里引用了 props 或 state,拿到的是初始闭包值——这就是"陈旧闭包"问题。

解决:把用到的值放进依赖数组,或用 useRef / 函数式更新。

#React#Hooks#闭包
Answer

本地未推送:用 git reset --hard <commit> 直接回退,干净利落。已推送到共享分支:用 git revert <commit> 生成一个反向提交,保留历史,避免弄乱同事的本地仓库。

口诀:没 push 用 reset,push 过用 revert

#Git#版本控制
Answer

四板斧:① 用 alpine 或 distroless 基础镜像;② 多阶段构建,编译产物拷到运行镜像,丢掉编译工具链;③ 合并 RUN 指令,减少层数;④ 用 && 串接命令并清理 apt 缓存。

# 多阶段构建示例
FROM golang:1.22 AS builder
WORKDIR /app
COPY . .
RUN go build -o app -ldflags="-s -w" .

FROM alpine:3.19
COPY --from=builder /app/app /app
CMD ["/app"]
#Docker#DevOps#镜像优化
Ask Your Question

没找到你的问题?提给我们

编辑部每周挑 3-5 个高赞问题写详细答案,被选中的提问者会收到一份手写笔记。