2015-06-08 39 views
5

Bolt uzyskuje blokadę pliku w pliku danych, więc wiele procesów nie może otworzyć tej samej bazy danych w tym samym czasie. Otwarcie już otwartej bazy Bolt spowoduje jej zawieszenie, dopóki drugi proces jej nie zamknie.Przechowywanie danych klucza klucza Boltdb wyłącznie w Go

Czy tak jest w przypadku koncepcji łączenia połączeń, na przykład różnych klientów łączących się i uzyskujących dostęp do bazy danych w tym samym czasie? Czy jest to możliwe w boltdb? Podobnie jak w tym samym czasie istnieją różne połączenia do odczytu i zapisu w bazie danych. Jak można je wdrożyć?

+1

Będąc plików oparciu dB, jest mało prawdopodobne, aby zmienić ze względu na sposób zamki pliku roboczego. Jest to prawdopodobnie lepsze pytanie w repozytorium Bolt niż StackOverflow: https://github.com/boltdb/bolt – elithrar

Odpowiedz

9

baza danych Bolt jest zwykle wbudowany w większej programu i nie jest używany przez sieć jak byś ze wspólnych baz danych (myśleć SQLite vs MySQL). Używanie Bolta jest trochę jak posiadanie trwałego map[[]byte][]byte, jeśli to było możliwe. W zależności od tego, co robisz, możesz po prostu użyć czegoś takiego jak Redis.

To powiedziawszy, jeśli chcesz użyć Bolta w ten sposób, nie jest to trudne do zawinięcia z prostym serwerem. Oto przykład, który zapisuje/odczytuje klucze z Bolt DB przez HTTP. Do łączenia połączeń można użyć Keep-Alive.

Kod na: https://github.com/skyec/boltdb-server

package main 

import (
    "flag" 
    "fmt" 
    "io/ioutil" 
    "log" 
    "net/http" 
    "time" 

    "github.com/boltdb/bolt" 
    "github.com/gorilla/mux" 
) 

type server struct { 
    db *bolt.DB 
} 

func newServer(filename string) (s *server, err error) { 
    s = &server{} 
    s.db, err = bolt.Open(filename, 0600, &bolt.Options{Timeout: 1 * time.Second}) 
    return 
} 

func (s *server) Put(bucket, key, contentType string, val []byte) error { 
    return s.db.Update(func(tx *bolt.Tx) error { 
     b, err := tx.CreateBucketIfNotExists([]byte(bucket)) 
     if err != nil { 
      return err 
     } 
     if err = b.Put([]byte(key), val); err != nil { 
      return err 
     } 
     return b.Put([]byte(fmt.Sprintf("%s-ContentType", key)), []byte(contentType)) 
    }) 
} 

func (s *server) Get(bucket, key string) (ct string, data []byte, err error) { 
    s.db.View(func(tx *bolt.Tx) error { 
     b := tx.Bucket([]byte(bucket)) 
     r := b.Get([]byte(key)) 
     if r != nil { 
      data = make([]byte, len(r)) 
      copy(data, r) 
     } 

     r = b.Get([]byte(fmt.Sprintf("%s-ContentType", key))) 
     ct = string(r) 
     return nil 
    }) 
    return 
} 

func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) { 
    vars := mux.Vars(r) 

    if vars["bucket"] == "" || vars["key"] == "" { 
     http.Error(w, "Missing bucket or key", http.StatusBadRequest) 
     return 
    } 

    switch r.Method { 
    case "POST", "PUT": 
     data, err := ioutil.ReadAll(r.Body) 
     if err != nil { 
      http.Error(w, err.Error(), http.StatusInternalServerError) 
      return 
     } 
     err = s.Put(vars["bucket"], vars["key"], r.Header.Get("Content-Type"), data) 
     w.WriteHeader(http.StatusOK) 
    case "GET": 
     ct, data, err := s.Get(vars["bucket"], vars["key"]) 
     if err != nil { 
      http.Error(w, err.Error(), http.StatusInternalServerError) 
      return 
     } 
     w.Header().Add("Content-Type", ct) 
     w.Write(data) 
    } 
} 

func main() { 
    var (
     addr string 
     dbfile string 
    ) 

    flag.StringVar(&addr, "l", ":9988", "Address to listen on") 
    flag.StringVar(&dbfile, "db", "/var/data/bolt.db", "Bolt DB file") 
    flag.Parse() 

    log.Println("Using Bolt DB file:", dbfile) 
    log.Println("Listening on:", addr) 

    server, err := newServer(dbfile) 
    if err != nil { 
     log.Fatalf("Error: %s", err) 
    } 

    router := mux.NewRouter() 
    router.Handle("/v1/buckets/{bucket}/keys/{key}", server) 
    http.Handle("/", router) 

    log.Fatal(http.ListenAndServe(addr, nil)) 
} 
+0

Wielkie dzięki! Rozumiałem, że różni się od innych baz danych, które są udostępniane w sieci. Będą one własnością jednego procesu, który udostępnia API w sieci. –

+0

Brzmi nieźle. Jedną z zalet owijania mechanizmu magazynowania takiego jak ten, jest możliwość zbudowania interfejsu w celu spełnienia konkretnych potrzeb. Czy używasz tylko małych klawiszy i wartości tam, gdzie liczy się przepustowość? Zrób z niego interfejs UDP. A może interfejs protobuf jest dla ciebie bardziej odpowiedni. Zamierzam kontynuować majsterkowanie z tym kodem jako projektem pobocznym. Prawdopodobnie spróbuje każdego z nich. Powodzenia. – SkyeC

+0

Istnieje wiele unikalnych identyfikatorów i wszystkie licytują kwoty w tempie milisekund i muszę je przechowywać i aktualizować bieżące wydatki (łączna suma ofert do tego czasu) jak najszybciej. Schemat, którego używam, to jak - wiadro dla każdego unikalnego identyfikatora i przechowywanie czasu jako klucza i wartości jako stawki. - zwykłe wiadro dla wszystkich unikatowych i aktualizowanie bieżących wydatków w tym gdzie klucz = unikalny identyfikator i wartość = ostatnie bieżące wydatki. Jak w tym przypadku, który interfejs powinienem wybrać. Jak mogę zwiększyć szybkość aktualizacji wartości, tj. Czy powinienem używać db.Update() lub db.Batch() i jak? –

4

Brak koncepcji łączenia połączeń w boltdb, ponieważ nie ma połączenia. Nie jest bazą danych klient/serwer, jest wbudowaną bazą danych (np. Sqlite lub Berkeley-DB).

Boltdb został zaprojektowany tak, aby wiele goroutines tego samego procesu mogło uzyskać dostęp do bazy danych w tym samym czasie (przy użyciu różnych transakcji). Model jest pojedynczy pisarz, wielu czytelników. Boltdb nie jest zaprojektowany do obsługi dostępu z wielu procesów.

Jeśli potrzebny jest program Go używać wbudowanej bazy danych wspierający dostęp z wielu procesów w tym samym czasie, może chcesz przyjrzeć się z owijarki ponad LMDB, takich jak:

+0

Okay! Dzięki. Czy ktokolwiek może utworzyć znacznik boltDB, dzięki czemu można łatwo zarządzać kolejnymi zapytaniami. –

+0

Znacznik boltb właśnie został dodany. –

+0

Dziękujemy za dodanie! –