How do I handle opening/closing Db connection in a Go app?

huangapple go评论90阅读模式
英文:

How do I handle opening/closing Db connection in a Go app?

问题

我在我的Web API应用程序中有一组函数。它们对Postgres数据库中的数据执行一些操作。

func CreateUser() {
    db, err := sql.Open("postgres", "user=postgres password=password dbname=api_dev sslmode=disable")
    // 在这里执行一些数据库操作
}

我认为这些函数应该独立于彼此与数据库进行交互,所以现在每个函数内部都有sql.Open(...)。我不知道这是否是正确管理数据库连接的方式。

我应该在应用程序启动时的某个地方打开连接,并将db作为参数传递给相应的函数,而不是在每个函数中打开连接吗?

英文:

I have a set of functions in my web API app. They perform some operations on the data in the Postgres database.

func CreateUser () {
    db, err := sql.Open("postgres", "user=postgres password=password dbname=api_dev sslmode=disable")
    // Do some db operations here
}

I suppose functions should work with db independently from each other, so now I have sql.Open(...) inside each function. I don't know if it's a correct way to manage db connection.

Should I open it somewhere once the app starts and pass db as an argument to the corresponding functions instead of opening the connection in every function?

答案1

得分: 8

每次需要时打开数据库连接是一种资源浪费,而且速度较慢。

相反,你应该在应用程序启动时(或首次需要时)创建一个 sql.DB,并将其传递到需要它的地方(例如作为函数参数或通过某些上下文),或者将其设置为全局变量,以便所有人都可以访问它。它可以安全地在多个 goroutine 中调用。

引用自 sql.Open() 的文档:

> 返回的 DB 可以安全地被多个 goroutine 并发使用,并且维护自己的空闲连接池。因此,应该只调用一次 Open 函数。很少需要关闭 DB。

你可以使用一个包的 init() 函数来进行初始化:

var db *sql.DB

func init() {
    var err error
    db, err = sql.Open("yourdriver", "yourDs")
    if err != nil {
        log.Fatal("Invalid DB config:", err)
    }
}

这里需要注意的一点是,sql.Open() 可能不会创建实际的数据库连接,它可能只是验证其参数。要测试是否可以实际连接到数据库,可以使用 DB.Ping(),例如:

func init() {
    var err error
    db, err = sql.Open("yourdriver", "yourDs")
    if err != nil {
        log.Fatal("Invalid DB config:", err)
    }
    if err = db.Ping(); err != nil {
        log.Fatal("DB unreachable:", err)
    }
}
英文:

Opening a db connection every time it's needed is a waste of resources and it's slow.

Instead, you should create an sql.DB once, when your application starts (or on first demand), and either pass it where it is needed (e.g. as a function parameter or via some context), or simply make it a global variable and so everyone can access it. It's safe to call from multiple goroutines.

Quoting from the doc of sql.Open():

> The returned DB is safe for concurrent use by multiple goroutines and maintains its own pool of idle connections. Thus, the Open function should be called just once. It is rarely necessary to close a DB.

You may use a package init() function to initialize it:

var db *sql.DB

func init() {
    var err error
    db, err = sql.Open("yourdriver", "yourDs")
    if err != nil {
        log.Fatal("Invalid DB config:", err)
    }
}

One thing to note here is that sql.Open() may not create an actual connection to your DB, it may just validate its arguments. To test if you can actually connect to the db, use DB.Ping(), e.g.:

func init() {
    var err error
    db, err = sql.Open("yourdriver", "yourDs")
    if err != nil {
        log.Fatal("Invalid DB config:", err)
    }
    if err = db.Ping(); err != nil {
        log.Fatal("DB unreachable:", err)
    }
}

答案2

得分: 0

我将使用一个PostgreSQL的示例。

package main

// 导入必要的包,不要忘记导入PostgreSQL驱动
import (
	"database/sql"
	_ "github.com/lib/pq" // PostgreSQL驱动
)

// 在包范围内初始化连接
var db *sql.DB

// 为连接编写init函数
func init() {
	var err error
	db, err = sql.Open("postgres", "connectionString")
	// 连接字符串示例 => 'postgres://username:password@localhost/dbName?sslmode=disable'
	if err != nil {
		panic(err)
	}
	err = db.Ping()
	if err != nil {
		panic(err)
	}
	// 注意,在init函数中我们没有使用defer db.Close(),因为连接将在init之后关闭。你可以在main函数中关闭它,或者省略关闭操作。
}

// 主函数
func main() {
	defer db.Close() // 可选
	// 运行你的数据库函数
}

// 查看这个示例
// https://play.golang.org/p/FAiGbqeJG0H

希望对你有帮助!

英文:

> I will use a postgres example

package main

> import necessary packages and don't forget the postgres driver

import (
  "database/sql"
  _ "github.com/lib/pq" //postgres driver
)

> initialize your connection in the package scope

var db *sql.DB

> have an init function for your connection

func init() {
  var err error
  db, err = sql.open("postgres", "connectionString")
  //connectioString example => 'postgres://username:password@localhost/dbName?sslmode=disable'
  if err != nil {
    panic(err)
  }
  err = db.Ping()
  if err != nil {
	panic(err)
  }
  // note, we haven't deffered db.Close() at the init function since the connection will close after init. you could close it at main or ommit it
}

>main function

func main() {
defer db.Close() //optional
//run your db functions
}

> checkout this example
https://play.golang.org/p/FAiGbqeJG0H

huangapple
  • 本文由 发表于 2016年11月14日 18:53:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/40587008.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定