Хранилище данных Boltdb-key-Value исключительно в Go

Bolt блокирует файл данных, поэтому несколько процессов не могут одновременно открывать одну и ту же базу данных. Открытие уже открытой базы данных Bolt приведет к ее зависанию до тех пор, пока другой процесс не закроет ее.

В этом случае существует ли какая-либо концепция пула соединений, например, различные клиенты, одновременно подключающиеся и получающие доступ к базе данных? Возможно ли это в BoltDb? Например, в базе данных одновременно читаются и пишутся различные соединения. Как это можно реализовать?


person Raunak Dugar    schedule 08.06.2015    source источник
comment
Будучи базой данных на основе файлов, она вряд ли изменится из-за того, как работают блокировки файлов. Это также, вероятно, лучший вопрос в репозитории Bolt, а не в StackOverflow: github.com/boltdb/bolt   -  person elithrar    schedule 08.06.2015


Ответы (2)


База данных Bolt обычно встроена в более крупную программу и не используется по сети, как в случае с общими базами данных (например, SQLite или MySQL). Использование Bolt немного похоже на наличие постоянного map[[]byte][]byte, если бы это было возможно. В зависимости от того, что вы делаете, вы можете просто использовать что-то вроде Redis.

Тем не менее, если вам нужно использовать Bolt таким образом, это не очень сложно сделать с помощью простого сервера. Вот пример, который записывает/читает ключи из БД Bolt через HTTP. Вы можете использовать Keep-Alive для пула соединений.

Код по адресу: 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))
}
person SkyeC    schedule 08.06.2015
comment
Большое спасибо! Я понял, чем она отличается от других баз данных, которые совместно используются в сети. Она будет принадлежать одному процессу, который предоставляет API по сети. - person Raunak Dugar; 09.06.2015
comment
Звучит отлично. Одним из преимуществ такой оболочки механизма хранения является то, что вы можете создать интерфейс, отвечающий вашим конкретным потребностям. Вы используете небольшие ключи и значения только там, где важна пропускная способность? Сделайте его интерфейсом UDP. Или, может быть, вам больше подходит интерфейс protobuf. Я собираюсь продолжать возиться с этим кодом в качестве побочного проекта. Так что, вероятно, попробую каждый из них. Удачи. - person SkyeC; 09.06.2015
comment
Существуют различные уникальные идентификаторы, и все они предлагают сумму со скоростью миллисекунд, и я должен сохранить ее и обновить их текущие расходы (общую сумму ставок до этого времени) как можно скорее. Схема, которую я использую, похожа на --a Bucket для каждого уникального идентификатора и хранения времени в качестве ключа и значения в качестве ставки. --общее ведро для всех уникальных и обновляющих их текущие расходы в том месте, где ключ=уникальный идентификатор и значение=последние текущие расходы. Как и в этом случае, какой интерфейс я должен предпочесть. И как я могу увеличить скорость обновления значения, т.е. должен ли я использовать db.Update() или db.Batch() и как? - person Raunak Dugar; 10.06.2015
comment
Это своего рода уход от темы для этого вопроса. Чтобы продолжить это обсуждение, не стесняйтесь открывать вопрос в проекте github, который я включил. - person SkyeC; 11.06.2015
comment
Как в Boltdb можно выполнить эффективное разделение данных, повторную балансировку данных и распределение данных? - person Raunak Dugar; 11.06.2015
comment
Это пригодный для использования код, за исключением того факта, что метод Get не проверяет, возвращается ли действительное ведро. Что-то вроде... if b == nil { fmt.Printf(Ого! Сегмент %s не найден!, b) return fmt.Errorf(Корзина %q не найден!, b) } - person auro; 26.11.2015

В Boltdb нет концепции пула соединений, потому что нет соединения. Это не база данных клиент/сервер, это встроенная база данных (например, sqlite или Berkeley-DB).

Boltdb разработан таким образом, что несколько горутин одного и того же процесса могут одновременно обращаться к базе данных (используя разные транзакции). Модель - один писатель, несколько читателей. Boltdb не предназначен для поддержки доступа из нескольких процессов.

Если вам нужна программа Go для использования встроенной базы данных, поддерживающей одновременный доступ из нескольких процессов, вы можете взглянуть на обертки над LMDB, такие как:

person Didier Spezia    schedule 08.06.2015
comment
Хорошо! Спасибо. Может ли кто-нибудь создать тег BoltDB, чтобы легко управлять дальнейшими запросами. - person Raunak Dugar; 09.06.2015
comment
Только что добавлен тег Boltb. - person Didier Spezia; 10.06.2015
comment
Спасибо тебе за добавление ! - person Raunak Dugar; 11.06.2015