docs/design_docs/primarykey_index.md
This document outlines the design of Milvus' primary key indexing system, which enables fast lookups of string or integer primary keys across multiple segments. The index will be loaded in the Delegator and persisted in S3 storage.
BBhash:
BBhash is a minimal perfect hash library for static key collections, capable of mapping each key to a unique, compact integer index. For example:
For string primary keys, BBhash processes the raw byte sequence directly without type conversion and supports variable-length strings. Key features include:
Value Array:
This array stores segment metadata for each primary key. It can be accessed directly using the BBhash mapping result, providing O(1) query efficiency:
example code
// Example code for building and using the primary key index
// Building the index
void buildPrimaryKeyIndex(const std::vector<std::string>& keys, const std::vector<SegmentInfo>& segmentInfos) {
// Initialize BBhash with the keys
bbhash::PerfectHasher<std::string> hasher(keys);
// Initialize value array with appropriate size
std::vector<SegmentInfo> valueArray(keys.size());
// Populate value array with segment information
for (size_t i = 0; i < keys.size(); i++) {
size_t index = hasher.lookup(keys[i]);
valueArray[index] = segmentInfos[i];
}
// Persist the index to storage
hasher.save("bbhash.idx");
saveValueArray(valueArray, "value_array.bin");
}
// Reading from the index
SegmentInfo lookupPrimaryKey(const std::string& key) {
// Load BBhash from storage (or use cached instance)
bbhash::PerfectHasher<std::string> hasher;
hasher.load("bbhash.idx");
// Load value array (or use memory-mapped instance)
std::vector<SegmentInfo> valueArray = loadValueArray("value_array.bin");
// Lookup the key
size_t index = hasher.lookup(key);
if (index != bbhash::NOT_FOUND) {
return valueArray[index];
}
return SegmentInfo(); // Return empty segment info if not found
}
BBhash (Bin Bloom Hash) maps keys to unique indices through multi-level hash functions:
Each entry in the value array contains:
For L1 Segments, we don't need primary key indexing and can use Bloom Filters for approximate filtering with false positives. For L2 Segments, we build PK → Segment mappings for data under each bucket. Note that false positives still exist here due to: 1. Data that has been deleted, and 2. BBhash's small probability of false positives (approximately 1/2³² ≈ 2.3×10⁻¹⁰).
Query Latency:
Throughput:
BBhash Precision:
Bloom Filter Precision: