知其然,知其所以然
Go 内部原理 2026-10-10

文件写入与 fsync:Write 返回了数据就落盘了吗

在 Go 1.27.1(arm64 macOS)里量文件写入的持久化:f.Write 只要 27 µs(进页缓存,别的读者立刻可见);syscall.Fsync 63 µs;而 Go 的 f.Sync() 在 macOS 上用 F_FULLFSYNC,要 2876 µs;追加日志每条都 Sync 比不 Sync 慢约 550 倍,批量每 100 条 Sync 一次可降到 15.4 µs/条;“临时文件 + Sync + 重命名 + 目录 Sync” 的成本几乎全在两次 Sync(约 5.5 ms)。

Go fsync 文件写入 持久性 F_FULLFSYNC 真实验证 动画演示
Go 内部原理 2026-10-10

regexp:RE2 的线性时间保证与回溯引擎的区别

在 Go 1.27.1 里验证 regexp 的线性时间保证:灾难性模式 ^(a+)+$ 在 100 万个字符的输入上只要 38.0 ms,而同一个模式在 Python 3 的 re 里 28 个字符就要 8919 ms、30 个字符超过 20 秒;Go 不支持反向引用、先行/后行断言和占有量词;默认最左优先而不是最左最长;预编译比每次编译快约 16 倍。

Go regexp RE2 线性时间 回溯 真实验证 动画演示
Go 内部原理 2026-10-10

strings.Builder 与 bytes.Buffer:拼接字符串的成本

在 Go 1.27.1 里量字符串拼接:5 万段 16 字节,s += 要 1.45 秒、分配 20.2 GB,而 strings.Builder 只要 0.49 ms,预先 Grow 后 0.17 ms 且只 1 次分配;Sprintf 拼接更慢;Builder.String() 零拷贝(0 次分配)而 bytes.Buffer.String() 要复制;Builder 的容量增长曲线;复制已写入的 Builder 会 panic。

Go strings.Builder bytes.Buffer 字符串拼接 Grow 真实验证 动画演示
Go 内部原理 2026-10-10

sort 与 slices.Sort:pdqsort 与稳定排序的差别

在 Go 1.27.1 里实测排序:100 万个随机 int,slices.Sort 60.8 ms、sort.Slice 102.5 ms、sort.SliceStable 334.0 ms;已排序、逆序、全相等的输入只要 0.6–0.9 ms(pdqsort 识别模式);几乎有序的输入稳定排序反而更快(17.5 对 37.9 ms);不稳定排序会把一半的相等键对顺序打乱(49962 对),稳定排序是 0 对。

Go 排序 pdqsort sort.Slice slices.Sort 稳定排序 真实验证 动画演示
Go 内部原理 2026-10-10

math/rand/v2:新的生成器和并发安全

在 Go 1.27.1 里实测 math/rand 与 math/rand/v2:默认的顶层函数 v1 与 v2 在 8 个 goroutine 下都是 1.0–1.4 ns,并不争锁;rand.Seed 从 Go 1.24 起是空操作,GODEBUG=randseednop=0 恢复后 8 个 goroutine 变成 106.3 ns;新建带种子的 v1 生成器要 10369 ns、5376 B,v2 PCG 只要 7 ns、16 B;共用一个 *Rand 会被 -race 报竞争,顶层函数不会。

Go math/rand/v2 随机数 PCG ChaCha8 并发 真实验证 动画演示
Go 内部原理 2026-10-10

crypto/tls:握手过程和证书校验在代码里怎么走

在 Go 1.27.1 里实测 crypto/tls:进程内生成 CA 和证书,握手后读到 TLS 1.3 与 TLS_AES_128_GCM_SHA256;逐个触发未信任、过期、主机名不符、版本不匹配、缺少 ServerName 等错误并给出原文;会话复用 0.63 ms 对完整握手 0.89 ms;RSA 2048 证书的握手比 ECDSA P-256 慢约 2.3 倍。

Go crypto/tls TLS 证书校验 会话复用 真实验证 动画演示
Go 内部原理 2026-10-10

database/sql 连接池:MaxOpenConns、空闲与寿命

用自己写的假驱动实测 database/sql 的连接池(池的逻辑在标准库里,与具体数据库无关):50 个并发语句,MaxOpenConns 从不限制到 5 到 1,总用时从 21.4 ms 到 210.8 ms 到 1051.4 ms;默认只保留 2 个空闲连接,每次突发之后再开 8 个;ConnMaxLifetime=100ms 让 1.5 秒内换 14 次连接;ConnMaxIdleTime 设 50 ms 实际要约 1 秒后才清理;rows 不关闭会一直占着连接。

Go database/sql 连接池 MaxOpenConns MaxIdleConns 真实验证 动画演示
Go 内部原理 2026-10-10

log/slog:结构化日志的 Handler 与性能

在 Go 1.27.1 里实测 log/slog:TextHandler 与 JSONHandler 的输出格式(分组、With、!BADKEY);被级别过滤的日志不会计算 LogValuer;输出到空 writer 时 JSONHandler.Info 471 ns、0 次分配,AddSource 约翻倍到 931 ns 并多 6 次分配,被过滤的 Info 只要 5 ns;标准库 log.Printf 145 ns,写到 io.Discard 时因短路只要 3 ns;8 个 goroutine 的 16000 条日志每行都完整。

Go log/slog 结构化日志 Handler 性能 真实验证 动画演示
Go 内部原理 2026-10-10

模糊测试:go test -fuzz 如何生成和缩小用例

在 Go 1.27.1(arm64 macOS)里实测 go test -fuzz:藏在 4 个特定字节之后的 panic,种子语料全部通过;fuzz 冷启动 0.62–0.77 秒就找到,失败输入 31 字节被缩小到 4 字节 "Go!?",写入 testdata/fuzz/FuzzParse/<哈希>,之后普通 go test 自动重放成回归测试;无 bug 的版本约 29 万次/秒;语料缓存在 GOCACHE/fuzz。

Go 模糊测试 fuzz 覆盖率引导 缩小用例 真实验证 动画演示
Go 内部原理 2026-10-10

基准测试的陷阱:编译器优化掉了被测代码

在 Go 1.27.1(arm64 macOS)里复现基准测试的陷阱:不使用结果时能内联的函数被整个删掉,循环体只剩 8 条指令(0.297 ns/op),b.Loop 版本保住了调用(2.350 ns/op),noinline 的同类函数是 25.850 ns/op;常量参数被折叠成常量;对同一个切片反复排序测的是已排序数据(7635 ns 对 462796 ns);同一个基准重复 10 次最大与最小差 20%。

Go 基准测试 编译器优化 b.Loop testing 真实验证 动画演示
Go 内部原理 2026-10-10

表驱动测试与子测试:并行子测试里的变量捕获

在 Go 1.27.1 里复现并行子测试的循环变量捕获问题:同一份测试代码,go.mod 写 go 1.21 时两个子测试看到的是最后一个用例的名字(3 个里只有 1 个通过),写 go 1.22 时全部通过;子测试名的转义规则(空格→_、重名→#01、空串→#00、斜杠保留);-run 的斜杠分隔与正则;-parallel 限制并行度。

Go 表驱动测试 子测试 t.Parallel 循环变量 真实验证 动画演示
Go 内部原理 2026-10-10

testing/synctest:用假时钟测试并发代码

在 Go 1.27.1 里实测 testing/synctest:TTL 一小时的缓存,测试里过了 1h0m1s 却耗时 0.00 秒(真实时钟的 300 ms TTL 版本要 0.35 秒);每秒一次的轮询在超时 10.001 秒内确定地调用 10 次;synctest.Wait 确保后台 goroutine 已执行完;所有 goroutine 阻塞又没有定时器时报 deadlock,根 goroutine 退出时有泄漏的 goroutine 也会 panic。

Go testing/synctest 假时钟 并发测试 死锁检测 真实验证 动画演示
京ICP备2026067502号-1京公网安备11010502063487号