数据库OLAP大数据后端【免费下载链接】druidApache Druid: a high performance real-time analytics database.项目地址https://gitcode.com/gh_mirrors/druid6/druid点击查看免费下载导读本文围绕 Apache Druid 的druid-distinctcount扩展系统讲解如何加载该扩展、在 Timeseries / TopN / GroupBy 三类查询中使用distinctCount聚合器计算去重计数典型场景如 UV 统计并深入剖析其基于 Bitmap 的实现原理、两个必须满足的前置分区条件以及使用限制。读完本文你将能够正确配置并安全使用该聚合器避免因分区与粒度设置不当导致的计数错误。该扩展位于仓库的 extensions-contrib/distinctcount 模块对应官方文档为 docs/development/extensions-contrib/distinctcount.md。一、加载 druid-distinctcount 扩展druid-distinctcount是 Apache Druid 的 contrib 扩展与其它扩展一样需要通过扩展加载列表启用。官方文档指出使用前需在扩展加载列表中包含druid-distinctcount具体加载方式参见 加载扩展说明。在实际部署中通常在common.runtime.properties中通过druid.extensions.loadList属性声明druid.extensions.loadList[druid-distinctcount]也可以与其它扩展一起列出例如druid.extensions.loadList[postgresql-metadata-storage, druid-hdfs-storage, druid-distinctcount]从仓库的 extensions-contrib/distinctcount/pom.xml 可以看到该模块的artifactId为druid-distinctcount依赖druid-processing、guava、jackson、fastutil等组件均为 provided 或 test 作用域并通过 DistinctCountDruidModule.java 注册了distinctCount这一聚合器 JSON 类型名public static final String DISTINCT_COUNT distinctCount; ... new SimpleModule(DistinctCountModule).registerSubtypes( new NamedType(DistinctCountAggregatorFactory.class, DISTINCT_COUNT) )这意味着在查询 JSON 的aggregations数组中只要将type设为distinctCountJackson 反序列化即可定位到 DistinctCountAggregatorFactory.java。二、使用前的两个关键前置条件务必遵守官方文档明确强调使用distinctCount聚合器前必须完成以下两步否则结果可能错误按单一维度做 Hash 分区使用基于单维度的 hash 分区规格按该维度例如visitor_id对数据进行分区确保该维度上具有同一值的所有行都落入同一个 segment。因为该聚合器只在单个 segment 内做去重若相同键分散到多个 segment会导致重复计数over count。保证 queryGranularity 能被 segmentGranularity 整除即查询粒度必须能整除分段粒度例如查询粒度为day而分段粒度为month时day能整除month反之若查询粒度比分段粒度更粗且无法整除结果会出错。官方原文的表述是make sure queryGranularity is divided exactly by segmentGranularity or else the result will be wrong。在 Druid 的摄取配置中Hash 分区可通过partitionsSpec与partitionDimensions实现例如使用type: hashed的分区规格并将partitionDimensions设为[visitor_id]。Hashed 分区的基本思路是先选定 segment 数量再按分区维度哈希把行均匀分布到各 segment相关背景可参考 Hadoop 摄取文档中的 Hashed 分区说明 与 本地批量任务文档。三、三种查询类型中的 distinctCount 用法以下三个示例完整继承自官方文档分别演示 Timeseries、TopN、GroupBy 查询中的distinctCount聚合器配置。三者均对visitor_id字段做去重计数输出命名为uv。1. Timeseries 查询{ queryType: timeseries, dataSource: sample_datasource, granularity: day, aggregations: [ { type: distinctCount, name: uv, fieldName: visitor_id } ], intervals: [ 2016-03-01T00:00:00.000/2013-03-20T00:00:00.000 ] }2. TopN 查询{ queryType: topN, dataSource: sample_datasource, dimension: sample_dim, threshold: 5, metric: uv, granularity: all, aggregations: [ { type: distinctCount, name: uv, fieldName: visitor_id } ], intervals: [ 2016-03-06T00:00:00/2016-03-06T23:59:59 ] }3. GroupBy 查询{ queryType: groupBy, dataSource: sample_datasource, dimensions: [sample_dim], granularity: all, aggregations: [ { type: distinctCount, name: uv, fieldName: visitor_id } ], intervals: [ 2016-03-06T00:00:00/2016-03-06T23:59:59 ] }三个示例的公共参数含义如下type聚合器类型固定为distinctCount对应源码中注册的 JSON 类型名name输出结果中的字段名如uvfieldName要去重计数的维度字段如visitor_id该字段必须是维度字段因为聚合器内部通过DimensionSelector读取它见下文源码分析。从 DistinctCountAggregatorFactory.java 的构造器可以看出它接受三个 JSON 属性name、fieldName和可选的bitmapFactoryname与fieldName均通过Preconditions.checkNotNull强制非空JsonCreator public DistinctCountAggregatorFactory( JsonProperty(name) String name, JsonProperty(fieldName) String fieldName, JsonProperty(bitmapFactory) BitMapFactory bitMapFactory ) { Preconditions.checkNotNull(name); Preconditions.checkNotNull(fieldName); ... this.bitMapFactory bitMapFactory null ? DEFAULT_BITMAP_FACTORY : bitMapFactory; }其中bitmapFactory未指定时默认使用RoaringBitMapFactory。如果需要对底层位图实现做精细调优可以在聚合器配置中显式指定例如{ type: distinctCount, name: uv, fieldName: visitor_id, bitmapFactory: { type: roaring } }bitmapFactory支持的类型由 BitMapFactory.java 中的 Jackson 子类型注册定义共有三种type 值实现类底层位图roaring默认RoaringBitMapFactoryRoaringBitmapFactory见 RoaringBitMapFactory.javajavaJavaBitMapFactoryBitSetBitmapFactory见 JavaBitMapFactory.javaconciseConciseBitMapFactoryConciseBitmapFactory见 ConciseBitMapFactory.java四、源码实现原理Bitmap 驱动的单段去重理解distinctCount的实现才能明白为何它有上述前置条件与限制。整个扩展只有 10 个 Java 源文件逻辑非常聚焦。1. 聚合核心DistinctCountAggregatorDistinctCountAggregator.java 是核心实现它在factorize时通过DimensionSelector拿到维度值每次aggregate()把当前行对应的维度值在 segment 字典中的整数索引写入MutableBitmap位图public void aggregate() { IndexedInts row selector.getRow(); for (int i 0, rowSize row.size(); i rowSize; i) { int index row.get(i); mutableBitmap.add(index); } }去重结果就是位图中置位比特的数量public Object get() { return mutableBitmap.size(); }注意这里的关键点位图去重的是字典索引整数而不是原始字符串本身。由于同一 segment 内同一个维度值必然映射到同一个字典索引位图的“集合去重”语义天然成立——这也正解释了为什么必须用单一维度 hash 分区把相同键的所有行收敛到同一个 segment一旦相同键落入不同 segment每个 segment 各自维护一个独立位图最终合并阶段无法再对原始值去重。2. 合并逻辑为何是求和从 DistinctCountAggregatorFactory.java 可以看到该聚合器的combine()与getCombiningFactory()均按数值求和处理public Object combine(Object lhs, Object rhs) { ... return ((Number) lhs).longValue() ((Number) rhs).longValue(); } Override public AggregatorFactory getCombiningFactory() { return new LongSumAggregatorFactory(name, name); }也就是说单个 segment 内是精确的位图去重而跨 segment 的结果合并是“各 segment 去重计数之和”无法消除跨 segment 重复。这也是官方文档提醒“否则可能重复计数”的根源。getIntermediateType()与getResultType()均为LONGgetMaxIntermediateSize()为 8 字节位图只在内存计算期间存活最终以 long 形式输出。3. Buffer 聚合与空值兜底在需要缓冲式聚合如 groupBy 的批处理时使用 DistinctCountBufferAggregator.java它通过Int2ObjectMapMutableBitmap按 buffer 位置维护一组WrappedRoaringBitmapaggregate()后将位图大小写入ByteBuffer中的 long 位置init()时置 0同样只返回 long 值。当字段不是维度或无法构造DimensionSelector时工厂会回退到 NoopDistinctCountAggregator.java及对应的NoopDistinctCountBufferAggregator直接返回 0保证查询不会因缺失维度而报错。4. 缓存键与模块注册该聚合器的查询结果缓存键由 getCacheKey() 生成包含类型标识、fieldName与bitmapFactory的字符串表示其中类型字节0x10定义于 processing 模块的 AggregatorUtil.java 的DISTINCT_COUNT_CACHE_KEY与其它聚合器共享同一套缓存键协议。五、使用限制与注意事项官方文档明确列出了distinctCount在使用中的两类限制与 groupBy 一起使用每个 segment 内 groupBy 键的数量不应超过maxIntermediateRows这是 groupBy 查询的中间结果行数上限可通过查询 context 调整。一旦超过结果将不正确。这是因为缓冲聚合需要为每个 groupBy 键维护独立的位图集合超出上限后会导致部分键的位图被截断或丢弃。与 topN 一起使用numValuesPerPasstopN 单遍扫描处理的候选值数量不应设置过大。该值过大时distinctCount会为大量候选维度值各维护一份位图占用大量内存可能导致 JVM 内存溢出OOM。从DistinctCountBufferAggregator的Int2ObjectMapMutableBitmap结构可以直观看到内存占用与“候选键数量 × 位图大小”成正比。此外结合前文源码结论还需重申两点使用纪律必须按去重维度做单一维度 Hash 分区否则合并阶段的求和会重复计数必须保证 queryGranularity 能被 segmentGranularity 整除否则跨粒度聚合的数值会失真。六、测试验证与延伸阅读仓库为扩展提供了三类查询的完整测试可直接作为行为参考DistinctCountTimeseriesQueryTest.java构造三条包含不同visitor_id的记录断言 Timeseries 查询得到UV 3DistinctCountGroupByQueryTest.java验证 groupBy 场景下的去重计数DistinctCountTopNQueryTest.java验证 topN 场景下的去重计数。如果你希望了解 Druid 提供的其它去重相关聚合方案例如基于基数估计的近似去重可以继续阅读 聚合器文档而distinctCount本身的特点是单 segment 内精确去重代价是需要严格的分区与粒度配合并受限于 groupBy / topN 的中间行数与内存参数。在实践中请务必先完成第一节的分区与粒度设计再在生产环境大规模使用。赞分享数据库OLAP大数据后端【免费下载链接】druidApache Druid: a high performance real-time analytics database.项目地址https://gitcode.com/gh_mirrors/druid6/druid点击查看免费下载相关推荐Apache Druid DistinctCount 聚合器扩展详解精确去重计数UV的原理、安装与三种查询实战Apache Druid DistinctCount 聚合器扩展详解精确去重计数UV的原理、安装与三种查询实战 本篇技术指南围绕 Apache Druid数据库数据分析OLAP大数据实时分析数据仓库后端DrawerKit动画原理深度剖析如何实现流畅的抽屉过渡效果DrawerKit动画原理深度剖析如何实现流畅的抽屉过渡效果 DrawerKit是一个优秀的iOS自定义视图控制器转场库它能够让你的应用实现类似AppleApache Druid RabbitMQ Firehose 扩展实战指南从配置、原理到源码解析Apache Druid RabbitMQ Firehose 扩展实战指南从配置、原理到源码解析 导读 本文围绕 Apache Druid当前仓库版本 0.数据库数据分析OLAP大数据实时分析数据仓库后端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
企业数字化 ERP 产品动态
相关推荐
Z3 Go 绑定开发实战:基于 CGO 的 SMT 求解器 Go 语言接口完全指南 开发工具 【免费下载链接】z3 The Z3 Theorem Prover 项目地址: https://gitcode.com/gh_mirrors/z3/z3 点击查看 免费下载 Z3 是由微软研究院开发的高性能 SMT(Satisfiability Modulo Theories)求解器,而本仓库的 src/api/go 目… · 2026/9/23 4:05:13
Spring Boot Admin 基于 Hazelcast 的高可用集群部署实战指南 Spring Boot Admin 基于 Hazelcast 的高可用集群部署实战指南 【免费下载链接】spring-boot-admin Admin UI for administration of spring boot applications 项目地址: https://gitcode.com/gh_mirrors/sp/spring-boot-admin
Spring Boot Admin Server 默认将实例事件… · 2026/9/23 4:05:13
ALOHA协议吞吐率仿真与优化:从18.4%到时隙ALOHA的工程实践 简介:这份资源围绕ALOHA与时隙ALOHA多址接入协议的性能仿真展开,面向无线通信、卫星通信及局域网方向的学习者与研究人员,帮助理解时隙划分、随机发送、碰撞检测与捕获效应等核心机制。压缩包共2个文件,均为m脚本文件,… · 2026/9/23 23:02:06
C# UHF RFID上位机开发:从DEMO到实战的串口通信与EPC解析 简介:这份资源是面向C#开发者与RFID入门者的UHF RFID阅读器演示工程,围绕UHFReader09型号设备,展示如何在.NET环境下完成标签读取、写入、解码及阅读器参数控制等核心操作。压缩包共52个文件、约660KB,以cs源代码为主体࿰… · 2026/9/23 23:02:06
LSTM时间序列预测实战:Python源码解析与调参避坑指南 简介:基于LSTM的时间序列分析预测Python源码,面向数据科学、人工智能方向的学习者与开发者。项目以空气污染数据为例,完整覆盖数据加载与归一化、LSTM模型构建(基于Keras/TensorFlow)、模型训练、评估与未来值预测等环… · 2026/9/23 23:01:53
长尾商品销量预测:基于DNN的时序预测与特征工程实战 简介:面向供应链备货中的长尾商品销量预测难题,这份基于TensorFlow 1.13编写的DNN项目源码,提供了7天、30天和60天三档预测的实现思路,适合有一定Python基础、希望借助低阶API掌握模型训练与部署的开发者。压缩包共6个文件&#x… · 2026/9/23 23:01:53
EverOS 记忆工作原理:Markdown 为源、SQLite 与 LanceDB 为派生索引的分层存储与同步管线 EverOS 记忆工作原理:Markdown 为源、SQLite 与 LanceDB 为派生索引的分层存储与同步管线 【免费下载链接】EverOS One portable memory layer for every AI agent: local-first, Markdown-native, user-owned, and self-evolving across apps, tools, and workflow… · 2026/9/23 23:01:41
鸵鸟目标检测数据集:419张VOC+YOLO双格式标注 简介:本资源是一份面向计算机视觉初学者与目标检测实践者的鸵鸟图像数据集,适用于YOLO、Faster R-CNN等主流检测模型的训练与验证。数据集共419张高质量JPG图像(1–500KB),全部标注为单一类别“ostrich”,并… · 2026/9/23 23:01:35
3招搞定手机怎么下载微信面试难题实战项目解析 3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A… · 2026/9/23 0:00:03
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29