руководство по использованию badger (высокопроизводительный магазин LSM K/V)

Go

badfer — это быстрая встроенная база данных K/V, реализованная на чистом Go и оптимизированная для LSM-дерева.

Установить

$ go get github.com/dgraph-io/badger/...

база данных

открыть базу данных

opts := badger.DefaultOptions
opts.Dir = "/tmp/badger"
opts.ValueDir = "/tmp/badger"
db, err := badger.Open(opts)
if err != nil {
	log.Fatal(err)
}
defer db.Close()

место хранения

магазин кв

Использование метода Txn.Set()

err := db.Update(func(txn *badger.Txn) error {
  err := txn.Set([]byte("answer"), []byte("42"))
  return err
})

Пакетные настройки

wb := db.NewWriteBatch()
defer wb.Cancel()

for i := 0; i < N; i++ {
  err := wb.Set(key(i), value(i), 0) // Will create txns as needed.
  handle(err)
}
handle(wb.Flush()) // Wait for all txns to finish.

WriteBatch не разрешает чтение. Для чтения-изменения-записи следует использовать транзакционный API.

Установить время жизни TTL

Badger позволяет установить дополнительное значение времени жизни (TTL) для ключа. Как только TTL закончится, KEY больше нельзя будет восстановить, и он будет удален сборщиком мусора. TTL можно установить равным единице с помощью Txn.SetWithTTL().time.Durationзначение

установить метаданные

Txn.SetWithMeta()Установить пользовательские метаданные

использоватьTxn.SetEntry()Ключ, значение, пользовательские метаданные и TTL могут быть установлены одновременно.

перебирать ключи

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

err := db.View(func(txn *badger.Txn) error {
  opts := badger.DefaultIteratorOptions
  opts.PrefetchSize = 10
  it := txn.NewIterator(opts)
  defer it.Close()
  for it.Rewind(); it.Valid(); it.Next() {
    item := it.Item()
    k := item.Key()
    err := item.Value(func(v []byte) error {
      fmt.Printf("key=%s, value=%s\n", k, v)
      return nil
    })
    if err != nil {
      return err
    }
  }
  return nil
})

сканирование префикса

Для перебора ключевых префиксов вы можете комбинировать Seek() и ValidForPrefix():

db.View(func(txn *badger.Txn) error {
  it := txn.NewIterator(badger.DefaultIteratorOptions)
  defer it.Close()
  prefix := []byte("1234")
  for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() {
    item := it.Item()
    k := item.Key()
    err := item.Value(func(v []byte) error {
      fmt.Printf("key=%s, value=%s\n", k, v)
      return nil
    })
    if err != nil {
      return err
    }
  }
  return nil
})

Обход ключей

Badger поддерживает уникальный режим итерации, называемый итерацией только по ключу. Это на несколько порядков быстрее, чем обычная итерация, потому что она включает доступ только к lsm-дереву, которое обычно полностью находится в оперативной памяти. Чтобы включить итерацию только по ключу, вам нужно установить IteratorOptions. Поле PrefetchValues ​​ложно. Это также можно использовать для разреженного чтения выбранных ключей во время итерации, вызывая item.Value() только при необходимости.

err := db.View(func(txn *badger.Txn) error {
  opts := badger.DefaultIteratorOptions
  opts.PrefetchValues = false
  it := txn.NewIterator(opts)
  defer it.Close()
  for it.Rewind(); it.Valid(); it.Next() {
    item := it.Item()
    k := item.Key()
    fmt.Printf("key=%s\n", k)
  }
  return nil
})

поток данных

Badger предоставляет платформу потоковой передачи, которая может одновременно проходить всю базу данных или ее части, преобразовывать данные в настраиваемые ключи-значения и непрерывно передавать данные для отправки по сети, записи на диск и даже записи в Badger. Это более быстрый способ обхода Badger, чем использование одного итератора. Stream поддерживает Badger как в режиме администратора, так и в обычном режиме.

stream := db.NewStream()
// db.NewStreamAt(readTs) for managed mode.

// -- Optional settings
stream.NumGo = 16                     // Set number of goroutines to use for iteration.
stream.Prefix = []byte("some-prefix") // Leave nil for iteration over the whole DB.
stream.LogPrefix = "Badger.Streaming" // For identifying stream logs. Outputs to Logger.

// ChooseKey is called concurrently for every key. If left nil, assumes true by default.
stream.ChooseKey = func(item *badger.Item) bool {
  return bytes.HasSuffix(item.Key(), []byte("er"))
}

// KeyToList is called concurrently for chosen keys. This can be used to convert
// Badger data into custom key-values. If nil, uses stream.ToList, a default
// implementation, which picks all valid key-values.
stream.KeyToList = nil

// -- End of optional settings.

// Send is called serially, while Stream.Orchestrate is running.
stream.Send = func(list *pb.KVList) error {
  return proto.MarshalText(w, list) // Write to w.
}

// Run the stream
if err := stream.Orchestrate(context.Background()); err != nil {
  return err
}
// Done.

удалить ключ

использоватьTxn.Delete()метод удаления ключа

Получить ключевое значение

Получить значение через txn.Get

err := db.View(func(txn *badger.Txn) error {
  item, err := txn.Get([]byte("answer"))
  handle(err)

  var valNot, valCopy []byte
  err := item.Value(func(val []byte) error {
    // This func with val would only be called if item.Value encounters no error.

    // Accessing val here is valid.
    fmt.Printf("The answer is: %s\n", val)

    // Copying or parsing val is valid.
    valCopy = append([]byte{}, val...)

    // Assigning val slice to another variable is NOT OK.
    valNot = val // Do not do this.
    return nil
  })
  handle(err)

  // DO NOT access val here. It is the most common cause of bugs.
  fmt.Printf("NEVER do this. %s\n", valNot)

  // You must copy it to use it outside item.Value(...).
  fmt.Printf("The answer is: %s\n", valCopy)

  // Alternatively, you could also use item.ValueCopy().
  valCopy, err = item.ValueCopy(nil)
  handle(err)
  fmt.Printf("The answer is: %s\n", valCopy)

  return nil
})

если не существуетTxn.Get()вернетErrKeyNotFoundошибка

Обратите внимание, что значение, возвращаемое Get(), действительно только тогда, когда транзакция открыта. Если значение необходимо использовать вне транзакции, оно должно быть скопировано в другой байтовый срез с помощью функции copy().

дела

транзакция только для чтения

Транзакции только для чтения используют метод DB.View()

err := db.View(func(txn *badger.Txn) error {
  // Your code here…
  return nil
})

Блокировка транзакции чтения-записи

Транзакции чтения и записи могут использовать метод DB.Update().

err := db.Update(func(txn *badger.Txn) error {
  // Your code here…
  return nil
})

Управляйте транзакциями вручную

Использовать напрямуюDB.NewTransaction()функция для ручного создания и фиксации транзакций. Он принимает логический параметр, чтобы указать, требуется ли транзакция чтения-записи. Для транзакций чтения и записи вам нужно вызватьTxn.Commit()чтобы убедиться, что транзакция совершена. Для транзакций только для чтения вызовитеtxn.reject()Вот и все.commit()Также называется внутреннимtxn .reject()для очистки транзакции, поэтому простого вызова Txn.Commit() достаточно для выполнения транзакции чтения-записи.

Однако, если ваш код по какой-то причине (ошибка) не вызываетTxn.Commit(). Его нужно вызвать в отсрочкеtxn . reject()

// Start a writable transaction.
txn := db.NewTransaction(true)
defer txn.Discard()

// Use the transaction...
err := txn.Set([]byte("answer"), []byte("42"))
if err != nil {
    return err
}

// Commit the transaction and check for error.
if err := txn.Commit(); err != nil {
    return err
}

Эта статья также опубликована в общедоступной учетной записи WeChat [Trail News]. Пожалуйста, отсканируйте код, чтобы следовать!