Какой самый быстрый способ прочитать таблицу sqlite3 в golang?
package main
import (
"fmt"
"database/sql"
_ "github.com/mattn/go-sqlite3"
"log"
"time"
)
func main() {
start := time.Now()
db, err := sql.Open("sqlite3", "/Users/robertking/go/src/bitbucket.org/thematicanalysis/optimization_test/robs.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
rows, err := db.Query("select * from data")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
}
err = rows.Err()
if err != nil {
log.Fatal(err)
}
fmt.Println(time.Since(start))
}
Это занимает 8 секунд в Go, потому что .Next
slow. В python a fetchall
занимает всего 4 секунды! Я переписываю GO, чтобы получить производительность, не теряя производительности.
Вот код python, я не смог найти эквивалент fetchall
в go:
import time
start = time.time()
import sqlite3
conn = sqlite3.connect('/Users/robertking/go/src/bitbucket.org/thematicanalysis/optimization_test/robs.db')
c = conn.cursor()
c.execute("SELECT * FROM data")
x = c.fetchall()
print time.time() - start
Изменить: добавление баунти. Я читаю данные в go, python и C, вот результаты. Не хотите использовать C, но будет придерживаться python, если GO не быстрее.:
py: 2.45s
go: 2.13s (using github.com/mxk/go-sqlite/sqlite3 instead of github.com/mattn/go-sqlite3)
c: 0.32s
Мне кажется, что go должен быть ближе к стороне? кто-нибудь знает, как сделать это быстрее? можно ли избежать мьютекса с режимом readonly?
изменить:
Кажется, что все реализации sqlite3 медленны (слишком много отражений и слишком много вызовов cgo для конверсий). Поэтому мне придется просто написать собственный интерфейс.
Здесь схема:
CREATE TABLE mytable
(
c0 REAL,
c1 INTEGER,
c15 TEXT,
c16 TEXT,
c17 TEXT,
c18 TEXT,
c19 TEXT,
c47 TEXT,
c74 REAL DEFAULT 0,
c77 TEXT,
c101 TEXT,
c103 TEXT,
c108 TEXT,
c110 TEXT,
c125 TEXT,
c126 TEXT,
c127 REAL DEFAULT 0,
x INTEGER
PRIMARY KEY
);
и запрос является динамическим, но обычно что-то вроде этого:
SELECT c77,c77,c125,c126,c127,c74 from mytable
изменить:
похоже, что я разблокирую реализацию sqlite3 и сделаю несколько методов, которые будут сосредоточены на производительности,
это пример кода, который намного быстрее:.
package main
/*
#cgo LDFLAGS: -l sqlite3
#include "sqlite3.h"
*/
import "C"
import (
//"database/sql"
"log"
"reflect"
"unsafe"
)
type Row struct {
v77 string
v125 string
v126 string
v127 float64
v74 float64
}
// cStr returns a pointer to the first byte in s.
func cStr(s string) *C.char {
h := (*reflect.StringHeader)(unsafe.Pointer(&s))
return (*C.char)(unsafe.Pointer(h.Data))
}
func main() {
getDataFromSqlite()
}
func getDataFromSqlite() {
var db *C.sqlite3
name := "../data_dbs/all_columns.db"
rc := C.sqlite3_open_v2(cStr(name+"\x00"), &db, C.SQLITE_OPEN_READONLY, nil)
var stmt *C.sqlite3_stmt;
rc = C.sqlite3_prepare_v2(db, cStr("SELECT c77,c125,c126,c127,c74 from data\x00"), C.int(-1), &stmt, nil);
rc = C.sqlite3_reset(stmt);
var result C.double
result = 0.0
rc = C.sqlite3_step(stmt)
for rc == C.SQLITE_ROW {
C.GoString((*C.char)(unsafe.Pointer(C.sqlite3_column_text(stmt, 0))))
C.GoString((*C.char)(unsafe.Pointer(C.sqlite3_column_text(stmt, 1))))
C.GoString((*C.char)(unsafe.Pointer(C.sqlite3_column_text(stmt, 2))))
C.sqlite3_column_double(stmt, 3)
result += C.sqlite3_column_double(stmt, 4)
rc = C.sqlite3_step(stmt)
}
log.Println(result)
}