在Go语言中连接多个数据库的良好实践是什么?

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

What is good practice to connect to multiple databases in Go?

问题

我正在尝试理解以下做法是好还是坏。

如果确定这是好的/正常的做法,那么我就可以更容易地将多个数据库连接器实现到我的项目中。

情况:我创建了一个API服务器,每次调用API时,以下代码会运行:

ctx := context.Background()

client, err := datastore.NewClient(ctx, "foobar")
if err != nil {
    log.Fatalf("Failed to create client: %v", err)
}

该服务每秒接收到许多请求,我不确定是否应该在启动时只运行一次newClient()函数,还是每次调用API时都运行。

PS: 如果使用另一个连接器,比如MySQL,每次API收到请求时,以下代码将运行:

db, err = sql.Open("mysql", "yourusername:yourpassword@/yourdatabase")
if err != nil {
    panic(err.Error())    
}
英文:

I am trying to understand if the following is good or bad practice.

If it turns out that this is good / normal practice, it makes it easier for me to implement multiple database connectors into my project.

Situation: I have created an API server and every time a call is done to the API this piece of code runs:

ctx := context.Background()

client, err := datastore.NewClient(ctx, "foobar")
if err != nil {
    log.Fatalf("Failed to create client: %v", err)
}

The service gets many requests per second and I'm not sure if I should run the newClient() function only once on startup, or every time the API is called.

PS:<br>
The same would go if another connector would be MySQL. Every time the API gets a request, the following code will run:

db, err = sql.Open(&quot;mysql&quot;, &quot;yourusername:yourpassword@/yourdatabase&quot;)
if err != nil {
    panic(err.Error())    
}

答案1

得分: 3

我不确定datastore.Client,但是database.DB管理一个连接池,可以安全地并发使用,并且你不应该每次执行查询时都调用db.Open。实际上,Open的文档的最后一段清楚地说明了这一点。

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

更新

如果你查看datastore文档中基本操作部分的示例代码中的注释,你会找到推荐的用法。

创建一个datastore客户端。在典型的应用程序中,你会创建一个单独的客户端,用于每个datastore操作。

英文:

I'm not sure about datastore.Client, but database.DB manages a pool of connections, is safe for concurrent use, and you shouldn't really call db.Open every time you want to execute a query. In fact, the last paragraph from Open's documentation states that clearly.

> 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.

Update:

If you take a look at the comment in the example code of the Basic Operations section in the datastore's documentation, you'll find the recommended use.

> Create a datastore client. In a typical application, you would
> create a single client which is reused for every datastore
> operation.

答案2

得分: 1

我没有使用Google Datastore的经验,但我预计它的设计与database/sql包类似。在这种情况下,每个请求使用sql.Open并不是太糟糕,因为连接是惰性初始化的,并且不会验证参数。但最好还是重用连接,因为它可以安全地并发使用。

尽管在使用完毕后关闭数据库是惯用的做法,但sql.DB对象被设计为长期存在。不要频繁地打开和关闭数据库。相反,为每个需要访问的数据存储创建一个sql.DB对象,并在程序完成访问该数据存储之前保持它的打开状态。根据需要传递它,或者以某种全局方式使其可用,但保持它的打开状态。不要在短暂的函数中打开和关闭。相反,将sql.DB作为参数传递给该短暂的函数。

如果不将sql.DB视为长期存在的对象,可能会遇到以下问题:连接的重用和共享不佳、可用网络资源耗尽,或由于大量TCP连接保持在TIME_WAIT状态而导致的间歇性故障。这些问题表明您没有按照database/sql的设计使用它。

默认情况下,它由一个没有连接限制的连接池支持。因此,您可以通过足够的并发goroutine来耗尽它(更不用说某些数据库系统具有相当重的连接初始化)。但是,您可以很容易地限制连接池的大小:http://go-database-sql.org/connection-pool.html

英文:

I don't have any experience with Google Datastore but I expect it to be designed similarly to database/sql package. And in that case it's not terrible to use sql.Open on every request as connections are initialized lazily and it doesn't validate the params. But it's much better to reuse it as it is safe to be used concurrently.

>Although it’s idiomatic to Close() the database when you’re finished with it, the sql.DB object is designed to be long-lived. Don’t Open() and Close() databases frequently. Instead, create one sql.DB object for each distinct datastore you need to access, and keep it until the program is done accessing that datastore. Pass it around as needed, or make it available somehow globally, but keep it open. And don’t Open() and Close() from a short-lived function. Instead, pass the sql.DB into that short-lived function as an argument.
>
> If you don’t treat the sql.DB as a long-lived object, you could experience problems such as poor reuse and sharing of connections, running out of available network resources, or sporadic failures due to a lot of TCP connections remaining in TIME_WAIT status. Such problems are signs that you’re not using database/sql as it was designed.

from: http://go-database-sql.org/accessing.html

By default it is backed by a connection pool without a limit for connections. So you may starve it with enough concurrent goroutines (not to mention some database systems have pretty heavy connection initialization). However, you can limit the pool size pretty easily: http://go-database-sql.org/connection-pool.html

答案3

得分: 0

这是一个基于个人偏好的答案,但是这是我认为的最佳选择。

使用github.com/mjibson/goon包来满足你的datastore需求。它很好地封装了GetPut等调用,并且可以为你进行结构体的编码和解码。它还会自动通过memcache来加速任何键查询。

所以回到你的实际问题。在每个请求中,使用以下两行代码之一是合适的:

g := goon.FromContext(c) // 如果你有上下文(Context)
g := goon.NewGoon(req) // 如果你有http.Request对象
英文:

This is a preference based answer, but here it is for what it's worth.

Use package github.com/mjibson/goon for all your datastore needs. It wraps up calls like Get and Put really nicely and will Marshal/Unmarshal structs for you. It also runs any key queries through memcache automatically to speed things up.

So back to your actual question. It is appropriate with goon to run one of these two lines upon each request.

g := goon.FromContext(c) // if you have Context
g := goon.NewGoon(req) // if you have http.Request

huangapple
  • 本文由 发表于 2017年5月3日 05:09:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/43747267.html
匿名

发表评论

匿名网友

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

确定