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

Go语言操作数据库报database is locked错误该如何解决?

报错原因
  • 你使用的是SQLite数据库,它基于文件级锁实现并发控制,读锁未释放时无法获取写锁执行写入操作。
  • 你代码中的defer rows.Close()会延迟到整个函数执行结束时才运行,当你执行INSERT语句的时候,之前查询得到的rows结果集还没有被关闭,对应的数据库连接仍持有读锁,写入操作无法获取独占写锁,因此抛出database is locked错误。
解决方案

你可以选择以下任意一种方式修复问题:

方案1:使用QueryRow替代Query(推荐)

你只需要判断用户是否存在,不需要处理多行结果集,使用QueryRow执行查询会在Scan完成后自动释放资源,不会残留锁:

func createNewUser(w http.ResponseWriter, r *http.Request) {
    var user User
    err := decodeJSONBody(w, r, &user)
    if checkError(w, err) {
        return
    }
    var exists bool
    // 用EXISTS查询效率更高,只需要判断是否存在记录
    err = mainDB.QueryRow("SELECT EXISTS(SELECT 1 FROM users WHERE SAM = ?)", user.Sam).Scan(&exists)
    if checkError(w, err) {
        return
    }
    if exists {
        http.Error(w, "User already exists", http.StatusConflict)
        return
    }
    _, err = mainDB.Exec("INSERT INTO users (SAM, Vorname, Nachname) VALUES (?, ?, ?)", user.Sam, user.Vorname, user.Nachname)
    if checkError(w, err) {
        return
    }
    json.NewEncoder(w).Encode(user)
}

方案2:主动提前关闭rows

如果要保留原有Query写法,在完成查询判断后主动调用rows.Close()释放资源,不要等待defer执行:

rows, err := mainDB.Query("SELECT * FROM users WHERE SAM = ?", user.Sam)
if checkError(w, err) {
    return
}
// 保留defer做兜底,防止异常分支漏关
defer rows.Close()
if rows.Next() {
    rows.Close() // 主动提前关闭
    http.Error(w, "User already exists", http.StatusConflict)
    return
}
rows.Close() // 查询判断完成后主动提前关闭,释放读锁
// 再执行INSERT操作

方案3:优化SQLite配置降低锁冲突概率

打开数据库时添加WAL模式和超时参数,大幅降低读写冲突的概率:

// 打开数据库时添加参数
mainDB, err := sql.Open("sqlite3", "你的数据库文件路径?_journal_mode=WAL&_busy_timeout=5000")
  • _journal_mode=WAL:开启预写日志模式,支持读写并发执行,不会出现读操作阻塞写操作的情况
  • _busy_timeout=5000:设置锁等待超时时间为5秒,拿不到锁时会等待而不是立刻报错

内容的提问来源于stack exchange,提问作者Bαmbus

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 04:36:07