Открыть сервис

BoltDB

BoltDB — это встраиваемая файловая база данных для языка программирования Go, реализующая структуру данных «B+‑дерево» и обеспечивающая хранение пар «ключ — значение» с транзакционной поддержкой и изоляцией по протоколу ACID. BoltDB не использует клиент-серверную архитектуру и не требует отдельного процесса СУБД: данные хранятся в одном бинарном файле, с которым приложение взаимодействует напрямую через функции Go‑библиотеки.

История

Проект BoltDB был создан Беном Джонсоном (Ben Johnson) в 2013 году как ответ на потребность в простой, надёжной и производительной встраиваемой БД для Go. Изначально разработчик ориентировался на опыт использования Berkeley DB и LMDB, но хотел получить решение, полностью написанное на Go, без привязок к C‑библиотекам. Первая стабильная версия (v1.0) вышла в 2014 году.

В 2017 году разработка BoltDB была официально остановлена: автор объявил, что больше не поддерживает проект. Однако код остался доступным на GitHub под лицензией MIT, и сообщество продолжило использовать библиотеку. На основе BoltDB был создан форк Bbolt, который в настоящее время поддерживается компанией CoreOS (подразделение Red Hat) и используется в таких проектах, как etcd и Kubernetes. Bbolt сохранил API и внутреннюю архитектуру BoltDB, добавив исправления ошибок и оптимизации.

Архитектура и принципы работы

BoltDB хранит все данные в одном файле на диске. Внутренняя организация основана на B+‑дереве — сбалансированном дереве поиска, в котором все ключи хранятся в листьях, а внутренние узлы служат для навигации. Это обеспечивает логарифмическую сложность операций поиска, вставки и удаления.

Страницы и узлы

Файл BoltDB делится на страницы фиксированного размера (по умолчанию 4096 байт). Каждая страница относится к одному из типов:

  • мета-страницы — содержат служебную информацию о состоянии базы (номер транзакции, корень дерева, размер файла);
  • узлы (nodes) — внутренние или листовые узлы B+‑дерева;
  • ветки (branches) — страницы, содержащие указатели на дочерние узлы;
  • листья (leaves) — страницы, хранящие непосредственно пары «ключ — значение».

При вставке нового элемента BoltDB может выполнять разделение страницы (split), если она переполнена, или слияние (merge) при удалении, чтобы поддерживать сбалансированность дерева.

Транзакции

BoltDB поддерживает два типа транзакций:

  • только чтение (ReadOnly) — несколько таких транзакций могут выполняться параллельно, так как они не модифицируют данные;
  • чтение-запись (ReadWrite) — только одна транзакция этого типа может быть активна в любой момент времени (последовательный доступ).

Все транзакции удовлетворяют требованиям ACID:

  • Атомарность — изменения либо применяются полностью, либо откатываются.
  • Согласованность — база данных всегда остаётся в целостном состоянии.
  • Изоляция — транзакции не видят незавершённых изменений других транзакций.
  • Долговечность — после успешного завершения транзакции данные гарантированно записаны на диск.

Курсоры и итерация

Для навигации по данным BoltDB предоставляет курсоры (Cursor), которые позволяют последовательно обходить все пары «ключ — значение» в отсортированном порядке. Курсор можно перемещать в начало, конец, к определённому ключу, а также выполнять поиск первого ключа, большего или равного заданному.

Классификация и сравнение

BoltDB относится к классу встраиваемых (embedded) key-value хранилищ. В отличие от реляционных СУБД (PostgreSQL, MySQL) или документоориентированных БД (MongoDB), BoltDB не поддерживает SQL, индексы на произвольные поля, полнотекстовый поиск или репликацию. Его основное преимущество — минимализм и предсказуемая производительность для сценариев, где требуется быстрый доступ к данным по ключу.

Сравнение с другими популярными встраиваемыми БД для Go:

СвойствоBoltDB / BboltBadgerLevelDB (Go‑порт)
Тип дереваB+‑деревоLSM‑деревоLSM‑дерево
Транзакции ACIDДа (одна пишущая)Да (одна пишущая)Нет (только пакетная запись)
Производительность чтенияВысокая (O(log n))Высокая (O(log n))Высокая (O(log n))
Производительность записиСредняя (из-за синхронной записи)Высокая (асинхронная запись)Высокая
Размер базы данныхОграничен размером файлаОграничен размером файлаОграничен размером файла
ПоддержкаBbolt (активен)АктивенАктивен (dgraph-io/badger)

Применение

BoltDB (и его форк Bbolt) широко используется в инфраструктурных проектах, где требуется надёжное локальное хранение небольших объёмов данных (обычно до нескольких гигабайт). Основные области применения:

  • Распределённые системы: etcd (хранилище конфигураций для Kubernetes) использует Bbolt для хранения ключей и значений.
  • Мониторинг и телеметрия: Prometheus может использовать BoltDB для хранения метрик (в некоторых конфигурациях).
  • Локальные кэши и сессии: веб-приложения на Go часто применяют BoltDB для хранения сессий пользователей или кэша данных из внешних API.
  • Прототипирование и малые проекты: благодаря простоте настройки (нет сервера, нет конфигурационных файлов) BoltDB популярен при разработке прототипов и инструментов командной строки.

Ограничения

  • Однопоточная запись: только одна пишущая транзакция может быть активна, что ограничивает пропускную способность при высокой нагрузке на запись.
  • Размер базы данных: хотя теоретически файл может быть очень большим, на практике производительность падает при размерах свыше 10–20 ГБ из-за фрагментации и времени на слияние страниц.
  • Отсутствие встроенной репликации: BoltDB не поддерживает репликацию или шардирование — это локальное хранилище для одного узла.
  • Нет поддержки SQL и сложных запросов: все операции выполняются только по первичному ключу.

Пример использования (Go)

```go package main

import ( "log" "github.com/boltdb/bolt" )

func main() { // Открываем или создаём файл базы данных db, err := bolt.Open("my.db", 0600, nil) if err != nil { log.Fatal(err) } defer db.Close()

// Создаём корзину (bucket) и записываем значение err = db.Update(func(tx *bolt.Tx) error { b, err := tx.CreateBucketIfNotExists([]byte("MyBucket")) if err != nil { return err } return b.Put([]byte("key"), []byte("value")) }) if err != nil { log.Fatal(err) }

// Читаем значение err = db.View(func(tx *bolt.Tx) error { b := tx.Bucket([]byte("MyBucket")) if b == nil { return nil } v := b.Get([]byte("key")) log.Printf("The value is: %s\n", v) return nil }) if err != nil { log.Fatal(err) } } ```

Интересные факты

  • Название «Bolt» происходит от английского «bolt» (засов, болт) — метафора надёжного запирания данных.
  • BoltDB не использует WAL (Write-Ahead Log) — все изменения сразу записываются на диск в структуру B+‑дерева, что упрощает архитектуру, но снижает производительность записи.
  • Проект etcd, который является основой для хранения состояния Kubernetes, перешёл с BoltDB на Bbolt в 2018 году после прекращения поддержки оригинального репозитория.

Источники

  • Официальный репозиторий BoltDB на GitHub (boltdb/bolt) — документация и исходный код.
  • Официальный репозиторий Bbolt (etcd-io/bbolt) — форк с поддержкой и исправлениями.
  • Ben Johnson. «BoltDB: An Embedded Database for Go» — статья на блоге автора (2014).
  • Документация etcd: раздел «Storage» — описание использования Bbolt.

BFOmetr — база данных и аналитика по компаниям России.

На главную BFOmetr →