Sorted Array (Memtable)

Source: db/sorted_array.go

The memtable is an in-memory sorted key-value store backed by three parallel slices. It is the mutable hot tier of the LSM tree - all committed writes land here before being compacted to disk.


Data structure

type SortedArray struct {
    keys    [][]byte
    vals    [][]byte
    deleted []bool
}

The three slices are kept in strict lexicographic key order. A deleted=true entry is a tombstone - it records that a key was deleted without removing it from the array, so that merging with older SSTables correctly shadows the older value.


Interface

SortedArray implements the SortedKV interface:

type SortedKV interface {
    EstimatedSize() int
    Iter() (SortedKVIter, error)
    Seek(key []byte) (SortedKVIter, error)
}

And additionally implements sortedKVExactGetter for O(log n) point lookups without creating a full iterator.

Key operations

MethodComplexityNotes
Set(key, val)O(log n) search + O(n) insertslices.BinarySearchFunc + slices.Insert
Del(key)O(log n) search + O(n) insertMarks existing entry as deleted, or inserts tombstone
getExact(key)O(log n)Binary search, returns (val, found, deleted)
Seek(key)O(log n)Returns iterator positioned at or after key
Iter()O(1)Iterator starting at position 0

Iterator

SortedArrayIter holds a snapshot of the slice headers at creation time. It supports Next() and Prev() for bidirectional iteration.


Fast-path append during commit

When committing a transaction and there are no concurrent transactions, appendSortedArray is attempted first:

// If all keys in src sort after all keys in dst, just append.
if dst.Size() != 0 && bytes.Compare(dst.keys[last], src.keys[0]) >= 0 {
    return false  // fall back to merge
}
dst.keys = append(dst.keys, src.keys...)
// ...

This is a common case for sequential key populations and avoids the O(n) merge path.


Merge into memtable

When the fast-path append is not safe (e.g., overlapping key ranges or concurrent transactions), the commit merges the transaction updates with the current memtable using MergedSortedKV:

merged := SortedArray{}
iter, _ := MergedSortedKV{&tx.updates, &kv.mem}.Iter()
for ; iter.Valid(); iter.Next() {
    merged.Push(iter.Key(), iter.Val(), iter.Deleted())
}
kv.mem = merged

The merge iterator gives priority to the first (newer) level, so transaction updates shadow older memtable entries.