Golang Fiber中用database/sql与pq驱动查询触发空指针panic问题
Go Fiber注册接口触发nil指针panic排查解决
在Golang Fiber框架下编写注册接口,使用database/sql和github.com/lib/pq作为PostgreSQL驱动,调用接口时触发invalid memory address or nil pointer dereference panic。存在一个不执行数据库查询的接口可正常运行,推测数据库连接本身无问题,但仍出现panic,需排查原因。
注册控制器代码
func Register(c *fiber.Ctx) error { user := new(User) if err := c.BodyParser(user); err != nil { return c.Status(400).JSON(fiber.Map{ "error": err.Error(), }) } if user.Username == "" || user.Password == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "error": "Bad request.", "message": "Please include both username and password.", }) } query := `SELECT username FROM user WHERE username = $1` res, err := db.Query(query, user.Username) if err != nil { return err } defer res.Close() return c.JSON("hello") }
调用接口时的错误栈
panic: runtime error: invalid memory address or nil pointer dereference [signal 0xc0000005 code=0x1 addr=0x20 pc=0x865db4] goroutine 22 [running]: database/sql.(*DB).conn(0x0, {0xbee8e0, 0xeee440}, 0x1) C:/Program Files/Go/src/database/sql/sql.go:1282 +0x54 database/sql.(*DB).query(0x9?, {0xbee8e0, 0xeee440}, {0xb670d2, 0x2d}, {0xc00028fb08, 0x1, 0x1}, 0xc0?) C:/Program Files/Go/src/database/sql/sql.go:1721 +0x57 database/sql.(*DB).QueryContext.func1(0x0?) C:/Program Files/Go/src/database/sql/sql.go:1704 +0x4f database/sql.(*DB).retry(0xc00002c440?, 0xc00028f9f0) database/sql.(*DB).QueryContext(0xc00000ad18?, {0xbee8e0?, 0xeee440?}, {0xb670d2?, 0xc00000ac50?}, {0xc00028fb08?, 0xc0000a3af8?, 0x7cbfaf?}) C:/Program Files/Go/src/database/sql/sql.go:1703 +0xc5 database/sql.(*DB).Query(...) C:/Program Files/Go/src/database/sql/sql.go:1717 example/backend/user.Register(0xc000004600) C:/Users/me/Documents/hello/backend/user/user.go:35 +0x1e5 github.com/gofiber/fiber/v2.(*App).next(0xc00000c900, 0xc000004600) C:/Users/me/go/pkg/mod/github.com/gofiber/fiber/v2@v2.48.0/router.go:144 +0x1b2 github.com/gofiber/fiber/v2.(*App).handler(0xc00000c900, 0x81622f?) C:/Users/me/go/pkg/mod/github.com/gofiber/fiber/v2@v2.48.0/router.go:171 +0x78 github.com/valyala/fasthttp.(*Server).serveConn(0xc000160000, {0xbeff80?, 0xc00008e0c0}) C:/Users/me/go/pkg/mod/github.com/valyala/fasthttp@v1.48.0/server.go:2363 +0x11d4 github.com/valyala/fasthttp.(*workerPool).workerFunc(0xc0000d0140, 0xc000096320) C:/Users/me/go/pkg/mod/github.com/valyala/fasthttp@v1.48.0/workerpool.go:224 +0xa4 github.com/valyala/fasthttp.(*workerPool).getCh.func1() C:/Users/me/go/pkg/mod/github.com/valyala/fasthttp@v1.48.0/workerpool.go:196 +0x32 created by github.com/valyala/fasthttp.(*workerPool).getCh in goroutine 1 C:/Users/me/go/pkg/mod/github.com/valyala/fasthttp@v1.48.0/workerpool.go:195 +0x1ab
正常运行的健康检查接口
app.Get("/", func(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).JSON(fiber.Map{ "status": "OK.", "message": "Service is up and running.", }) })
PostgreSQL连接初始化代码
package config import ( "database/sql" "fmt" "os" "github.com/joho/godotenv" _ "github.com/lib/pq" ) var db *sql.DB func PostgreInit() error { // load env if envErr := godotenv.Load(".env"); envErr != nil { fmt.Println("Could not load .env") os.Exit(1) } var ( DB_NAME = os.Getenv("DB_NAME") DB_USER = os.Getenv("DB_USER") DB_PASSWORD = os.Getenv("DB_PASSWORD") DB_HOST = os.Getenv("DB_HOST") DB_PORT = os.Getenv("DB_PORT") ) // connect to db var err error connStr := fmt.Sprintf( "postgres://%v:%v@%v:%v/%v?sslmode=disable", DB_USER, DB_PASSWORD, DB_HOST, DB_PORT, DB_NAME) db, err = sql.Open("postgres", connStr) if err != nil { return err } if err = db.Ping(); err != nil { return err } return nil }
排查分析
从错误栈可以看到,panic发生在database/sql.(*DB).conn(0x0, ...),说明调用Query方法的db变量是nil指针。健康检查接口正常是因为它没有涉及数据库操作,不依赖db变量,所以无法反映数据库连接实例的有效性。
导致db为nil的核心原因有两个:
- 未初始化数据库连接:程序启动时没有调用
config.PostgreInit(),导致config包的db变量始终为nil。 - 变量引用错误:注册控制器中使用的
db不是config包中初始化的实例,而是未赋值的局部/包级变量。
解决步骤
- 确保在程序入口初始化数据库连接
在main.go中,启动Fiber之前必须调用PostgreInit()并处理初始化错误:
func main() { // 初始化数据库连接 if err := config.PostgreInit(); err != nil { log.Fatalf("数据库初始化失败: %v", err) } // 初始化Fiber应用 app := fiber.New() // 注册路由 app.Post("/register", user.Register) app.Get("/", func(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).JSON(fiber.Map{ "status": "OK.", "message": "Service is up and running.", }) }) // 启动服务 if err := app.Listen(":3000"); err != nil { log.Fatalf("服务启动失败: %v", err) } }
- 修正控制器中db变量的引用
在user包的Register函数中,必须明确引用config包的db实例,需要先导入config包,然后修改查询代码:
// 导入config包 import "example/backend/config" func Register(c *fiber.Ctx) error { // ... 其他代码不变 ... // 使用config包的db实例 res, err := config.db.Query(query, user.Username) // ... 其他代码不变 ... }
- 额外修正SQL语法问题
user是PostgreSQL的关键字,直接使用会导致SQL语法错误,需要用双引号包裹表名:
query := `SELECT username FROM "user" WHERE username = $1`
内容的提问来源于stack exchange,提问作者poo
相关产品推荐
相关产品推荐

