logo
0
0
WeChat Login

GoKV2

A high-performance, persistent key-value storage library written in Go. Supports three storage engines (B+tree, LSM-tree, and KV-Separation) with WAL (Write-Ahead Logging) and snapshot-based persistence.

Features

  • Three Storage Engines: Choose between B+tree, LSM-tree, or KV-Separation based on your workload
  • Persistence: WAL + Snapshot for durability and fast recovery
  • Concurrent Access: Thread-safe with fine-grained locking
  • Range Queries: Efficient range scans and prefix searches
  • Batch Operations: Atomic batch writes
  • Configurable: Tunable parameters for different workloads
  • Compression: Optional gzip compression for snapshots

Installation

go get cnb.cool/asphodelus_dev/gokv2

Quick Start

package main import ( "fmt" "log" "cnb.cool/asphodelus_dev/gokv2" ) func main() { // Open database with default options (B+tree engine) db, err := gokv2.Open("./data") if err != nil { log.Fatal(err) } defer db.Close() // Put err = db.Put([]byte("hello"), []byte("world")) if err != nil { log.Fatal(err) } // Get value, err := db.Get([]byte("hello")) if err != nil { log.Fatal(err) } fmt.Printf("Value: %s\n", value) // Delete err = db.Delete([]byte("hello")) if err != nil { log.Fatal(err) } }

Storage Engine Selection

// Use B+tree engine (optimized for read-heavy workloads) db, err := gokv2.Open("./data", gokv2.WithStorageEngine(gokv2.BTreeEngine)) // Use LSM-tree engine (optimized for write-heavy workloads) db, err := gokv2.Open("./data", gokv2.WithStorageEngine(gokv2.LSMEngine)) // Use KV-Separation engine (default, optimized for large values) db, err := gokv2.Open("./data", gokv2.WithStorageEngine(gokv2.KVSepEngine))

When to Use Which Engine

FeatureB+treeLSM-treeKV-Separation
Read PerformanceExcellentGoodGood
Write PerformanceGoodExcellentExcellent
Space AmplificationLowMediumLow
Write AmplificationMediumLowVery Low
Range ScansExcellentGoodGood
Large ValuesGoodMediumExcellent
Best ForRead-heavy, OLTPWrite-heavy, LogsLarge values, Mixed workload

Engine Details

  • B+tree Engine: Traditional B+tree with in-place updates. Best for read-heavy workloads with balanced read/write patterns.

  • LSM-tree Engine: Log-Structured Merge-tree with compaction. Optimized for write-heavy workloads with sequential writes.

  • KV-Separation Engine (Default): Separates keys and values into different storage structures. Keys are stored in a sorted index (LSM-like), while values are stored in an append-only value log. Ideal for:

    • Large value sizes (>1KB)
    • Mixed read/write workloads
    • Scenarios requiring low write amplification
    • Applications with value log garbage collection needs

API Reference

Database Operations

// Open database func Open(path string, opts ...Option) (*DB, error) // Close database func (db *DB) Close() error // Get value by key func (db *DB) Get(key []byte) ([]byte, error) // Put key-value pair func (db *DB) Put(key, value []byte) error // Delete key func (db *DB) Delete(key []byte) error // Check if key exists func (db *DB) Has(key []byte) (bool, error) // Get database size (number of keys) func (db *DB) Size() int // Force sync to disk func (db *DB) Sync() error // Create snapshot manually func (db *DB) Snapshot() error

Iteration

// Create iterator over all keys func (db *DB) NewIterator() (*Iterator, error) // Range iterator [start, end) func (db *DB) Range(start, end []byte) (*Iterator, error) // Prefix scan func (db *DB) Scan(prefix []byte) (*Iterator, error) // ForEach iteration func (db *DB) ForEach(fn func(key, value []byte) bool) error

Iterator Methods

// Check if iterator is valid func (it *Iterator) Valid() bool // Move to next entry func (it *Iterator) Next() bool // Move to previous entry func (it *Iterator) Prev() bool // Get current key func (it *Iterator) Key() []byte // Get current value func (it *Iterator) Value() []byte // Seek to key >= target func (it *Iterator) Seek(target []byte) bool // Seek to first key func (it *Iterator) SeekToFirst() bool // Seek to last key func (it *Iterator) SeekToLast() bool // Close iterator func (it *Iterator) Close() error

Batch Operations

// Create batch batch := db.Batch() // Add operations batch.Put([]byte("key1"), []byte("value1")) batch.Put([]byte("key2"), []byte("value2")) batch.Delete([]byte("key3")) // Commit atomically err := batch.Commit()

Configuration Options

// Storage engine selection gokv2.WithStorageEngine(gokv2.BTreeEngine) // or gokv2.LSMEngine, gokv2.KVSepEngine // B+tree order (default: 128) gokv2.WithBTreeOrder(256) // WAL sync mode gokv2.WithSyncMode(gokv2.SyncAlways) // SyncNone, SyncBatch, SyncAlways // Snapshot settings gokv2.WithSnapshotInterval(time.Hour) gokv2.WithSnapshotWALThreshold(256 * 1024 * 1024) gokv2.WithSnapshotOpsThreshold(100000) // Compression gokv2.WithCompression(true) // Read-only mode gokv2.WithReadOnly(true) // LSM-specific options gokv2.WithMemTableSize(64 * 1024 * 1024) // 64MB gokv2.WithLevel0FileNum(4) gokv2.WithLevelSizeMultiplier(10) // KVSep-specific options gokv2.WithVLogSegmentSize(256 * 1024 * 1024) // 256MB value log segment size

Project Structure

gokv2/ ├── go.mod ├── gokv.go # Main entry, public API ├── options.go # Configuration options ├── errors.go # Error definitions ├── iterator.go # Iterator interface ├── batch.go # Batch operations ├── transaction.go # MVCC transactions │ ├── internal/ │ ├── btree/ # B+tree storage engine │ │ ├── node.go # Node definition │ │ ├── tree.go # B+tree operations │ │ └── iterator.go # Tree iterator │ │ │ ├── lsm/ # LSM-tree storage engine │ │ ├── memtable.go # In-memory table (skiplist) │ │ ├── sstable.go # Sorted String Table │ │ ├── level.go # Level management │ │ ├── compaction.go # Compaction strategy │ │ └── lsm.go # LSM-tree manager │ │ │ ├── kvsep/ # KV-Separation storage engine │ │ ├── kvsep.go # KVSep manager │ │ ├── index_file.go # Sorted index for keys │ │ ├── vlog.go # Value log (append-only) │ │ ├── vlog_gc.go # Value log garbage collection │ │ ├── memtable.go # In-memory index │ │ └── compaction.go # Index compaction │ │ │ ├── wal/ # Write-Ahead Log │ │ ├── wal.go # WAL manager │ │ ├── entry.go # Log entry format │ │ ├── segment.go # Segment management │ │ └── reader.go # Log reader │ │ │ ├── snapshot/ # Snapshot management │ │ ├── snapshot.go # Snapshot manager │ │ └── format.go # Snapshot format │ │ │ ├── mvcc/ # Multi-Version Concurrency Control │ │ ├── txn_manager.go # Transaction manager │ │ ├── version.go # Version store │ │ └── gc.go # Garbage collector │ │ │ └── engine/ # Storage engine abstraction │ ├── interface.go # Engine interface │ ├── engine.go # B+tree engine implementation │ ├── lsm_adapter.go # LSM engine adapter │ ├── kvsep_adapter.go# KVSep engine adapter │ ├── recovery.go # Data recovery │ └── compaction.go # Compaction triggers │ └── cmd/ └── test/ └── main.go # Integration test

Architecture

Data Flow

Write Path: Client -> DB.Put() -> WAL.Write() -> Engine.Put() -> [B+tree/LSM/KVSep] -> Memory/Disk Read Path: Client -> DB.Get() -> Engine.Get() -> [B+tree/LSM/KVSep] -> Memory/Disk Recovery Path: Startup -> Load Snapshot -> Replay WAL -> Ready

KV-Separation Architecture

Write Path: Key -> Index (LSM-like sorted structure) Value -> Value Log (append-only file) Read Path: Key -> Index lookup -> Value pointer -> Value Log read Garbage Collection: Scan Value Log -> Identify dead values -> Rewrite live values -> Reclaim space

WAL Format

| CRC32 (4B) | Type (1B) | KeyLen (4B) | ValueLen (4B) | Timestamp (8B) | SeqNum (8B) | Key | Value |

Snapshot Format

| Header (32B) | [Compressed Data] | Checksum (4B) | Header: | Magic (4B) | Version (4B) | SeqNum (8B) | EntryCount (8B) | Flags (4B) | Reserved (4B) |

Performance

Benchmark results on typical hardware (Intel i7-10750H, NVMe SSD):

OperationB+treeLSM-treeKV-Separation
Sequential Write~348K ops/s~111K ops/s~16K ops/s
Random Read~500K ops/s~200K ops/s~200K ops/s
Range Scan (100 keys)~2M keys/s~1M keys/s~1M keys/s
Mixed Workload~300K ops/s~400K ops/s~350K ops/s

Note: KV-Separation performance improves significantly with larger value sizes (>4KB).

Error Handling

var ( ErrKeyNotFound // Key does not exist ErrKeyEmpty // Key cannot be empty ErrKeyTooLarge // Key exceeds max size ErrValueTooLarge // Value exceeds max size ErrDatabaseClosed // Database is closed ErrCorruptedData // Data corruption detected ErrBatchClosed // Batch already committed/closed ErrIteratorClosed // Iterator is closed )

Thread Safety

All public methods are thread-safe. The library uses:

  • RWMutex for read/write separation
  • Fine-grained locking in B+tree nodes
  • Lock-free structures where possible in LSM-tree
  • MVCC (Multi-Version Concurrency Control) for transactions

Transactions

GoKV2 supports ACID transactions with snapshot isolation:

// Begin a read-write transaction txn, err := db.Begin() if err != nil { log.Fatal(err) } // Perform operations txn.Put([]byte("key1"), []byte("value1")) txn.Put([]byte("key2"), []byte("value2")) value, err := txn.Get([]byte("key1")) // Commit or rollback if err := txn.Commit(); err != nil { txn.Rollback() log.Fatal(err) }

License

MIT License

About

No description, topics, or website provided.
Language
Go100%