Garnet Roaring Bitmap 扩展指南:R.* 命令、压缩位图原理与持久化机制

发布时间:2026/9/15 19:47:14
Garnet Roaring Bitmap 扩展指南:R.* 命令、压缩位图原理与持久化机制
Garnet Roaring Bitmap 扩展指南R.* 命令、压缩位图原理与持久化机制【免费下载链接】garnetGarnet is a remote cache-store from Microsoft Research that offers strong performance (throughput and latency), scalability, storage, recovery, cluster sharding, key migration, and replication features. Garnet can work with existing Redis clients.项目地址: https://gitcode.com/GitHub_Trending/garnet4/garnetGarnet 以扩展模块custom-object extension的形式内置了一个Roaring Bitmap实现通过R.SETBIT、R.GETBIT、R.BITCOUNT、R.BITPOS四个 RESP 命令提供稀疏到稠密uint32集合的压缩存储与快速查询。本文以 roaring-bitmap.md 为骨架结合modules/RoaringBitmap/下的完整源码与测试讲解如何加载该扩展、四个命令的精确语义与边界行为、容器升降级的底层实现以及 RDB/AOF/集群迁移的持久化路径帮助你直接把它接入自己的使用场景。为什么需要 Roaring Bitmap一个覆盖完整uint32值域的朴素位图需要 512 MiB2³² bit。Roaring 的做法是把整个值域切成65 536 个分块chunk每块覆盖 65 536 个 bit即高 16 位相同的所有元素再按块内稠密度选择存储格式数组容器Array container当块内被置位的元素不超过 4 096 个时使用有序的ushort[]存储内存约为2 × count字节位图容器Bitmap container一旦块内元素超过 4 096 阈值升级为固定 8 KiB 的ulong[1024]位图恰好 65 536 bit。空的块不占用任何字节不在分块字典中登记因此稀疏集合的存储成本与元素数量成正比。实现会在基数变化时自动完成array → bitmap 提升与bitmap → array 降级。在源码层面这套逻辑位于 RoaringBitmap.cs内部维护两个并行数组highKeys有序、去重的高 16 位键与containerschunkCount记录当前分块数totalCardinality增量维护全局基数保证BITCOUNT是 O(1) 读取。块内的转换阈值定义在 ArrayContainer.csArrayThreshold 40964 096 个元素时数组恰好也占 8 KiB与位图容器持平但查询仍是 O(log n)超过之后位图容器在每元素成本上严格更优。容器接口契约见 IContainer.cs——所有变更操作都可能返回一个新的容器实例例如数组越界提升为位图调用方必须用返回值替换旧引用当Remove使容器为空时返回null由父级负责移除分块。加载 Roaring Bitmap 扩展该扩展位于 modules/RoaringBitmap/默认不注册需要通过MODULE LOADCS命令手动加载。模块入口 RoaringBitmapModule.cs 的OnLoad完成三件事context.Initialize(GarnetRoaringBitmap, 1)初始化类型名称 版本 1context.RegisterType(new RoaringBitmapFactory())注册自定义对象工厂依次注册四个命令R.SETBITCommandType.ReadModifyWrite、R.GETBIT/R.BITCOUNT/R.BITPOS均为CommandType.Read并声明各自的 arity。所有R.前缀是为了避免与 Garnet 内置的、作用于原始字节串的SETBIT/GETBIT/BITCOUNT/BITPOS命令冲突。模块程序集名与命名空间均为GarnetRoaringBitmap见 GarnetRoaringBitmap.csproj编译产物即为LOADCS时的目标程序集。对于嵌入式托管场景不需要LOADCS而是像集成测试 RespRoaringBitmapTests.cs 那样通过server.Register.NewType(factory)与server.Register.NewCommand(...)在服务启动前完成注册。命令总览命令Arity说明R.SETBIT key offset value4将offset无符号 32 位整数处的 bit 置为0或1返回该 bit 的旧值R.GETBIT key offset3返回offset处的 bit 值键不存在时返回0R.BITCOUNT key2返回置 1 的 bit 总数population count键不存在时返回0R.BITPOS key bit [from]3 或 4返回from默认0起第一个等于bit0/1的位置整个uint32值域内无匹配返回-1arity 数值与 RoaringBitmapModule.cs 中RespCommandsInfo的声明一致其中R.BITPOS注册的 arity 为-3可变参允许可选的第四个参数from。R.SETBIT key offset value R.SETBIT counters 1000 1 (integer) 0 R.SETBIT counters 1000 1 (integer) 1offset必须是非负且能放进uint32的整数最大4294967295value必须是0或1。参数校验集中在 RoaringBitmapCommands.csTryParseUInt32要求整段输入都被解析为[0, uint.MaxValue]区间的十进制数TryParseBit只接受单字节0/1。一个值得注意的实现细节RSetBit.NeedInitialUpdateRoaringBitmapCommands.cs会在框架分配空对象之前先预校验参数避免一条格式非法的R.SETBIT留下 tombstone 式的空键。校验失败分别返回ERR bit offset is not an unsigned 32-bit integerERR bit value must be 0 or 1正常路径下Updater调用rb.SetBit(bitOffset, bit)返回旧值0 或 1并同步更新HeapMemorySize见 RoaringBitmapObject.cs。R.GETBIT key offset R.GETBIT counters 1000 (integer) 1 R.GETBIT counters 9999 (integer) 0任何从未置位过的 offset或整个键不存在时都返回0。并且读取不会创建键RGetBit对已有键走Reader对缺失键走NotFound分支RoaringBitmapCommands.csNotFound中即使参数非法也只会写错误、不会落键。这一点被测试 GetBitOnMissingKey_Returns0 显式验证执行R.GETBIT后再检查KeyExists为假。R.BITCOUNT key R.BITCOUNT counters (integer) 1返回置 1 的 bit 总数缺失键返回0且不创建键RoaringBitmapCommands.cs。底层直接读取RoaringBitmap.CardinalityRoaringBitmap.cs——这是一个每次变更时增量维护的缓存值因此R.BITCOUNT无需遍历容器。R.BITPOS key bit [from] R.BITPOS counters 1 (integer) 1000 R.BITPOS counters 0 (integer) 0 R.BITPOS counters 1 1001 (integer) -1bit 1返回from起第一个置位不存在则返回-1。bit 0扫描会跨越整个uint32值域只有在from到2³² - 1全部被置位时才返回-1。缺失键时RoaringBitmapCommands.csbit 1直接返回-1bit 0返回from默认0——因为空位图的第一个空位就是from本身。测试 BitPosOnMissingKey_Bit0ReturnsFromOrZero 验证了R.BITPOS rb 0→0、R.BITPOS rb 0 100→100。底层实现值得展开RoaringBitmap.cs先用二分查找定位第一个highKey fromHi的分块找1时逐块调用NextSetBit找0时利用未分配的高键代表整段 65 536 位全空这一性质一旦发现相邻分块之间存在高键空洞空洞起始位置立即就是答案块内则用NextUnsetBit找空位块满则推进到下一个高键。容器层的NextSetBit/NextUnsetBit分别在 ArrayContainer.cs二分 线性推进与 BitmapContainer.csTrailingZeroCount 按字求反中实现。持久化与复制Roaring Bitmap 对象走 Garnet 标准的自定义对象路径通过Serialize/Deserialize覆写完成序列化因此RDB 检查点、AOF 与集群迁移key migration无需任何额外接线即可工作。序列化包装在 RoaringBitmapObject.cs真正格式定义于 RoaringBitmap.cs带版本号且要求严格有序byte version 0x01 int32 chunkCount (小端) foreach chunk按 highKey 升序: ushort highKey byte containerKind (1 array, 2 bitmap) int32 cardinality bytes body (按容器类型而定)反序列化端做了完整的一致性校验版本不匹配、分块数越界、高键非严格递增、未知容器类型、存储基数与实际基数不一致都会抛出InvalidDataException。ContainerKind被注释为稳定的磁盘值禁止重编号IContainer.cs保证升级兼容。对象层同时提供CloneObject深拷贝事务与快照语义需要RoaringBitmapObject.cs以及内存尺寸核算每次变更用bitmap.ByteSize的前后差值更新HeapMemorySize使MEMORY USAGE结果保持准确。注释也如实指出Garnet 全局对象存储的 size tracker 目前不会从自定义对象的Updater传播每次操作的增量libs/server/Storage/Functions/ObjectStore/RMWMethods.cs这是核心层的既有限制与本扩展无关。已知限制v1第一版聚焦于干净、可充分测试的基础实现以下项被有意推迟原文档明确列出均为后续 PR 的候选Run containerRoaring 的第三种经典容器。加入后可把连续区间的压缩率再提升约 30%但当前 array/bitmap 组合已覆盖真实场景中绝大部分空间收益。R.BITOP AND/OR/XOR/NOT多键集合运算。底层数据结构天然支持这些运算目前只缺命令表面。空键清理当R.SETBIT key offset 0清掉最后一个 bit 时键会以空位图对象的形式残留而不是被删除。这是自定义对象框架 tombstone 路径的属性output.HasRemoveKey只在内置对象路径上生效已单独跟踪。源码中 RoaringBitmapObject.cs 已暴露IsEmpty作为删除提示可被后续版本使用。测试验证扩展自带两层测试可作为行为规范与回归基线RespRoaringBitmapTests.cs389 行通过 StackExchange.Redis 走真实 RESP 协议做端到端集成测试覆盖缺失键语义GETBIT/BITCOUNT返回 0 且不建键、BITPOS的-1/from行为、置位/清位/基数统计、位位置查询等。RoaringBitmapDataTests.cs纯数据结构层测试可直接探测分块布局、容器类型与序列化往返。相关阅读原始功能请求GitHub issue #1270详见上游仓库讨论。SETBIT/GETBIT/BITCOUNT/BITPOSRedis 兼容的原始字节串位操作作用于字节串而非 Roaring 位图二者功能互补。扩展机制的更多样例modules/下的GarnetJSON、RoaringBitmap等模块以及main/GarnetServer/Extensions/中的自定义对象与自定义过程示例。【免费下载链接】garnetGarnet is a remote cache-store from Microsoft Research that offers strong performance (throughput and latency), scalability, storage, recovery, cluster sharding, key migration, and replication features. Garnet can work with existing Redis clients.项目地址: https://gitcode.com/GitHub_Trending/garnet4/garnet创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考