Druid 是一个实时分析型的数据库,用于大规模实时数据导入、快速查询分析的场景,包括网站访问点击流分析、网络性能监控分析、应用性能指标存储与分析、供应链分析、广告分析等。
Druid 的核心集成了数据仓库、时序数据库、日志搜索系统的设计,主要包含如下特性:
- 列式存储:Druid 使用列存方式组织数据,访问时可按需加载访问到的列,支持快速的扫描和聚合计算能力;同时数据按列式存储,能极大的提升数据的压缩率。
- 分布式可扩展:Druid 集群可扩展至上百台服务器,可以高并发出力读写请求,提供每秒百万级的数据导入,以及亚秒级的查询延时。
- 支持实时及批量导入:Druid 支持实时或批量方式导入数据,非常方便点支持从 Kafka、Hadoop 等数据源导入数据。
- 高可用&负载均衡:Druid 集群支持在线的增加、移除服务节点,集群会进行自动的负载均衡,当有节点故障时,Druid 通过也可通过多副本高可用的方式自动 Failover。
- 云原生架构:Druid 将数据存储在外部 Deep Storage(例如 云存储、HDFS 等),即使 Druid 服务节点故障,也不影响数据的可靠性。
- 索引加速:Druid 通过位图方式自动对数据建索引,支持快速的索引过滤。
- 时间分区:Druid 会先将数据按时间分区,也可根据其他方式进一步分区,基于时间范围的查询只会访问对应时间范围内地数据。
- 预聚合:Druid 支持在导入数据时对数据进行提前的聚合分析,例如sum、count、min、max等,作为数据的元数据存储,当实际访问时,可直接访问预聚合好的数据。
- SQL 支持:Druid 同时支持 SQL、HTTP 方式访问,表达能力强,灵活方便。
Druid 数据模型
架构解析
核心组件
- Coordinator 负责集群的协调及数据高可用
- Overlord 控制集群数据导入任务的分配
- Broker 处理客户端查询请求
- Router 是可选的路由组件
- Historical 负责可查询数据的存储
- MiddleMangager 负责数据的导入
部署方式
Druid 的各个组件可以随意部署,但根据组件的职能,会分成三类,每一类组件建议在服务器上混部。
- Master Servers:运行集群的 Coordinator 与 Overlord 控制类的组件。
- Query Servers:运行集群查询类组件,包括 Broker、Router
- Data Servers:运行集群数据导入、存储相关组件,包括 Middle Managers、Histricals
外部依赖
Druid 本身不存储数据,数据的存储依赖于外部的组件,数据的存储(Deep Storage)依赖外部的存储,例如 AWS S3、阿里云 OSS、HDFS 等分布式存储,云数据存储依赖 MySQL、PostgreSQL 等数据库;依赖 Zookeeper 实现服务发现、Leader 选举等功能。
Deep Storage
Druid 本身不存储数据,而将数据存储到外部的 Deep Storage,由 Deep Storage 保证数据的可靠存储,例如 AWS S3、阿里云 OSS、HDFS 等分布式存储。
Druid 的数据会按数据顺序组织,并按时间维度对数据进行分区存储,一段时间范围的数据会存储到一起,组成一个 Segment。数据在 Segment 里会按列存方式进行压缩存储,并对 Dimension 数据建立索引。
Segment 结构
Druid 的所有数据都包含时间戳列,还包含多个 Dimensions 以及 Metrics 列,其中 Dimension 列可支持快速过滤、聚合,Druid 在存储 Dimension 列时,会进行压缩存储,并通过位图方式建索引,每一列的数据包含
- Dictionary:存储列值到 整型 ID 的映射
- Column Data:根据 1产生的一系列的整型 ID,进行压缩存储
- Inverted Index(Bitmaps):针对 Column 里每个不同的 value,会建一个位图倒排索引
比如 Page 列的存储,包含 "Justin Bieber", "Ke$ha" 两个取值,该列对应的存储类似如下三个部分
1: Dictionary that encodes column values
{
"Justin Bieber": 0,
"Ke$ha": 1
}
2: Column data
[0,
0,
1,
1]
3: Bitmaps - one for each unique value of the column
value="Justin Bieber": [1,1,0,0]
value="Ke$ha": [0,0,1,1]
当某一段时间范围内地数据量很大时,在将数据存储为 Segments 时,可以采用 sharding 策略,比如按文件大小切分 Segments、或根据指定的 Dimension 进行 Hash 分到多个 Segments,在检索的时候,能进一步减少需要查询的数据。
读写流程
数据导入
Druid 支持从 Kafka、Hadoop 里导入数据,数据导入以 Task 方式进行,Overlord 负责导入任务的分配,Middle Manager 负责实际的数据导入,数据会先写到 Middle Manager 的内存,积累到一定大小或时间窗口后,数据会组织为 Segment 写到 Deep Storage,并将 Segment 的元数据写入到 Metadata Storage。
Coordinator 会周期性的检测 Metadata Storage,当发现新的 Segment 产生时,会将 Segment 根据负载情况分给其中的部分 Historical(根据副本数) 节点管理,Historical 节点接管 Segment 的管理,这部分 Segment 即可用于查询。
数据查询
Broker 接收数据的查询请求,根据 Metadata 的信息,计算出查询关联的 Middle Managers、Historicals 节点,并将请求发送到对应的节点, Middle Managers、Historicals 根据查询的时间范围,找出所有可能包含查询数据的 Segments,并从中过滤出满足条件的数据,Broker 负责将查询结果进行汇总返回给客户端。
总结
- Druid 与传统数据库通过读写 API 写入数据的方式不同,通过 Pull 方式拉取数据,对接常用的 Kafka、HDFS等大数据生态数据源。
- 借助外部可靠的 Deep Storage 和 Meatadata store 来实现数据、元数据的存储,将 Druid 从数据存储的高可靠管理中解放,让各个组件的实现都非常轻量;
- Druid 的实现高度模块化,每个模块有独立的职能,但因为组件非常多,在部署管理上稍微有些复杂。
- 通过列式存储以及位图索引,极大的降低存储成本,并支持高效的数据过滤查询。
- 通过时间分区策略,对事件型、时序类型场景非常友好,能快速根据查询时间范围降低扫描的数据量。