Repository navigation
Proposal: Kvrocks Count-Min Sketch #3404
Replies: 3 comments 7 replies
This comment was marked as off-topic.
This comment was marked as off-topic.
|
Hi @Tangruilin I think the current Per-Bucket performance conclusion is not accurate enough for RocksDB. The comparison uses Even in the proposal's own estimate, Per-Bucket writes are
So I don't think Per-Bucket should be chosen only because it appears to have the lowest write amplification. It reduces value rewrite size, but it creates Given that CMS is mostly write-heavy and read performance is less important, I think a better direction is:
PS: Here "page-based" means splitting the CMS counter matrix into fixed-size physical pages instead of storing one key per counter or one key for the whole matrix. Each page contains multiple adjacent counters and is stored as one RocksDB subkey. This is a middle ground between Single Key and Per-Bucket: it avoids rewriting the entire matrix on each update, while also avoiding
This keeps the first implementation reviewable and correct, while leaving a clean path to optimize the write-heavy workload later. It also avoids locking us into a Per-Bucket layout that may look efficient at the counter level but is likely expensive at the RocksDB record level. |
|
Hi @apache/kvrocks-committers. We have a proposal here for the new data structure, and I’d like to get everyone’s feedback. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Proposal: Kvrocks Count-Min Sketch
Background
Redis Stack Compatibility
Redis Stack has implemented the Count-Min Sketch probabilistic data structure, providing commands such as
CMS.INITBYPROB,CMS.INCRBY, andCMS.QUERY. To maintain compatibility with the Redis ecosystem, Kvrocks needs to implement these features.Related Issue:
Kvrocks Storage Architecture
Column Family Design
Kvrocks uses a single RocksDB instance, dividing data by access pattern through Column Family (CF):
Count-Min Sketch
Introduction
Count-Min Sketch (CMS) is a probabilistic data structure for estimating element frequencies in a data stream. It uses fixed memory with O(1) time complexity for insertion and query, and only overestimates (never underestimates). Commonly used for hot content tracking, traffic monitoring, recommendation systems, etc.
Algorithm diagram:
For detailed algorithm explanation, see: https://redis.io/docs/latest/develop/data-types/probabilistic/count-min-sketch/
The core challenge is to design a storage format that balances disk read/write performance
Command List
CMS.INITBYPROB key error_rate probabilityCMS.INITBYDIM key width depthCMS.INCRBY key item increment [...]CMS.QUERY key item [item...]CMS.MERGE destkey numkeys srckey...CMS.INFO keyCMS Metadata Design
Storage Format:
CMS metadata contains base metadata and CMS's metadata for count, such as width, depth, total_count, etc.
Field Description:
Count Matrix Storage Design
Count Matrix is a 2D count matrix stored in PrimarySubkey CF. Considering the balance of read, write, and space amplification, we have several storage options:
1. Single Key Storage:
This approach stores the entire CM as a Value. The disadvantage is high write amplification for each write. The advantage is higher iterator and read efficiency.
2. Per-Bucket Storage:
This approach stores each element in CMS separately. The advantage is low write amplification — each key is written independently. The disadvantage is relatively lower read and iterator
efficiency, as well as higher space amplification.
Here, a bucket means one counter in the CMS matrix. A single-item
INCRBYtouches one bucket in each layer, so it updatesdepthbuckets in total.Scheme Comparison:
Assuming parameters:
width = 2000, depth = 9, total matrix sizewidth × depth × 4 = 72 KB.InternalKey overhead estimate (excluding namespace):
1 (ns_len) + 2 (slot_id) + 4 (key_len) + 20 (key) + 8 (version) + sub_key_size ≈ 35 + sub_key_size bytes.Logical Counter-Value Write Amplification Calculation:
The calculation above only counts counter value bytes. At the RocksDB key-value payload level, per-bucket storage writes multiple small records for one item update. With
depth = 9, the estimated lower-bound payload is about9 × (InternalKey + 4B value), which is larger than 36 B and does not include WAL, memtable, SST index/filter, or compaction overhead.Conclusion:
For write-intensive scenarios, the per-bucket storage model offers lower logical counter-value write amplification and higher concurrency; for read-intensive scenarios, the single key approach provides lower read amplification, and since single key requires fewer Internal Keys, it also has lower space amplification.
However, considering that the primary use case for CMS is counting—a write-intensive workload—and that modern storage is abundant, a certain degree of space amplification is acceptable.
Therefore, the per-bucket approach is selected for the initial implementation because it minimizes logical counter rewrites and keeps the implementation straightforward. Its RocksDB-level small-key overhead should be evaluated with benchmarks and may be optimized in a later iteration.
I/O Operations Analysis
Assuming parameters:
width = 2000, depth = 9, k = 100CMS Command I/O Operations
CMS.INITBYPROBCMS.INITBYDIMCMS.INCRBY item [item...]CMS.QUERY item [item...]CMS.MERGE dest N src...CMS.INFOExample Calculation (width=2000, depth=9, single INCRBY):
Per-bucket has more I/O operations but lower logical counter-value write amplification; Single Key has fewer I/O operations but higher logical counter-value write amplification.
References
All reactions