
闭包"闭"的是对外层变量的引用,而不是值的拷贝。循环里所有回调共享同一个 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); }
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)
常见 5 类原因:① 索引列上用了函数或隐式类型转换,导致索引失效;② LIKE 以 % 开头;③ 联合索引没遵守最左前缀;④ OR 两侧不是都有索引;⑤ 优化器认为全表扫描更便宜(数据量小或统计信息过期)。
排查第一步永远是 EXPLAIN,看 type 和 rows:
-- 隐式转换: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';
漏掉 await 会拿到一个 Promise 而不是结果,代码继续往下跑,后面对这个值做同步操作就会出错。如果后面没用到这个值,则"看起来没事",但内部的 reject 会变成未捕获异常,进程可能崩溃。
Node.js 14+ 对未处理的 rejection 默认会终止进程。建议在 ESLint 中开启 require-await 与 no-floating-promises。
JDK 7 中扩容时可能形成环形链表,导致 get 死循环;JDK 8 改成尾插法解决了死循环,但仍会丢数据、覆盖值。本质上 put 不是原子操作,多线程并发写同一个桶会互相覆盖。
解决方案按场景选:ConcurrentHashMap(推荐)、Collections.synchronizedMap(性能差),或加锁。
拦你的是浏览器,不是服务器——服务器其实已经把响应发出去了。预检请求(OPTIONS)失败、响应头 Access-Control-Allow-Origin 与请求 Origin 不匹配、或带 Cookie 时没设 Allow-Credentials: true 且 Origin 用了 *,都会让浏览器拦截。
排查时一定要看浏览器 Network 里的 OPTIONS 请求,而不是看主请求。
三个概念常被搞混:雪崩是大量 key 同时过期,请求全打到 DB;击穿是单个热点 key 过期瞬间被打爆;穿透是查根本不存在的数据,缓存永远建不起来。
对应方案:雪崩给过期时间加随机抖动;击穿用互斥锁或热点 key 永不过期;穿透用布隆过滤器或缓存空值。
不写依赖数组,每次渲染后都会执行;写空数组 [] 只在挂载后执行一次(卸载时执行 return 的清理函数)。常见的坑是在空数组里引用了 props 或 state,拿到的是初始闭包值——这就是"陈旧闭包"问题。
解决:把用到的值放进依赖数组,或用 useRef / 函数式更新。
本地未推送:用 git reset --hard <commit> 直接回退,干净利落。已推送到共享分支:用 git revert <commit> 生成一个反向提交,保留历史,避免弄乱同事的本地仓库。
口诀:没 push 用 reset,push 过用 revert。
四板斧:① 用 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"]