一个现代化的云原生,流原生,分析型数据库Druid是为快速查询和快速摄入数据的工作流而设计的。Druid强在有强大的UI,运行时可操作查询,和高性能并发处理。Druid可以被视为一个满足多样化用户场景的数据仓库的开源替代品。
轻松与现有的数据管道集成Druid可以从消息总线流式获取数据(如Kafka,AmazonKinesis),或从数据湖批量加载文件(如HDFS,AmazonS3和其他同类数据源)。
比传统方案快100倍的性能
解锁新的工作流Druid为Clickstream,APM(应用性能管理系统),supplychain(供应链),网络遥测,数字营销和其他事件驱动形式的场景解锁了新的查询方式和工作流。Druid专为实时和历史数据的快速临时查询而构建。
可以部署在AWS/GCP/Azure,混合云,k8s和租用服务器上Druid可以部署在任Linux环境中,无论是内部环境还是云环境。部署Druid是非常easy的:通过添加或删减服务来扩容缩容。
ApacheDruid适用于对实时数据提取,高性能查询和高可用要求较高的场景。因此,Druid通常被作为一个具有丰富GUI的分析系统,或者作为一个需要快速聚合的高并发API的后台。Druid更适合面向事件数据。
比较常见的使用场景
Druid常常用来收集和分析网络流数据。Druid被用于管理以任意属性切分组合的流数据。Druid能够提取大量网络流记录,并且能够在查询时快速对数十个属性组合和排序,这有助于网络流分析。这些属性包括一些核心属性,如IP和端口号,也包括一些额外添加的强化属性,如地理位置,服务,应用,设备和ASN。Druid能够处理非固定模式,这意味着你可以添加任何你想要的属性。
Druid经常用于商业智能场景。公司部署Druid去加速查询和增强应用。和基于Hadoop的SQL引擎(如Presto或Hive)不同,Druid为高并发和亚秒级查询而设计,通过UI强化交互式数据查询。这使得Druid更适合做真实的可视化交互分析。
如果您的使用场景符合以下的几个特征,那么Druid是一个非常不错的选择:
如果您的使用场景符合以下特征,那么使用Druid可能是一个不好的选择:
ES:最大的特点是使用了倒排索引解决索引问题。根据研究,ES在数据获取和聚集用的资源比在Druid高。框架选型:
Druid同时支持流式和批量数据摄入。Druid通常通过像Kafka这样的消息总线(加载流式数据)或通过像HDFS这样的分布式文件系统(加载批量数据)来连接原始数据源。Druid通过Indexing处理将原始数据以segment的方式存储在数据节点,segment是一种查询优化的数据结构。
数据副本Druid根据配置的副本数创建多个数据副本,所以单机失效不会影响Druid的查询。独立服务Druid清晰的命名每一个主服务,每一个服务都可以根据使用情况做相应的调整。服务可以独立失败而不影响其他服务的正常运行。例如,如果数据摄入服务失效了,将没有新的数据被加载进系统,但是已经存在的数据依然可以被查询。自动数据备份Druid自动备份所有已经indexed的数据到一个文件系统,它可以是分布式文件系统,如HDFS。你可以丢失所有Druid集群的数据,并快速从备份数据中重新加载。滚动更新通过滚动更新,你可以在不停机的情况下更新Druid集群,这样对用户就是无感知的。所有Druid版本都是向后兼容。
获取Druid安装包有以下几种方式
1CPU,4GB内存启动命令:bin/start-nano-quickstart配置目录:conf/druid/single-server/nano-quickstart
4CPU,16GB内存启动命令:bin/start-micro-quickstart配置目录:conf/druid/single-server/micro-quickstart
8CPU,64GB内存(~i3.2xlarge)启动命令:bin/start-small配置目录:conf/druid/single-server/small
16CPU,128GB内存(~i3.4xlarge)启动命令:bin/start-medium配置目录:conf/druid/single-server/medium
32CPU,256GB内存(~i3.8xlarge)启动命令:bin/start-large配置目录:conf/druid/single-server/large
64CPU,512GB内存(~i3.16xlarge)启动命令:bin/start-xlarge配置目录:conf/druid/single-server/xlarge
Java8(8u92+)Linux,MacOSX,或者其他的类UnixOS(Windows是不支持的)安装Docker环境安装Docker-compose环境
Druid包括几个单服务配置示例,以及使用这些配置启动Druid进程的脚本。如果您在笔记本电脑等小型机器上运行以进行快速评估,那么micro-quickstart配置是一个不错的选择,适用于4CPU/16GBRAM环境。如果您计划在教程之外使用单机部署进行进一步评估,我们建议使用比micro-quickstart更大的配置。虽然为大型单台计算机提供了示例配置,但在更高规模下,我们建议在集群部署中运行Druid,以实现容错和减少资源争用。
安装推荐Imply方式,Imply方式出了提供druid组件,还有图形化、报表等功能
因为启动druid需要用到perl环境,需要安装下
选择与自己系统相匹配的版本,我的是Centos764位的,所以如果是我的话我会选择此版本,要记住的你们下载的话选择的是以tar.gz结尾的。
上传文件,解压目录
mkdir/usr/local/javatar-zxvfjdk-8u301-linux-x64.tar.gz配置环境变量配置环境变量,修改profile文件并加入如下内容
source/etc/profile检查环境
#创建imply安装目录mkdir/usr/local/imply#解压implytar-zxvfimply-2021.05-1.tar.gz环境准备进入imply-2021.05-1目录后
#进入imply目录cdimply-2021.05-1快速启动使用本地存储、默认元数据存储derby,自带zookeeper启动,来体验下druid
#创建日志目录mkdirlogs#使用命令启动nohupbin/supervise-cconf/supervise/quickstart.conf>logs/quickstart.log2>&1&查看日志通过quickstart.log来查看impl启动日志
访问Imply可以通过访问9095端口来访问imply的管理页面
我们导入演示案例种的演示文件
因为我们是通过imply安装的,在Basedirectory输入绝对路径/usr/local/imply/imply-2021.05-1/dist/druid/quickstart/tutorial,Filefilter输入wikiticker-2015-09-12-sampled.json.gz,并选择apply应用配置,我们数据已经加载进来了
在这里可以新增虚拟列,将一个列的数据转换成另一个虚拟列,这里我们没有设置,直接跳过
这里我们使用docker-compose的方式启动kafka
#运行kafka生产者发送消息./kafka-console-producer.sh--broker-list192.168.64.173:9092--topictest发送的数据如下
编写代码编写代码发送消息到kafka中
关闭集群
准备大量数据提供查询,我们插入1万条随机打车数据
#创建查询目录mkdirquery#编辑查询的JSONviquery/filter1.json{"queryType":"timeseries","dataSource":"taxi_message","granularity":"month","aggregations":[{"type":"count","name":"taxiNum"}],"filter":{"type":"selector","dimension":"status","value":1},"intervals":["2021-06-07/2022-06-07"]}参数解释
执行查询命令在命名行中执行下面的命令会将查询json发送到对应的broker中进行查询--data-binary指定的查询json的路径
druid查询采用的是HTTPRESTFUL方式,REST接口负责接收客户端的查询请求,客户端只需要将查询条件封装成JSON格式,通过HTTP方式将JSON查询条件发送到broker节点,查询成功会返回JSON格式的结果数据。了解一下druid提供的查询类型
viquery/topN.json{"queryType":"topN","dataSource":"taxi_message","dimension":"local","threshold":2,"metric":"age","granularity":"Quarter","aggregations":[{"type":"longMin","name":"age","fieldName":"age"}],"filter":{"type":"selector","dimension":"sex","value":"也"},"intervals":["2021-06-07/2022-06-07"]}执行查询
在实际应用中经常需要进行分组查询,等同于sql语句中的Groupby查询,如果对单个维度和指标进行分组聚合计算,推荐使用topN查询,能够获得更高的查询性能,分组查询适合多维度,多指标聚合查询
viquery/groupBy.json{"queryType":"groupBy","dataSource":"taxi_message","granularity":"Quarter","dimensions":["sex"],"aggregations":[{"type":"count","name":"taxiNum"}],"filter":{"type":"bound","dimension":"age","lower":"21","upper":"31","alphaNumeric":true},"intervals":["2021-06-07/2022-06-07"]}执行查询
在介绍具体的查询之前,我们先来了解一下各种查询都会用到的基本组件,如Filter,Aggregator,Post-Aggregator,Query,Interval等,每种组件都包含很多的细节
Filter就是过滤器,在查询语句中就是一个JSON对象,用来对维度进行筛选和过滤,表示维度满足Filter的行是我们需要的数据,类似sql中的where字句。Filter包含的类型如下:选择过滤器SelectorFilter的功能类似于SQL中的wherekey=value,它的json示例如下
"Filter":{"type":"selector","dimension":dimension_name,"value":target_value}使用案例
viquery/filter1.json{"queryType":"timeseries","dataSource":"taxi_message","granularity":"month","aggregations":[{"type":"count","name":"taxiNum"}],"filter":{"type":"selector","dimension":"status","value":1},"intervals":["2021-06-07/2022-06-07"]}正则过滤器RegexFilter允许用户使用正则表达式进行维度的过滤筛选,任何java支持的标准正则表达式druid都支持,它的JSON格式如下:
"filter":{"type":"regex","dimension":dimension_name,"pattern":regex}使用案例,我们搜索姓名包含数字的的用户进行聚合统计
viquery/filter2.json{"queryType":"timeseries","dataSource":"taxi_message","granularity":"month","aggregations":[{"type":"count","name":"taxiNum"}],"filter":{"type":"regex","dimension":"username","pattern":"[0-9]{1,}"},"intervals":["2021-06-07/2022-06-07"]}执行查询
"filter":{"type":"and","fields":[filter1,filter2]}"filter":{"type":"or","fields":[filter1,filter2]}"filter":{"type":"not","fields":[filter]}使用案例,我们查询每一个月,进行打车并且是女性的数量
viquery/filter3.json{"queryType":"timeseries","dataSource":"taxi_message","granularity":"month","aggregations":[{"type":"count","name":"taxiNum"}],"filter":{"type":"and","fields":[{"type":"selector","dimension":"status","value":1},{"type":"selector","dimension":"sex","value":"也"}]},"intervals":["2021-06-07/2022-06-07"]}进行数据查询
viquery/filter4.json{"queryType":"timeseries","dataSource":"taxi_message","granularity":"month","aggregations":[{"type":"count","name":"taxiNum"}],"filter":{"type":"in","dimension":"local","values":["四川省","江西省","福建省"]},"intervals":["2021-06-07/2022-06-07"]}进行数据查询
{"type":"bound","dimension":"age","lower":"21",#默认包含等于"upper":"31",#默认包含等于"alphaNumeric":true#数字比较时指定alphaNumeric为true}使用案例,我们查询每一个月,年龄在21-31之间打车人的数量
viquery/filter5.json{"queryType":"timeseries","dataSource":"taxi_message","granularity":"month","aggregations":[{"type":"count","name":"taxiNum"}],"filter":{"type":"bound","dimension":"age","lower":"21","upper":"31","alphaNumeric":true},"intervals":["2021-06-07/2022-06-07"]}进行数据查询
编写测试,我们这里按照季度聚合,并且我们过滤年龄是21-31的数据,并且按照地域以及性别进行分组
viquery/filter6.json{"queryType":"groupBy","dataSource":"taxi_message","granularity":"Quarter","dimensions":["local","sex"],"aggregations":[{"type":"count","name":"taxiNum"}],"filter":{"type":"bound","dimension":"age","lower":"21","upper":"31","alphaNumeric":true},"intervals":["2021-06-07/2022-06-07"]}进行查询
viquery/filter7.json{"queryType":"groupBy","dataSource":"taxi_message","granularity":{"type":"duration","duration":7200000},"dimensions":["local","sex"],"aggregations":[{"type":"count","name":"taxiNum"}],"filter":{"type":"bound","dimension":"age","lower":"21","upper":"31","alphaNumeric":true},"intervals":["2021-06-07/2021-06-10"]}数据查询
viquery/filter8.json{"queryType":"groupBy","dataSource":"taxi_message","granularity":{"type":"period","period":"P1D"},"dimensions":["sex"],"aggregations":[{"type":"count","name":"taxiNum"}],"filter":{"type":"bound","dimension":"age","lower":"21","upper":"31","alphaNumeric":true},"intervals":["2021-06-07/2021-06-10"]}数据查询
Aggregator是聚合器,聚合器可以在数据摄入阶段和查询阶段使用,在数据摄入阶段使用聚合器能够在数据被查询之前按照维度进行聚合计算,提高查询阶段聚合计算性能,在查询过程中,使用聚合器能够实现各种不同指标的组合计算。
公共属性聚合器的公共属性介绍
计数聚合计数聚合器,等同于sql语法中的count函数,用于计算druidroll-up合并之后的数据条数,并不是摄入的原始数据条数,在定义数据模式指标规则中必须添加一个count类型的计数指标count;比如想查询Roll-up后有多少条数据,查询的JSON格式如下
viquery/aggregator1.json{"queryType":"timeseries","dataSource":"taxi_message","granularity":"Quarter","aggregations":[{"type":"count","name":"count"}],"filter":{"type":"bound","dimension":"age","lower":"21","upper":"31","alphaNumeric":true},"intervals":["2021-06-07/2022-06-07"]}求合聚合求和聚合器,等同于sql语法中的sum函数,用户指标求和计算,druid提供两种类型的聚合器,分别是long类型和double类型的聚合器;第一类就是longSumAggregator,负责整数类型的计算,JSON格式如下:{“type”:“longSum”,“name”:out_name,“fieldName”:“metric_name”}第二类是doubleSumAggregator,负责浮点数计算,JSON格式如下:{“type”:“doubleSum”,“name”:out_name,“fieldName”:“metric_name”}示例
viquery/aggregator2.json{"queryType":"timeseries","dataSource":"taxi_message","granularity":"Quarter","aggregations":[{"type":"longSum","name":"ageSum","fieldName":"age"}],"filter":{"type":"bound","dimension":"age","lower":"21","upper":"31","alphaNumeric":true},"intervals":["2021-06-07/2022-06-07"]}5.6DruidSQLDruidSQL是一个内置的SQL层,是Druid基于JSON的本地查询语言的替代品,它由基于ApacheCalcite的解析器和规划器提供支持DruidSQL将SQL转换为查询Broker(查询的第一个进程)上的原生Druid查询,然后作为原生Druid查询传递给数据进程。除了在Broker上转换SQL)的(轻微)开销之外,与原生查询相比,没有额外的性能损失。
可以在druid的控制台进行查询
我们在这里实现SpringBoot+Mybatis实现SQL查询ApacheDruid数据
@Data@ToStringpublicclassTaxiMessage{privateString__time;privateIntegerage;privateIntegercreateDate;privateStringlocal;privateStringsex;privateIntegerstatus;privateStringstatusText;privateStringusername;}编写mapper所有字段名、表名必须使用如下方式标识“表名”
@MapperpublicinterfaceTaxiMessageMapper{@Select("SELECT*FROM\"taxi_message\"whereusername=#{username}")publicTaxiMessagefindByUserName(Stringusername);}编写Service
@ServicepublicclassTaxiMessageService{@AutowiredprivateTaxiMessageMappertaxiMessageMapper;publicTaxiMessagefindByUserName(Stringusername){returntaxiMessageMapper.findByUserName(username);}}编写启动类
@SpringBootApplication@MapperScan(basePackages="com.druid.mapper")publicclassApplication{publicstaticvoidmain(String[]args){SpringApplication.run(Application.class);}}