blank import 是让 Go 加载包并执行其 init() 函数和全局初始化,但不引入任何导出标识符;常用于数据库驱动注册、pprof 路由挂载等副作用场景,且不可撤回。

blank import 是什么,它到底在干什么
它不是“导入一个空包”,而是告诉 Go 编译器:请加载这个包、执行它的 init() 函数、初始化全局变量,但我不打算用它导出的任何符号(比如 sql.Register 或 http.HandleFunc)。本质是“只跑副作用,不拿接口”。
常见场景包括:
- 数据库驱动注册:
import _ "github.com/go-sql-driver/mysql"—— 驱动包的init()会调用sql.Register - pprof HTTP handler 注册:
import _ "net/http/pprof"—— 启动时自动挂载/debug/pprof/路由 - 日志钩子或指标埋点初始化:
import _ "github.com/sirupsen/logrus/hooks/prometheus"
它不引入任何新标识符,也不占用内存变量名,但副作用实实在在发生,且不可撤回。
为什么不能用普通 import 替代 blank import
因为 Go 编译器禁止未使用的导入 —— 如果你写 import "net/http/pprof" 却没调用它里面的任何函数或类型,编译直接报错:imported and not used: "net/http/pprof"。而 blank import 是唯一被语言允许的“仅执行初始化”的方式。
立即学习“go语言免费学习笔记(深入)”;
注意几个关键限制:
-
import . "xxx"(点导入)不行:它会把包里所有导出名拉进当前作用域,污染命名空间,还可能引发冲突 -
import p "xxx"(别名导入)也不行:你得显式用p.SomeFunc(),但副作用包通常没有要调的公开函数 - blank import 不等于“懒加载”:它在程序启动时就执行,和 main 函数几乎同时发生
blank import 容易踩的坑
最常被忽略的是副作用的隐式依赖和调试困难:
- CI 或静态检查工具(如
go vet、staticcheck)可能因_忽略未使用包告警,导致本该发现的冗余依赖逃逸 - 多个 blank import 包之间若有初始化顺序依赖(比如 A 的
init()依赖 B 已初始化),而它们又没被其他包显式导入,Go 不保证执行顺序 —— 可能 panic 或静默失败 - 本地开发时一切正常,但交叉编译或构建镜像时漏掉某个 blank import 包(比如忘了
go mod tidy),副作用就彻底消失,问题难复现 - 误把需要调用的包写成 blank import:比如想用
mapstructure.Decode(),却写了import _ "github.com/mitchellh/mapstructure"—— 结果编译通过,运行时报 undefined
怎么确认 blank import 真生效了
不能只看编译过没过,得验证副作用是否实际触发:
- 对
net/http/pprof:启动服务后访问/debug/pprof/,有响应说明已注册 - 对 SQL 驱动:尝试
sql.Open("mysql", "..."),如果报错sql: unknown driver "mysql",说明 blank import 没生效或路径写错(比如用了旧版路径import _ "github.com/Go-SQL-Driver/mysql") - 加日志:在驱动包的
init()函数里临时加log.Println("mysql init triggered"),观察启动日志
副作用一旦发生就无法撤销,所以测试阶段务必覆盖所有含 blank import 的构建路径,尤其是 Docker 构建和远程 CI。


















