【VictoriaMetrics源码阅读】vm中仿照RoaringBitmap的实现:uint64set
作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢!
正文
VictoriaMetrics中使用uint64类型来表示一个MetricID,且MetricID是递增的。
因此有这些一些需求:
- 需要缓存某一类metricID的集合,例如某个MetricQL的查询结果对应很多个MetricID;
- 需要对多个metricID的集合取交集和并集等操作。
并且:
- 占用的内存要尽可能的小
- 要避免大量的小对象,否则会对GC造成很大压力
- 插入、检查是否存在某个ID、并集计算、交集计算等要尽可能的快
VictoriaMetrics-1.72.0-cluster/lib/uint64set/uint64set.go 这里的代码很好的满足了上述需求。
下面我来分析它的实现原理。

1.结构
- 整个uint64按位分成多个部分,每个部分分别采用各自的插入和查找策略。相当于分级存储。
- 高32位先建一个桶
- 桶的个数动态增加
- metricID是递增的,因此在这样的业务场景下,高32位通常是0
- 高16位再建一个存储桶,这里的策略与上面相似。
- 桶的个数动态的增加
- 通常桶内的值是顺序增加的,插入过程只能顺序查找
- 在做集合计算的阶段,可以先对桶排序。
- 低16位的处理稍稍复杂一些:
- 当元素个数在56个以内时,直接在一个uint16的数组里顺序追加和顺序搜索
- 当元素超过56个时,建立一个1024个元素的数组,数组的元素是uint64类型
- uint64类型一共64个位,用每个位来代表0-63的某个值。相比用64个uint8来存储,空间节省了8倍。
2.插入过程
- 先确定高32位的分桶(如果没有就新增这个分桶)
- 在32位的分桶上,再确定高16位的分桶(如果没有就新增这个分桶)
- 在16位的分桶上,检查是否在56个元素以内
- 56个元素以内,直接在[]uint8数组上追加
- 超过56个元素,创建1024个元素的uint64数组,并把56个元素添加进去
- 16位的分桶上,通过前10位值作为下标,确定uint64数组的位置
- 把低6位的值,转换为一个uint64上的mask(值为N就把第N位置1),然后与上一步确定的uint64取bit or运算
3.查找过程
- 先确定高32位的分桶
- 再确定高16位的分桶
- 如果不存在1024个元素的分桶,就在56个元素的数组里面顺序查找
- 如果存在1024个元素的分桶,根据高10位获得下标
- 根据低6位,看uint64中对应的位是否是1
4.交集运算过程
- 先对a,b两个集合的32位分桶进行排序
- 依次偏移a,b两个集合32位分桶的下标,直到分桶值对齐
- 通过分桶的匹配,可以快速淘汰大量的值
- 32位分桶的值相等后,继续比较高16位的分桶。比较方法与上面相同。
- 低16位的值,比较1024个元素的数组。两个uint64做bit and运算,即可完成取交集。
5.总结
- uint64set类,首先考虑了业务场景。在绝大多数高32位都相同的大量数据的情况下,能够取得很好的效果。
- 相反:如果是大量随机的uint64值,这里的方法不见得会更好
- 相比map而言,插入和查找做不到O(1)的性能,但是分段查找也不会太差
- 相比map而言,内存上的好处非常多:
- 数据的扩容方便且快速,map可能需要多次扩容+rehash才行
- 更加节约内存
- 对GC的压力很小
- 对并集、交集等集合操作,分桶能够快速避开不合适的数据的比较,性能极高。
如果想看具体的源码的注释,请移步我上传的VM注释版的源码。
希望对你有用,have fun
【VictoriaMetrics源码阅读】vm中仿照RoaringBitmap的实现:uint64set的更多相关文章
- JDK源码阅读-DirectByteBuffer
本文转载自JDK源码阅读-DirectByteBuffer 导语 在文章JDK源码阅读-ByteBuffer中,我们学习了ByteBuffer的设计.但是他是一个抽象类,真正的实现分为两类:HeapB ...
- boost.asio源码阅读(2) - task_io_service
1.0 task_io_service 在boost.asio源码阅读(1)中,代码已经查看到task_io_service中. 具体的操作调用void task_io_service::init_t ...
- 源码阅读笔记 - 1 MSVC2015中的std::sort
大约寒假开始的时候我就已经把std::sort的源码阅读完毕并理解其中的做法了,到了寒假结尾,姑且把它写出来 这是我的第一篇源码阅读笔记,以后会发更多的,包括算法和库实现,源码会按照我自己的代码风格格 ...
- SpringMVC源码阅读:Controller中参数解析
1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...
- caffe-windows中classification.cpp的源码阅读
caffe-windows中classification.cpp的源码阅读 命令格式: usage: classification string(模型描述文件net.prototxt) string( ...
- caffe中batch norm源码阅读
1. batch norm 输入batch norm层的数据为[N, C, H, W], 该层计算得到均值为C个,方差为C个,输出数据为[N, C, H, W]. <1> 形象点说,均值的 ...
- go 中 select 源码阅读
深入了解下 go 中的 select 前言 1.栗子一 2.栗子二 3.栗子三 看下源码实现 1.不存在 case 2.select 中仅存在一个 case 3.select 中存在两个 case,其 ...
- 转-OpenJDK源码阅读导航跟编译
OpenJDK源码阅读导航 OpenJDK源码阅读导航 博客分类: Virtual Machine HotSpot VM Java OpenJDK openjdk 这是链接帖.主体内容都在各链接中. ...
- openjdk源码阅读导航
转自:http://rednaxelafx.iteye.com/blog/1549577 这是链接帖.主体内容都在各链接中. 怕放草稿箱里过会儿又坑掉了,总之先发出来再说…回头再慢慢补充内容. 先把I ...
- avalon源码阅读(1)
来源 写angularJS源码阅读系列的时候,写的太垃圾了. 一个月后看,真心不忍直视,以后有机会的话得重写. 这次写avalonJS,希望能在代码架构层面多些一点,少上源码.多写思路. avalon ...
随机推荐
- 带你认识三种kafka消息发送模式
摘要:在kafka-0.8.2之后,producer不再区分同步(sync)和异步方式(async),所有的请求以异步方式发送,这样提升了客户端效率. 本文分享自华为云社区<kafka消息发送模 ...
- Datahub新版本0.9.1更新,列级别数据血缘功能发布!
大家好,我是独孤风. 近期Datahub进行了一次大的版本更新,从0.9版本以后Datahub也正式发布了列级别数据血缘的功能. 0.9.1版本又增加了,列的影响分析这个功能. 这样Datahub对于 ...
- MySQL 项目中 SQL 脚本更新、升级方式,防止多次重复执行
一套代码,多家部署时,在SQL脚本升级时,通过一个SQL文件给运维,避免出现SQL执行序顺出错及漏执行SQL SQL Server 项目中 SQL 脚本更新方式 Oracle 项目中 SQL 脚本更新 ...
- SpringBoot Jar 包太大 瘦身 【初试】
SpringBoot Jar 包太大 瘦身,建议使用时,参考: SpringBoot Jar 包太大 瘦身 [终极版] 29M, 排除少量JAR包方式 打包,排除指定jar 包 <build&g ...
- 基于rest_framework的ModelViewSet类编写登录视图和认证视图
背景:看了博主一抹浅笑的rest_framework认证模板,发现登录视图函数是基于APIView类封装. 优化:使用ModelViewSet类通过重写create方法编写登录函数. 环境:既然接触到 ...
- Java kafka 设置从指定时间戳开始消费
包含的功能: 1.Java kafka 设置从指定时间戳开始消费 2.JdbcTemplate操作MySql 3.Java多线程消费kafka 4.Java获取kafka所有topic pom.xml ...
- JSP使用MySQL数据库报错java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
错误 在JavaWeb中的JSP中使用MySQL数据库报错找不到 com.mysql.jdbc.Driver . 错误日志如下: java.lang.ClassNotFoundException: c ...
- 【每日一题】21.边的染色 (DFS连通图 + 思维)
补题链接:Here 思维不够,看到这种陌生的题目无从下手. 这题应该做过一次的人会觉得它其实并不难. 主要思想:把边权->点权. 这样做的好处是,无论你怎么分配点权,在环内的异或值一定为 \(0 ...
- L2-020 功夫传人 (25分)
分析: ⽤⼆维数 组v存储师⻔谱系关系,v[i]表示编号为i的师傅所拥有的徒弟,如果徒弟个数等于0, 也就是说这是个得道者,那么v[i][0]保存放⼤的倍数,⽽且⽤visit[i] = true标记当 ...
- [完整]流程解决Vue3项目搭建步骤
Vue3项目完整搭建步骤 一. 使用vite创建vue3项目 npm init vue@latest 或者npm create vite@latest进行初始化项目并创建项目名称code,进入code ...
