2014-09-09 17 views
9

Mam problem z SQLite rzuca klucz w mojej maszynie, gdy dzwonię do bazy danych pisać w tym samym czasie, co odczyt. Dzieje się tak, gdy różne metody próbują uzyskać dostęp do bazy danych w tym samym czasie.połączenie bazy danych sqlite golang

To co robię jest podobne do tego, co robi się w this thread, zaakceptowana odpowiedź wyjaśnia, w jaki sposób korzystać z transakcji bazy danych, aby uniknąć blokad bazy danych.

Oto niektóre z moich kodu:

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!") 

Mylące jest to program istnieje po to wyprowadzanie:

database lock? 
database is locked 
Database storage complete! 
2014/09/09 18:33:11 database is locked 
exit status 1 

Nie chcę mój program do zatrzymania na blokadę bazy danych, Chcę, aby przechowywał dane w pamięci i kontynuował działalność, dopóki baza danych nie zostanie odblokowana i będę mógł spróbować ponownie.

Czy istnieje jakiś standardowy sposób, w jaki mogę to osiągnąć, może kolejka lub struktura danych jakiegoś rodzaju, lub czy istnieje jakiś sposób rozwiązania problemu z bazą danych?

Dlaczego program kończy działanie po wyprowadzeniu Database storage complete!?

Edit:

wierzę, że problem został rozwiązany, ale nie mogę być pewien. Używam goroutines i połączenia DB w pakiecie. Poprzednio każdy func w moim kodzie był inicjalizacją połączenia z bazą danych, kiedy został wywołany. Teraz mam "globalną" zmienną dla połączenia DB zdefiniowanego na górze pakietu i zainicjowaną przed rozpoczęciem jakichkolwiek procedur. Oto kod w pigułce:

var nDB *sql.DB 

Później w głównej func ...

mypkg.InitDB() 
go mypkg.RunDatabaseOperations() 
mypkg.BeginHTTPWatcher(rtr) 

InitDB() jest zdefiniowana jako następujące:

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()) 
    } 
} 

Więc RunDatabaseOperations skanuje zasobów internetowych dla danych okresowo i zapisuje je w bazie danych, gdy następuje zmiana (raz na kilka sekund). BeginHTTPWatcher nasłuchuje żądań HTTP, aby dane mogły być odczytywane z uruchomionego programu i przesyłane przez sieć do żądającego danych, niezależnie od tego, czy jest to żądanie lokalne czy zewnętrzne. Jeszcze nie miałem problemu.

+1

'2014/09/09 18:33:11 baza danych jest zablokowana' wygląda jak 'log.Fatal (err)' praca. Czy możesz podać więcej informacji, aby zrobić próbną próbkę? Którego dostawcy sqlite używasz? – RoninDev

+1

Używam 'github.com/mattn/go-sqlite3' – bvpx

+0

Wydaje się, że problem współbieżności ... Czy uruchomiłeś ten kod równolegle? Jeśli tak, opublikuj więcej kodu, abyśmy mogli zobaczyć, co się dzieje. –

Odpowiedz

7

The documentation mówi:

jednej instancji połączenia i wszystkich jego pochodnych obiektów (sporządzone sprawozdanie, operacje tworzenia kopii zapasowych, etc.) nie mogą być używane jednocześnie z wielu goroutines bez synchronizacji zewnętrznej.

(To jest inny sterownik SQLite, ale ograniczenie to odnosi się również do Ciebie.)

Kiedy używasz goroutines, wy must stosowanie osobnych połączeń z bazą danych.

Domyślnie SQLite przerywa się natychmiast po napotkaniu bazy danych zablokowanej przez inną transakcję. Aby zezwolić na większą współbieżność, możesz nakazać mu oczekiwanie na zakończenie drugiej transakcji poprzez ustawienie limitu czasu zajętości.

Użyj funkcji BusyTimeout, jeśli posiada ją twój sterownik SQLite, lub wykonaj bezpośrednio polecenie SQL PRAGMA busy_timeout.

0

Proszę napisać więcej kodu, aby uzyskać pełniejszy obraz tego, co się dzieje.

Jednak tutaj jest kilka myśli. Golang domyślnie buforuje połączenia db (choć wydaje się, że CENTOS może nie ...). Ponadto program "zatrzymuje się", ponieważ oczekuje na otwarte połączenie z puli połączeń db. Jeśli chcesz, aby pozostała część programu była kontynuowana w tym czasie, uruchom tę funkcję jako funkcję asynchroniczną - sprawdź numer goroutines here. To skutecznie spowoduje, że twój program będzie w kolejce, jak chcesz, ponieważ połączenia będą przydzielane w kolejności, w jakiej były żądane, kiedy tylko będą dostępne. Read more over here jeśli interesujesz się obiektami wewnętrznymi.

Jeśli potrzebujesz fragmentów kodu, aby dowiedzieć się, jak może wyglądać twoja gorynta, daj nam znać.

+0

Zaktualizowałem mój oryginalny wpis za pomocą kodu, którego używam teraz, i wszystko wydaje się działać dobrze, ale nie jestem pewien, czy używam goroutines i połączenia DB w pakiecie poprawnie, czy też nie. – bvpx