Начало работы с BoltDB
Член TechCats/панк-программист: Смотри, это называется профессионал
Getting Started
Установить
go get go.etcd.io/bbolt/...
получит два предмета
- go package ->
$GOPATH -
boltcommand line ->$GOBIN
Open Database
Использовать базу данных kv очень просто, вам нужен только путь к файлу для создания полной среды.
package main
import (
"log"
bolt "go.etcd.io/bbolt"
)
func main() {
// Open the my.db data file in your current directory.
// It will be created if it doesn't exist.
db, err := bolt.Open("my.db", 0600, nil)
if err != nil {
log.Fatal(err)
}
defer db.Close()
...
}
Здесь к БД не поддерживает несколько ссылок. Это связано с тем, что ссылка на файл базы данных содержит блокировку файла.file lock.
Если они параллельны, последующие ссылки будут заблокированы.
Для отдельных ссылок можно добавить контроль времени ожидания.
db, err := bolt.Open("my.db", 0600, &bolt.Options{Timeout: 1 * time.Second})
Transaction
Эта статья не актуальна
В отличие от levelDB от Google, bbolt поддерживает транзакции. detailПреимущества и недостатки болта:detailВ то же время bbolt происходит от болта, не слишком отличаясь за исключением того, что bbolt все еще поддерживается.
дела
одновременное чтение и запись
При этом только
- транзакция чтения-записи
- Несколько транзакций только для чтения
действия⚠️: В начале транзакции будет сохранен просмотр данных这意味着事务处理过程中不会由于别处更改而改变
потокобезопасность
Одна транзакция и все объекты (сегменты, ключи), которые она создает, не являются потокобезопасными.
Рекомендуется блокировать или открывать транзакцию для каждой горутины одновременно.
Конечно, изdbСтруктура верхнего уровня этого bbolt создает потокобезопасные транзакции.
тупик
Вышеупомянутые транзакции чтения-записи и транзакции только для чтения отказываются зависеть друг от друга. Конечно, не в той же горутине.
Причина взаимоблокировки заключается в том, что транзакции чтения и записи требуют периодического переназначения файла данных (т.database). Это невозможно при запуске транзакции только для чтения.
чтение и запись транзакций
использовать db.UpdateОткройте бизнес по чтению и записи
err := db.Update(func(tx *bolt.Tx) error{
···
return nil
})
Как упоминалось выше, внутри транзакции представление данных одинаково. (Подробное объяснение заключается в том, что в этой области действия данные представляют вам возможную согласованность)
возвращаемое значение
bboltdb оценивает статус транзакции на основе вашего возвращаемого значения, вы можете добавить любую логику и вернуть ее при возникновении ошибкиreturn errbboltdb откатится, еслиreturn nilзатем зафиксируйте транзакцию.
Всегда проверяйтеUpdateВозвращаемое значение, потому что он вернет информацию, вызвавшую неудачную транзакцию (это вне вашей логики), например давление на жестком диске (это вне вашей логики)
⚠️: сообщение об ошибке, которое вы настраиваете для возврата ошибки, также будет передано.
транзакция только для чтения
использовать db.Viewсоздать транзакцию только для чтения
err := db.View(func(tx *bolt.Tx) error {
···
return nil
})
То же самое, и вы получаете последовательное представление данных.
Конечно, транзакция только для чтения может извлекать информацию только без каких-либо изменений. (кстати, но вы можете сделать копию базы данных, ведь для этого нужно только прочитать данные)
Массовое чтение и запись транзакций
чтение и запись транзакцийdb.UpdateНаконец нужноdatabaseЗафиксируйте изменения, которые будут ждать, пока жесткий диск не будет готов.
Каждый файл для чтения и записи взаимодействует с диском. Это не маленькие накладные расходы.
ты можешь использовать db.BatchНачать пакетную транзакцию. Он будет отправлен партиями в конце (на самом деле открыто несколько горутин).db.BatchТранзакции имеют возможность объединяться и фиксироваться вместе), что снижает накладные расходы. ⚠️:db.BatchРаботает только с горутинами
Использование пакетных транзакций требует компромиссов, а идемпотентные функции заменяются на скорость ⚠️: db.BatchКогда часть транзакции терпит неудачу, она будет пытаться вызвать эти функции транзакции несколько раз, что приведет к непредсказуемой согласованности без событий, если она не является идемпотентной.
Пример: использование переменных вне транзакций, чтобы сделать ваши журналы менее странными
var id uint64
err := db.Batch(func(tx *bolt.Tx) error {
// Find last key in bucket, decode as bigendian uint64, increment
// by one, encode back to []byte, and add new key.
...
id = newValue
return nil
})
if err != nil {
return ...
}
fmt.Println("Allocated ID %d", id)
ручная транзакция
Такие операции, как открытие, откат, создание новых объектов и фиксация транзакций, можно выполнять вручную. из-за себяdb.Update и db.ViewЭто их упаковка: ручной бизнес запомнить, чтобы закрыть (близко)
Включить использование транзакцийdb.Begin(bool)В то же время параметр указывает, возможна ли операция записи. следующее:
- true - чтение и запись транзакций
- false - транзакция только для чтения
// Start a writable transaction.
tx, err := db.Begin(true)
if err != nil {
return err
}
defer tx.Rollback()
// Use the transaction...
_, err := tx.CreateBucket([]byte("MyBucket"))
if err != nil {
return err
}
// Commit the transaction and check for error.
if err := tx.Commit(); err != nil {
return err
}
Using Store ?
Using Buckets
Ведро — это набор пар ключ-значение. В сегменте значение ключа уникально.
Создайте
использовать Tx.CreateBucket() и Tx.CreateBucketIfNotExists()Создайте новый бакет (рекомендуется второй) Принимаемый параметр - имя бакета
Удалить
использовать Tx.DeleteBucket()удалить по имени корзины
пример
func main() {
db, err := bbolt.Open("./data", 0666, nil)
if err != nil {
log.Fatal(err)
}
defer db.Close()
db.Update(func(tx *bbolt.Tx) error {
b, err := tx.CreateBucketIfNotExists([]byte("MyBucket"))
if err != nil {
return fmt.Errorf("create bucket: %v", err)
}
if err = tx.DeleteBucket([]byte("MyBucket")); err != nil {
return err
}
return nil
})
}
Using key/value pairs ?
Самая важная часть — как использовать хранилище kv.Во-первых, нужна корзина для хранения пар ключ-значение.
Put
использоватьBucket.Put()хранить пары ключ-значение, получая два[]byteпараметр типа
db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("MyBucket"))
err := b.Put([]byte("answer"), []byte("42"))
return err
})
Очевидно, что приведенный выше пример устанавливает Pair: key: answer value: 42
Get
использовать Bucket.Get()для запроса значения ключа. параметр является[]byte(не забывайте, что на этот раз мы просто запрашиваем, вы можете использовать транзакции только для чтения)
db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("MyBucket"))
v := b.Get([]byte("answer"))
fmt.Printf("The answer is: %s\n", v)
return nil
})
Внимательно вы заметите,Getне вернетсяerrorДа, это потому чтоGet()Должен работать корректно (если не произойдет системная ошибка), соответственно, при возвратеnil, запрошенная пара ключ-значение не существует. ⚠️: обратите внимание, что значения нулевой длины и отсутствие пар ключ-значение ведут себя по-разному. (один возвращает ноль, другой нет)
func main() {
db, err := bolt.Open("./data.db", 0666, nil)
if err != nil {
log.Fatal(err)
}
defer db.Close()
err = db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucketIfNotExists([]byte("MyBucket"))
if err != nil {
return fmt.Errorf("create bucket: %v", err)
}
if err = b.Put([]byte("answer"), []byte("42")); err != nil {
return err
}
if err = b.Put([]byte("zero"), []byte("")); err != nil {
return err
}
return nil
})
db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("MyBucket"))
v := b.Get([]byte("noexists"))
fmt.Println(reflect.DeepEqual(v, nil)) // false
fmt.Println(v == nil) // true
v = b.Get([]byte("zero"))
fmt.Println(reflect.DeepEqual(v, nil)) // false
fmt.Println(v == nil) // true
return nil
})
}
Delete
использовать Bucket.Delete()удалить пару ключ-значение
db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("MyBucket"))
fmt.Println(b.Get([]byte("answer")))
err := b.Delete([]byte("answer"))
if err != nil {
return err
}
return nil
})
⚠️:Get()Полученное значение среза байта действительно только в текущей транзакции (текущая область действия функции), если вы хотите использовать его в других транзакциях, вам нужно использоватьcopy()скопируйте его в другой фрагмент байта
Tricks
ключ автоинкремента ведра
использовать NextSequence()Чтобы создать ключ автоинкремента, см. пример ниже.
// CreateUser saves u to the store. The new user ID is set on u once the data is persisted.
func (s *Store) CreateUser(u *User) error {
return s.db.Update(func(tx *bolt.Tx) error {
// Retrieve the users bucket.
// This should be created when the DB is first opened.
b := tx.Bucket([]byte("users"))
// Generate ID for the user.
// This returns an error only if the Tx is closed or not writeable.
// That can't happen in an Update() call so I ignore the error check.
id, _ := b.NextSequence()
u.ID = int(id)
// Marshal user data into bytes.
buf, err := json.Marshal(u)
if err != nil {
return err
}
// Persist bytes to users bucket.
return b.Put(itob(u.ID), buf)
})
}
// itob returns an 8-byte big endian representation of v.
func itob(v int) []byte {
b := make([]byte, 8)
binary.BigEndian.PutUint64(b, uint64(v))
return b
}
type User struct {
ID int
...
}
Вложенные сегменты
Очень просто, ведра могут реализовывать вложенное хранилище
func (*Bucket) CreateBucket(key []byte) (*Bucket, error)
func (*Bucket) CreateBucketIfNotExists(key []byte) (*Bucket, error)
func (*Bucket) DeleteBucket(key []byte) error
пример
Предположим, у вас есть мультитенантное приложение, в котором корзина корневого уровня является корзиной учетной записи. Внутри этого сегмента находится последовательность учетных записей, которые сами по себе являются сегментами. В сегменте последовательности (подсегменте) может быть много связанных сегментов (Пользователи, Заметки и т. д.).
// createUser creates a new user in the given account.
func createUser(accountID int, u *User) error {
// Start the transaction.
tx, err := db.Begin(true)
if err != nil {
return err
}
defer tx.Rollback()
// Retrieve the root bucket for the account.
// Assume this has already been created when the account was set up.
root := tx.Bucket([]byte(strconv.FormatUint(accountID, 10)))
// Setup the users bucket.
bkt, err := root.CreateBucketIfNotExists([]byte("USERS"))
if err != nil {
return err
}
// Generate an ID for the new user.
userID, err := bkt.NextSequence()
if err != nil {
return err
}
u.ID = userID
// Marshal and save the encoded user.
if buf, err := json.Marshal(u); err != nil {
return err
} else if err := bkt.Put([]byte(strconv.FormatUint(u.ID, 10)), buf); err != nil {
return err
}
// Commit the transaction.
if err := tx.Commit(); err != nil {
return err
}
return nil
}
перебирать ключи
В ведре пары ключ-значение упорядочены по байтам в соответствии со значением ключа. использовать Bucket.Cursor()перебрать его
db.View(func(tx *bolt.Tx) error {
// Assume bucket exists and has keys
b := tx.Bucket([]byte("MyBucket"))
c := b.Cursor()
for k, v := c.First(); k != nil; k, v = c.Next() {
fmt.Printf("key=%s, value=%s\n", k, v)
}
return nil
})
Курсор имеет 5 способов итерации
-
First()Move to the first key. -
Last()Move to the last key. -
Seek()Move to a specific key.\ -
Next()Move to the next key.\ -
Prev()Move to the previous key.
Каждый метод возвращает(key []byte, value []byte)Два значения Возвращает два, когда значение, на которое указывает метод, не существуетnilзначение, которое возникает, когда:
- При переходе к последней паре ключ-значение снова вызовите
Cursor.Next() - Когда текущая ссылка является первой парой ключ-значение, вызовите
Cursor.Prev() - При использовании 4.
Next()и 5.Prev()метод без использования 1.First()2.Last()3.Seek()При указании начального положения
⚠️Частный случай: когдаkeyдля неnil но value Да nilДа, значит это вложенное ведро,valueЗначение является суббукет, используйтеBucket.Bucket()Метод обращается к подсегменту, параметрkeyценность
db.View(func(tx *bolt.Tx) error {
c := b.Cursor()
fmt.Println(c.First())
k, v := c.Prev()
fmt.Println(k == nil, v == nil) // true,true
if k != nil && v == nil {
subBucket := b.Bucket()
// doanything
}
return nil
})
обход префикса
используя CursorМы можем выполнить специальный обход, например: пройти пары ключ-значение с определенным префиксом
db.View(func(tx *bolt.Tx) error {
// Assume bucket exists and has keys
c := tx.Bucket([]byte("MyBucket")).Cursor()
prefix := []byte("1234")
for k, v := c.Seek(prefix); k != nil && bytes.HasPrefix(k, prefix); k, v = c.Next() {
fmt.Printf("key=%s, value=%s\n", k, v)
}
return nil
})
обход диапазона
Травера в диапазоне, таком как: использование может быть отсортировано кодирование времени (RFC3339) может распространять данные для определенных диапазонов даты
db.View(func(tx *bolt.Tx) error {
// Assume our events bucket exists and has RFC3339 encoded time keys.
c := tx.Bucket([]byte("Events")).Cursor()
// Our time range spans the 90's decade.
min := []byte("1990-01-01T00:00:00Z")
max := []byte("2000-01-01T00:00:00Z")
// Iterate over the 90's.
for k, v := c.Seek(min); k != nil && bytes.Compare(k, max) <= 0; k, v = c.Next() {
fmt.Printf("%s: %s\n", k, v)
}
return nil
})
⚠️: реализация Golang RFC3339 Nano недоступна для заказа
ForEach
В случае значения в ведре вы можете использоватьForEach()траверс
db.View(func(tx *bolt.Tx) error {
// Assume bucket exists and has keys
b := tx.Bucket([]byte("MyBucket"))
b.ForEach(func(k, v []byte) error {
fmt.Printf("key=%s, value=%s\n", k, v)
return nil
})
return nil
})
⚠️: вForEach()Пары ключ-значение, пройденные при необходимостиcopy()Может использоваться только вне транзакции
Advance
Backup
Boltdb — это один файл, поэтому его легко сделать резервной копией. ты можешь использоватьTx.writeto()функция для записи в согласованную базу данных. Если эта функция вызывается из транзакции только для чтения, она выполнит «горячее» резервное копирование, не блокируя другие операции чтения и записи базы данных.
По умолчанию он будет использовать обычный дескриптор файла, который будет использовать кэш страниц операционной системы.
Информацию об оптимизации наборов данных, превышающих объем оперативной памяти, см.[Tx](https://link.zhihu.com/?target=https%3A//godoc.org/go.etcd.io/bbolt%23Tx)документация.
Распространенным вариантом использования является резервное копирование через HTTP, поэтому вы можете использовать что-то вродеcURLТакой инструмент для создания резервных копий базы данных:
func BackupHandleFunc(w http.ResponseWriter, req *http.Request) {
err := db.View(func(tx *bolt.Tx) error {
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Disposition", `attachment; filename="my.db"`)
w.Header().Set("Content-Length", strconv.Itoa(int(tx.Size())))
_, err := tx.WriteTo(w)
return err
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
Затем вы можете сделать резервную копию с помощью этой команды:
$ curl http://localhost/backup > my.db
Или вы можете открыть свой браузер, чтобыhttp://localhost/backup, он будет загружен автоматически.
Если вы хотите сделать резервную копию в другой файл, вы можете использоватьTX.copyfile()Доступность.
Statistics
База данных ведет текущий подсчет многих внутренних операций, чтобы вы могли лучше понять, что происходит. Делая снимки данных в два момента времени, мы можем увидеть, какие операции были выполнены в течение этого периода времени.
Например, мы можем использовать горутину для записи статистики каждые 10 секунд:
go func() {
// Grab the initial stats.
prev := db.Stats()
for {
// Wait for 10s.
time.Sleep(10 * time.Second)
// Grab the current stats and diff them.
stats := db.Stats()
diff := stats.Sub(&prev)
// Encode stats to JSON and print to STDERR.
json.NewEncoder(os.Stderr).Encode(diff)
// Save stats for the next loop.
prev = stats
}
}()
Также полезно передавать эту информацию в мониторинг.
Read-Only Mode
Вы можете включить режим только для чтения, чтобы предотвратить изменения ошибок
db, err := bolt.Open("my.db", 0666, &bolt.Options{ReadOnly: true})
if err != nil {
log.Fatal(err)
}
использовать сейчасdb.Update()Ожидание открытия транзакций чтения и записи будет заблокировано
Mobile Use
Мобильная поддержка осуществляетсяgomobileПредоставляемые инструменты
Create a struct that will contain your database logic and a reference to a *bolt.DB with a initializing constructor that takes in a filepath where the database file will be stored. Neither Android nor iOS require extra permissions or cleanup from using this method.
func NewBoltDB(filepath string) *BoltDB {
db, err := bolt.Open(filepath+"/demo.db", 0600, nil)
if err != nil {
log.Fatal(err)
}
return &BoltDB{db}
}
type BoltDB struct {
db *bolt.DB
...
}
func (b *BoltDB) Path() string {
return b.db.Path()
}
func (b *BoltDB) Close() {
b.db.Close()
}
Database logic should be defined as methods on this wrapper struct. To initialize this struct from the native language (both platforms now sync their local storage to the cloud. These snippets disable that functionality for the database file):
Android
String path;
if (android.os.Build.VERSION.SDK_INT >=android.os.Build.VERSION_CODES.LOLLIPOP){
path = getNoBackupFilesDir().getAbsolutePath();
} else{
path = getFilesDir().getAbsolutePath();
}
Boltmobiledemo.BoltDB boltDB = Boltmobiledemo.NewBoltDB(path)
iOS
- (void)demo {
NSString* path = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
NSUserDomainMask,
YES) objectAtIndex:0];
GoBoltmobiledemoBoltDB * demo = GoBoltmobiledemoNewBoltDB(path);
[self addSkipBackupAttributeToItemAtPath:demo.path];
//Some DB Logic would go here
[demo close];
}
- (BOOL)addSkipBackupAttributeToItemAtPath:(NSString *) filePathString
{
NSURL* URL= [NSURL fileURLWithPath: filePathString];
assert([[NSFileManager defaultManager] fileExistsAtPath: [URL path]]);
NSError *error = nil;
BOOL success = [URL setResourceValue: [NSNumber numberWithBool: YES]
forKey: NSURLIsExcludedFromBackupKey error: &error];
if(!success){
NSLog(@"Error excluding %@ from backup %@", [URL lastPathComponent], error);
}
return success;
}
Расширенное чтение
больше указаний
For more information on getting started with Bolt, check out the following articles:
- Intro to BoltDB: Painless Performant Persistence by Nate Finch.
- Bolt -- an embedded key/value database for Go by Progville
Сравнение с другими базами данных
Postgres, MySQL и другие реляционные базы данных
Реляционные базы данных организуют данные в строки, и к ним можно получить доступ только с помощью SQL. Этот подход обеспечивает гибкость при хранении и запросе данных, но также влечет за собой накладные расходы при анализе и планировании операторов SQL. Bolt получает доступ ко всем данным через ключи среза байтов. Это позволяет Bolt быстро читать и записывать данные по ключам, но не предоставляет встроенной поддержки объединения значений вместе. Большинство реляционных баз данных (кроме SQLite) представляют собой автономные серверы, работающие независимо от приложения. Это дает вашей системе гибкость для подключения нескольких серверов приложений к одному серверу базы данных, но также увеличивает нагрузку на сериализацию и передачу данных по сети. Bolt работает как библиотека, включенная в приложение, поэтому весь доступ к данным должен проходить через процесс приложения. Это приближает данные к вашему приложению, но ограничивает многопроцессный доступ к данным.
LevelDB, RocksDB
LevelDB и ее производные (RocksDB, HyperLevelDB) похожи на Bolt тем, что представляют собой библиотеки, встроенные в приложения, но их базовая структура представляет собой дерево слияния с лог-структурой (дерево LSM). Деревья LSM оптимизируют случайную запись, используя журналы с упреждающей записью и многоуровневые отсортированные файлы, называемые SSTables. Bolt использует внутреннее дерево B+ и только один файл. Оба подхода требуют компромиссов. Если вам нужна высокая скорость произвольной записи (> 10 000 Вт/с) или вам нужно использовать вращающиеся диски, LevelDB может быть хорошим выбором. Если ваше приложение интенсивно читает или сканирует диапазон, Bolt может быть хорошим выбором. Еще одно важное соображение заключается в том, что в LevelDB нет транзакций. Он поддерживает пакетную запись пар ключ/значение и поддерживает чтение моментальных снимков, но не позволяет безопасно выполнять операции сравнения и замены. Bolt поддерживает полностью сериализуемые транзакции ACID.
LMDB
Bolt изначально был портом LMDB, поэтому он похож на архитектуру. Оба используют деревья B+, имеют семантику ACID и полностью сериализуемые транзакции, а также поддерживают MVCC без блокировки с использованием одного модуля записи и нескольких модулей чтения. Эти два проекта несколько расходятся. LMDB фокусируется на чистой производительности, а Bolt — на простоте и удобстве использования. Например, из соображений производительности LMDB допускает несколько небезопасных операций, таких как прямая запись. Bolt решил запретить операции, которые могут оставить базу данных в поврежденном состоянии. Единственным исключением для Bolt являетсяDB.NoSync. API также имеет некоторые отличия. Максимальный размер mmap требуется при открытии LMDB,mdb_envИ Bolt автоматически обработает добавочный размер mmap. LMDB использует несколько флагов для перегрузки функций получения и установки, в то время как Bolt разделяет эти особые случаи на свои собственные функции.
Предостережения и ограничения
Выбор правильного инструмента для работы важен, и Bolt не является исключением. Вот несколько вещей, о которых следует помнить при оценке и использовании Bolt:
-
Bolt отлично подходит для рабочих нагрузок с интенсивным чтением. Производительность последовательной записи также высока, но случайная запись может быть медленной. ты можешь использовать это
DB.Batch()Или добавьте журналы с упреждающей записью, чтобы решить эту проблему. \ -
Болт использует B + дерево внутри внутренне, поэтому могут быть много случайных посещений страницы. SSDS может значительно улучшить производительность по сравнению с прядильными дисками. \
-
Старайтесь избегать чтения длительных транзакций. Когда вы используете функцию копирования для записи Bolt, вы не можете восстановить эти старые страницы в старой транзакции, используя старую страницу. \
-
Фрагменты байтов, возвращаемые Bolt, действительны только на время транзакции. После фиксации или отката транзакций память, на которую они указывают, может быть повторно использована новыми страницами или может быть удалена из виртуальной памяти.
unexpected fault addressПаника возникает при посещении. \ -
Bolt использует эксклюзивные блокировки записи для файлов базы данных, поэтому он не может использоваться несколькими процессами. \
-
будьте осторожны при использовании
Bucket.FillPercent. Установка высокого процента заполнения для сегментов со случайными вставками приведет к очень плохому использованию страниц для базы данных. \ -
Обычно используются большие ковши. Меньшие сегменты приводят к падению использования страницы, когда они превышают размер страницы (обычно 4 КБ). \
-
批量加载大量随机写入新的存储桶可能很慢,因为在提交事务之前页面不会拆分。建议不要在单个事务中将100,000个以上的键/值对随机插入到一个新的存储桶中。 \
-
BOLT использует файл карты памяти, поэтому базовая операционная система может обрабатывать кэш данных. Как правило, операционная система кэширует как можно больше файлов в памяти и освобождает память для других процессов по мере необходимости. Это означает, что Bolt может отображать высокий уровень использования памяти при использовании больших баз данных. Однако это ожидаемо, и операционная система будет освобождать память по мере необходимости. Пока карты памяти болтов соответствуют виртуальному адресному пространству процесса, он может обрабатывать базу данных большего размера, чем доступная физическая оперативная память. На 32-битной системе могут быть проблемы. \
-
Структуры данных в базе данных Bolt отображаются в памяти, поэтому файлы данных будут специфичными для байтов. Это означает, что вы не можете скопировать файл Bolt с компьютера с прямым порядком байтов на компьютер с прямым порядком байтов и заставить его работать. Для большинства пользователей это не проблема, так как большинство современных процессоров имеют очень узкий порядок следования байтов. \
-
Из-за того, как страницы расположены на диске, Bolt не может обрезать файл данных и вернуть доступные страницы на диск. Вместо этого Bolt поддерживает бесплатный список неиспользуемых страниц в своем файле данных. Эти бесплатные страницы можно повторно использовать в будущих транзакциях. Поскольку база данных обычно растет, это хорошо работает во многих случаях. Однако важно отметить, что удаление больших блоков данных не позволит вам освободить это место на диске. Для получения дополнительной информации о распределении страниц см.См. эту заметку. \
материал для чтения
Для встроенной сериализуемой транзакционной базы данных ключ/значение Bolt представляет собой относительно небольшую базу кода (
Лучшая отправная точка - главная точка входа болта:
-
Open()- Инициализировать ссылку на базу данных. Он отвечает за создание базы данных (если она не существует), получение эксклюзивной блокировки файла, чтение метастраниц и сопоставление памяти с файлом. \ -
DB.Begin()-в соответствии сwritableЗначение параметра запускает транзакцию только для чтения или чтения-записи. Это требует кратковременного получения «мета» блокировки для отслеживания открытых транзакций. Одновременно может существовать только одна транзакция чтения-записи, поэтому «rwlock» будет получен во время транзакции чтения-записи. \ -
Bucket.Put()- Запишите пары ключ/значение в корзину. После проверки параметров используйте курсор для перемещения по дереву B+ на страницу и место, где записаны ключи и значения. Как только местоположение найдено, ведро материализует базовую страницу и родительскую страницу страницы как «узлы» в памяти. В этих узлах происходят мутации во время транзакций чтения и записи. Во время фиксации эти изменения сбрасываются на диск. \ -
Bucket.Get()- Получить пары ключ/значение из корзины. При этом курсор используется для перехода на страницу и позицию пары ключ/значение. Во время транзакции только для чтения данные ключа и значения возвращаются как прямая ссылка на базовый файл mmap, поэтому накладные расходы на выделение отсутствуют. Для транзакций чтения и записи эти данные могут относиться либо к файлу mmap, либо к одному из значений узла памяти. \ -
Cursor- Этот объект используется только для обхода дерева B+ страницы диска или узла памяти. Он может найти конкретный ключ, перейти к первому или последнему значению или двигаться вперед или назад. Курсор обрабатывает движение вверх и вниз дерева B+ прозрачно для конечного пользователя. \ -
Tx.Commit()- Преобразовать список грязных узлов и бесплатных страниц в память на страницы, чтобы написать на диск. Письмо на диск затем разделен на два этапа. Во-первых, грязные страницы записываются на диск иfsync()происходить. Во-вторых, напишите новую метастраницу с увеличенным идентификатором транзакции, затемfsync()Происходит другая страница. Эти двухэтапные записи гарантируют, что частично записанные страницы данных будут проигнорированы при сбое, поскольку метастраницы, указывающие на них, не будут записаны. Частично записанные метастраницы недействительны, поскольку они были записаны с контрольными суммами. \
Если у вас есть другие комментарии, которые могут быть полезны другим, отправьте их через запрос на включение.
Другие элементы с использованием болтов
Ниже приведен список общедоступных проектов с открытым исходным кодом, в которых используется Bolt:
- Algernon - A HTTP/2 web server with built-in support for Lua. Uses BoltDB as the default database backend.
- Bazil - A file system that lets your data reside where it is most convenient for it to reside.
- bolter - Command-line app for viewing BoltDB file in your terminal.
- boltcli - the redis-cli for boltdb with Lua script support.
- BoltHold - An embeddable NoSQL store for Go types built on BoltDB
- BoltStore - Session store using Bolt.
- Boltdb Boilerplate - Boilerplate wrapper around bolt aiming to make simple calls one-liners.
- BoltDbWeb - A web based GUI for BoltDB files.
- bleve - A pure Go search engine similar to ElasticSearch that uses Bolt as the default storage backend.
- btcwallet - A bitcoin wallet.
- buckets - a bolt wrapper streamlining simple tx and key scans.
- cayley - Cayley is an open-source graph database using Bolt as optional backend.
- ChainStore - Simple key-value interface to a variety of storage engines organized as a chain of operations.
- Consul - Consul is service discovery and configuration made easy. Distributed, highly available, and datacenter-aware.
- DVID - Added Bolt as optional storage engine and testing it against Basho-tuned leveldb.
- dcrwallet - A wallet for the Decred cryptocurrency.
- drive - drive is an unofficial Google Drive command line client for *NIX operating systems.
- event-shuttle - A Unix system service to collect and reliably deliver messages to Kafka.
- Freehold - An open, secure, and lightweight platform for your files and data.
- Go Report Card - Go code quality report cards as a (free and open source) service.
- GoWebApp - A basic MVC web application in Go using BoltDB.
- GoShort - GoShort is a URL shortener written in Golang and BoltDB for persistent key/value storage and for routing it's using high performent HTTPRouter.
- gopherpit - A web service to manage Go remote import paths with custom domains
- Gitchain - Decentralized, peer-to-peer Git repositories aka "Git meets Bitcoin".
- InfluxDB - Scalable datastore for metrics, events, and real-time analytics.
- ipLocator - A fast ip-geo-location-server using bolt with bloom filters.
- ipxed - Web interface and api for ipxed.
- Ironsmith - A simple, script-driven continuous integration (build - > test -> release) tool, with no external dependencies
- Kala - Kala is a modern job scheduler optimized to run on a single node. It is persistent, JSON over HTTP API, ISO 8601 duration notation, and dependent jobs.
- Key Value Access Langusge (KVAL) - A proposed grammar for key-value datastores offering a bbolt binding.
- LedisDB - A high performance NoSQL, using Bolt as optional storage.
- lru - Easy to use Bolt-backed Least-Recently-Used (LRU) read-through cache with chainable remote stores.
- mbuckets - A Bolt wrapper that allows easy operations on multi level (nested) buckets.
- MetricBase - Single-binary version of Graphite.
- MuLiFS - Music Library Filesystem creates a filesystem to organise your music files.
- NATS - NATS Streaming uses bbolt for message and metadata storage.
- Operation Go: A Routine Mission - An online programming game for Golang using Bolt for user accounts and a leaderboard.
- photosite/session - Sessions for a photo viewing site.
- Prometheus Annotation Server - Annotation server for PromDash & Prometheus service monitoring system.
- reef-pi - reef-pi is an award winning, modular, DIY reef tank controller using easy to learn electronics based on a Raspberry Pi.
- Request Baskets - A web service to collect arbitrary HTTP requests and inspect them via REST API or simple web UI, similar to RequestBin service
- Seaweed File System - Highly scalable distributed key~file system with O(1) disk read.
- stow - a persistence manager for objects backed by boltdb.
- Storm - Simple and powerful ORM for BoltDB.
- SimpleBolt - A simple way to use BoltDB. Deals mainly with strings.
- Skybox Analytics - A standalone funnel analysis tool for web analytics.
- Scuttlebutt - Uses Bolt to store and process all Twitter mentions of GitHub projects.
- tentacool - REST api server to manage system stuff (IP, DNS, Gateway...) on a linux server.
- torrent - Full-featured BitTorrent client package and utilities in Go. BoltDB is a storage backend in development.
- Wiki - A tiny wiki using Goji, BoltDB and Blackfriday.
If you are using Bolt in a project please send a pull request to add it to the list.