英文:
golang sqlite database connection pooling
问题
我在同时进行数据库写入和读取时遇到了SQLite引发的问题。当不同的方法在完全相同的时间尝试访问数据库时,就会发生这种情况。
我正在做的类似于这个线程中所做的事情,被接受的答案解释了如何使用数据库事务来避免数据库锁定。
这是我的一些代码:
stmt, err := dbtx.Prepare(`statement`)
if err != nil {
log.Fatal(err)
}
_, err = stmt.Exec(values, values, values)
if err != nil {
log.Fatal(err)
}
err = dbtx.Commit()
if err != nil {
fmt.Println("database lock?")
fmt.Println(err)
dbtx.Rollback()
}
fmt.Println("Database storage complete!")
令人困惑的是,程序在输出以下内容后就退出了:
database lock?
database is locked
Database storage complete!
2014/09/09 18:33:11 database is locked
exit status 1
我不希望程序在数据库锁定时停止,我希望它将数据存储在内存中,并继续进行业务,直到数据库解锁,然后我可以再次尝试。
有没有一些标准的方法可以实现这一点,也许是一种队列或某种数据结构,或者有没有一种特定于数据库的方法来解决这个问题?
为什么程序在输出Database storage complete!
后退出?
编辑:
我相信我已经解决了这个问题,但我不能确定。我正在使用goroutines和一个全局的DB连接。以前,我的代码中的每个函数在调用时都会初始化一个数据库连接。现在,我在包的顶部定义了一个“全局”变量用于DB连接,并在任何例程开始之前进行初始化。以下是代码的要点:
var nDB *sql.DB
在main函数中...
mypkg.InitDB()
go mypkg.RunDatabaseOperations()
mypkg.BeginHTTPWatcher(rtr)
InitDB()
定义如下:
func InitDB() {
fmt.Println("Init DB ...")
var err error
nDB, err = sql.Open("sqlite3", "./first.db")
if err != nil {
log.Fatal(err)
}
if nDB == nil {
log.Fatal(err)
}
fmt.Printf("nDB: %v\n", ODB)
fmt.Println("testing db connection...")
err2 := nDB.Ping()
if err2 != nil {
log.Fatalf("Error on opening database connection: %s", err2.Error())
}
}
因此,RunDatabaseOperations
定期扫描在线资源的数据,并在有更改时将其存储到数据库中(每隔几秒钟一次)。BeginHTTPWatcher
监听HTTP请求,以便可以从运行中的程序中读取数据,并将其通过网络传输给数据的请求者,无论是本地请求还是外部请求。我还没有遇到问题。
英文:
I'm having trouble with SQLite throwing a wrench in my machinery when I call a database write at the same exact time as a read. This happens when different methods happen to attempt to access the database at the same exact time.
What I'm doing is similar to what is being done in this thread, the accepted answer explains how to use database transactions to avoid database locks.
Here is some of my code:
stmt, err := dbtx.Prepare(`statement`)
if err != nil {
log.Fatal(err)
}
_, err = stmt.Exec(values, values, values)
if err != nil {
log.Fatal(err)
}
err = dbtx.Commit()
if err != nil {
fmt.Println("database lock?")
fmt.Println(err)
dbtx.Rollback()
}
fmt.Println("Database storage complete!")
The confusing thing is the program exists after outputting this:
database lock?
database is locked
Database storage complete!
2014/09/09 18:33:11 database is locked
exit status 1
I don't want my program to halt on a database lock, I want it to store the data in memory and continue about its business until the database is unlocked and I can try again.
Is there some standard way I can achieve this, maybe a queue or data structure of some sort, or is there a database-specific way to go about solving this issue?
Why does the program exit after outputting Database storage complete!
?
Edit:
I believe I've fixed the problem, but I can't be sure. I'm using goroutines and a package-wide DB connection. Previously, each func within my code was initializing a database connection when it was called. Now, I have a "global" variable for the DB connection defined at the top of the package and initialized before any routines begin. Here's the code in a nutshell:
var nDB *sql.DB
Later in the main func...
mypkg.InitDB()
go mypkg.RunDatabaseOperations()
mypkg.BeginHTTPWatcher(rtr)
InitDB()
is defined as the following:
func InitDB() {
fmt.Println("Init DB ...")
var err error
nDB, err = sql.Open("sqlite3", "./first.db")
if err != nil {
log.Fatal(err)
}
if nDB == nil {
log.Fatal(err)
}
fmt.Printf("nDB: %v\n", ODB)
fmt.Println("testing db connection...")
err2 := nDB.Ping()
if err2 != nil {
log.Fatalf("Error on opening database connection: %s", err2.Error())
}
}
So, RunDatabaseOperations
scans an online resource for data periodically and stores it into the database when there is a change (once every few seconds). BeginHTTPWatcher
listens for HTTP requests so data can be read from the running program and transmitted over-the-wire to the requestor of the data, whether it is a local or external request. I haven't had a problem yet.
答案1
得分: 8
文档中提到:
> 单个连接实例及其所有派生对象(如预编译语句、备份操作等)在没有外部同步的情况下不能同时从多个goroutine中使用。
(这是一个不同的SQLite驱动程序,但这个限制也适用于你的驱动程序。)
当你使用goroutine时,必须使用单独的数据库连接。
默认情况下,当SQLite遇到被另一个事务锁定的数据库时,会立即中止。
为了允许更多的并发性,你可以设置一个忙等待超时,让它等待其他事务完成。
如果你的SQLite驱动程序支持,可以使用BusyTimeout函数,或直接执行PRAGMA busy_timeout SQL命令。
英文:
The documentation says:
> A single connection instance and all of its derived objects (prepared statements, backup operations, etc.) may NOT be used concurrently from multiple goroutines without external synchronization.
(This is a different SQLite driver, but this restriction also applies to yours.)
When you are using goroutines, you must use separate database connections.
By default, SQLite aborts immediately when it encounters a database that is locked by another transaction.
To allow more concurrency, you can tell it to wait for the other transaction to finish by setting a busy timeout.
Use the BusyTimeout function, if your SQLite driver has it, or execute the PRAGMA busy_timeout SQL command directly.
答案2
得分: 0
请发布更多的代码,这样我们才能更全面地了解正在发生的情况。
不过,我有几个想法。Golang默认使用连接池来管理数据库连接(尽管CENTOS似乎不是这样)。此外,你的程序在等待来自数据库连接池的打开连接时会“停止”。如果你希望在此期间程序的其余部分继续运行,你应该将其作为异步函数运行-在这里查看goroutines。这将有效地使你的程序排队,因为连接将按照请求的顺序分配,只要它们可用。如果你对内部原理感兴趣,可以在这里阅读更多信息。
如果你需要一些关于goroutine的代码片段,让我们知道。
英文:
Please post more of your code, so that we can get a fuller idea of what's going down.
However, here are a couple of thoughts. Golang pools db connections by default (although, CENTOS seems to perhaps not..). Also, your program is 'halting' because it's waiting for an open connection from the db connection pool. If you want the rest of your program to continue during this time, you should run this as an asynchronous function - check out goroutines here. That will effectively cause your program to queue, as you want it to, since connections will be assigned in the order that they were requested whenever they become available. Read more over here if you're interested in the internals.
If you need some code snippets for how your goroutine might look, let us know.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论