You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的核心原因有两个:

  1. 未初始化数据库连接:程序启动时没有调用config.PostgreInit(),导致config包的db变量始终为nil。
  2. 变量引用错误:注册控制器中使用的db不是config包中初始化的实例,而是未赋值的局部/包级变量。

解决步骤

  1. 确保在程序入口初始化数据库连接
    在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)
    }
}
  1. 修正控制器中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)

    // ... 其他代码不变 ...
}
  1. 额外修正SQL语法问题
    user是PostgreSQL的关键字,直接使用会导致SQL语法错误,需要用双引号包裹表名:
query := `SELECT username FROM "user" WHERE username = $1`

内容的提问来源于stack exchange,提问作者poo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 09:45:05