Sqlite并发写入性能

我正在用 Golang 和 Sqlite3 编写一个网站,我预计每天有几分钟的时间每秒大约有 1000 次并发写作,所以我做了以下测试(忽略错误检查以看起来更干净):


t1 := time.Now()

tx, _ := db.Begin()

stmt, _ := tx.Prepare("insert into foo(stuff) values(?)")

defer stmt.Close()


for i := 0; i < 1000; i++ {

    _, _ = stmt.Exec(strconv.Itoa(i) + " - ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789,./;'[]-=<>?:()*&^%$#@!~`")

}


tx.Commit()

t2 := time.Now()

log.Println("Writing time: ", t2.Sub(t1))

写入时间约为0.1秒。然后我将循环修改为:


for i := 0; i < 1000; i++ {

    go func(stmt *sql.Stmt, i int) {

        _, err = stmt.Exec(strconv.Itoa(i) + " - ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789,./;'[]-=<>?:()*&^%$#@!~`")

        if err != nil {

            log.Fatal(err)

        }

    }(stmt, i)

}

这给了我神圣的 46.2 秒!我运行了很多次,每次都超过 40 秒!有时甚至超过一分钟!由于 Golang 并发处理每个用户,是否意味着我必须切换数据库才能使网页正常工作?谢谢!


qq_花开花谢_0
浏览 358回答 1
1回答

呼啦一阵风

我最近在 Go 中为网络应用程序评估了 SQLite3 的性能,并了解到它需要一些设置才能远程使用。打开预写日志您需要使用 WAL&nbsp;PRAGMA journal_mode=WAL。这就是为什么你的表现如此糟糕的主要原因。使用 WAL,我可以在几秒钟内完成 10000 次没有事务的并发写入。在交易中,这将是闪电般的快速。禁用连接池我使用mattn/go-sqlite3它打开一个带有SQLITE_OPEN_FULLMUTEX标志的数据库。这意味着每个 SQLite 调用都会被锁保护。一切都会被序列化。这实际上就是你想要的 SQLite。在这种情况下,Go 的问题是您会收到随机错误,告诉您数据库已锁定。原因是因为sql/DB内部的工作方式。它内部为您管理连接池,因此它将打开多个 SQLite 连接,而您不想这样做。为了解决这个问题,我基本上必须禁用池。打电话db.SetMaxOpenConns(1),它会工作。即使在具有数万次并发读取和写入的非常高的负载下,它也能毫无问题地工作。其他解决方案可能是使用SQLITE_OPEN_NOMUTEX在多线程模式下运行 SQLite 并让它为您管理。但是 SQLite 在多线程应用程序中并不真正起作用。读取可以并行发生,但一次只能写入一次。您会偶尔遇到busySQLite 完全正常的错误,但需要您对它们做些什么——当发生这种情况时,您可能不想完全停止写入操作。这就是为什么大多数时候人们要么同步地使用 SQLite,要么通过将调用发送到一个单独的线程来为 SQLite 工作。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go