SQLite очень медленно происходит вставка в таблицу
Почему вставка в таблицу происходит очень медленно. Для примера я решил вставить 1000 записей, что по времени вылилось ~1 минуту? Это вообще как? Что я делаю не так? Вот пример:
package main
import (
"database/sql"
"log"
_ "github.com/mattn/go-sqlite3"
)
type record struct {
field1 string
field2 string
field3 string
}
func main() {
db, err := sql.Open("sqlite3", "./data.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
OpenCreateTable(db)
for i := 0; i < 1000; i++ {
if err := Insert(db, record{"One", "Two", "Three"}); err != nil {
log.Fatal(err)
}
}
}
func OpenCreateTable(db *sql.DB) error {
stmt, err := db.Prepare(`CREATE TABLE IF NOT EXISTS records (id INTEGER PRIMARY KEY, field1 TEXT, field2 TEXT, field3 TEXT);`)
if err != nil {
return err
}
if _, err = stmt.Exec(); err != nil {
return err
}
return nil
}
func Insert(db *sql.DB, r record) error {
stmt, err := db.Prepare(`INSERT INTO records (field1, field2, field3) VALUES (?, ?, ?)`)
if err != nil {
return err
}
if _, err = stmt.Exec(r.field1, r.field2, r.field3); err != nil {
return err
}
return nil
}
Конфиг компа:
Процессор Intel(R) Core(TM) i7-6800K CPU @ 3.40GHz, 3401 МГц, ядер: 6, логических процессоров: 12
16Gb DDR3
ST1000DM010-2EP102
Ответы (1 шт):
Оберните вашу последовательность вставок в транзакцию
package main
import (
"database/sql"
"fmt"
"log"
"time"
_ "github.com/mattn/go-sqlite3"
)
type record struct {
field1 string
field2 string
field3 string
}
func main() {
db, err := sql.Open("sqlite3", "./data.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
start := time.Now()
OpenCreateTable(db)
if err := Begin(db); err != nil {
log.Fatal(err)
}
for i := 0; i < 1000; i++ {
if err := Insert(db, record{"One", "Two", "Three"}); err != nil {
log.Fatal(err)
}
}
Commit(db)
dt := time.Now().Sub(start)
fmt.Println("Duration: ", dt)
}
func OpenCreateTable(db *sql.DB) error {
stmt, err := db.Prepare(`CREATE TABLE IF NOT EXISTS records (id INTEGER PRIMARY KEY, field1 TEXT, field2 TEXT, field3 TEXT);`)
if err != nil {
return err
}
if _, err = stmt.Exec(); err != nil {
return err
}
return nil
}
func Insert(db *sql.DB, r record) error {
stmt, err := db.Prepare(`INSERT INTO records (field1, field2, field3) VALUES (?, ?, ?)`)
if err != nil {
return err
}
if _, err = stmt.Exec(r.field1, r.field2, r.field3); err != nil {
return err
}
return nil
}
func Begin(db *sql.DB) error {
stmt, err := db.Prepare(`BEGIN`)
if err != nil {
return err
}
if _, err = stmt.Exec(); err != nil {
return err
}
return nil
}
func Commit(db *sql.DB) error {
stmt, err := db.Prepare(`COMMIT`)
if err != nil {
return err
}
if _, err = stmt.Exec(); err != nil {
return err
}
return nil
}
У меня получилось 25-30 миллисекунд.
UPDATE
Если вынести Prepare для INSERT во внешний код, то ускорение практически в два раза.
package main
import (
"database/sql"
"fmt"
"log"
"time"
_ "github.com/mattn/go-sqlite3"
)
type record struct {
field1 string
field2 string
field3 string
}
var (
insertStmt *sql.Stmt
)
func main() {
db, err := sql.Open("sqlite3", "./data.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
start := time.Now()
OpenCreateTable(db)
if err := Begin(db); err != nil {
log.Fatal(err)
}
insertStmt, err = db.Prepare(`INSERT INTO records (field1, field2, field3) VALUES (?, ?, ?)`)
if err != nil {
panic(err)
}
for i := 0; i < 1000; i++ {
if err := Insert(db, record{"One", "Two", "Three"}); err != nil {
log.Fatal(err)
}
}
Commit(db)
dt := time.Now().Sub(start)
fmt.Println("Duration: ", dt)
}
func OpenCreateTable(db *sql.DB) error {
stmt, err := db.Prepare(`CREATE TABLE IF NOT EXISTS records (id INTEGER PRIMARY KEY, field1 TEXT, field2 TEXT, field3 TEXT);`)
if err != nil {
panic(err)
}
if _, err = stmt.Exec(); err != nil {
return err
}
return nil
}
func Insert(db *sql.DB, r record) error {
var err error
if _, err = insertStmt.Exec(r.field1, r.field2, r.field3); err != nil {
return err
}
return nil
}
func Begin(db *sql.DB) error {
stmt, err := db.Prepare(`BEGIN`)
if err != nil {
return err
}
if _, err = stmt.Exec(); err != nil {
return err
}
return nil
}
func Commit(db *sql.DB) error {
stmt, err := db.Prepare(`COMMIT`)
if err != nil {
return err
}
if _, err = stmt.Exec(); err != nil {
return err
}
return nil
}
Получается стабильно 15 миллисекунд
Ещё кучу миллисекунд можно добыть, если отключить синхронный доступ. Для этого нужно указать ключ _sync=0 в строке где открывается файл:
sql.Open("sqlite3", "./data.db?_sync=0")
Можно до кучи добавить &_journal=OFF, но там выигрыш получается не столь значительный.