Druid原理及产险实践
本文根据平安产险大数据开发工程师李凯勃、关志华在平安产险&DataFunTalk大数据技术沙龙中分享的《Druid原理及产险实践》编辑整理而成。
今天分享的内容分为两部分,第一部分是Druid原理,包括相关选型、原理、架构以及调优经验。第二部分是BDAS使用场景,是基于Druid做的监控日志报表系统。
Druid非阿里开源的数据连接池,是一个MOLAP数据库,架构是MMDB架构,是一个多节点的系统。同时也是一个内存数据库,面向列的存储。同时会使用PreAGG,是一个NoSQL数据库,处理记录与时间有强关联的时序数据库。Druid同时在社区支持很多插件,如kafka插件、mysql插件、hdfs插件等。
我们从去年五月份做技术选型,spark是用的比较广的框架,特性Schema free,之前不用先定义数据格式就可以做存储解析;效率高(中间结果不落盘),响应时间依据数据量而定。最后没有选用spark是因为并发量上不去,因为我们业务并发量可能上千,使用spark很容易造成高温。Elastic Search也是很热门的一个领域,大家常见的理解就是一个全文搜索的引擎,其实在分析方面也有很多新技术。其特性也是Schema free,本身架构兼容这种数据格式,对比Druid的优点是会保存原始数据。同时拥有一个完整的技术栈(elk),非常通用完善。在分析的基础上有一个检索的功能。其实选择什么框架需要依据具体的场景,不同场景不同框架有不同的优势。支持高基维,但是缺点是数据量上不去,有时数据入库需要做倒排索引,但是索引数据量和原始数据相差不大,最后舍弃。Druid需要预先定义维度和指标,还支持预聚合,根据时间或维度做预聚合,这样入库后会丢弃原始数据。数据响应亚秒级,数据可用毫秒级,基本满足需求;Lambda 架构,扩张性、容错性高,我们选用的是Druid。SQL on Hadoop主要技术为MPP(大规模并行处理)和CS(列式存储),特点是吞吐量大需要离线批量处理,我们目前是实时与离线并行使用。其他商业产品企业级特性,SQL支持良好,定制化硬件,天花板低(PB级别以下),非线性拓展,扩容需要停机维护,最重要的一点是二次开发困难。最后技术选型为Druid,将其定位为实时可用一个上升的SaaS层服务,支持大型冷数据上的OLAP 场景,实现对一个多维度高基数的亚秒级响应的支持。
下面是原始数据从一开始的产生到入库的一些概念,原始数据有点类似传统数据库格式,而发表者、广告商我们认为是一个多维度,在入库时都要定义好。Druid还有一个特性就是面向行级别依据时间做切分,不同的行可能会切到不同的segment里面,对于列会做一个压缩。Segment是Druid存储的基本单元,是以timestamp进行数据分块的,这样做的好处是查询的时候可以避免全局扫描,查询就是遍历起始时间终止时间并找到对应数据块,因此查询场景比较快,真实的数据块命名格式为数据源加开始时间和结束时间。需要注意的是如果是比较大的场景,几个小时数据量可能就达到TB级别,这时建议在数据块上再做一个分块。
接下来讲一下Druid数据流转,流转图中有很多节点,每个节点都有自己的职责。中间有一个zookeeper,每一个节点都或多或少与其相连,zookeeper在其中负责同步作用,每一个节点不会做强关联工作,只需要用zookeeper同步。从左到右是一个数据写入过程,有离线数据和批量数据。
中枢节点Broker是查询节点,对外提供 REST 接口,接受来自外部客户端的查询,并将这些查询转发到 Realtime 和 Historical 节点。从这两个节点拿数据,然后将节点返回给Broker,将数据进行合并返回给客户端。这里broker节点起到一个转发和合并的作用,合并过程需要规定的内存,推荐配置内存相对大一点。
历史节点Historical 节点是非实时数据进行处理存储和查询的地方,只响应Broker请求。在查询数据时现在本地找,然后在深度存储里查找,查找到后返回给Broker,没有与其他节点关联。在 Zookeeper 的管理下提供服务,并使用 Zookeeper 监视信号加载或删除新数据段。这个节点也是非常吃内存,该节点可以多个节点,建议使用多个节点,每个节点互相不通信,同样利用zookeeper同步,将信息解耦开来。
Coordinator扮演一个管理者的角色,负责Historical节点组的数据负载均衡,确保数据可用、可复制,并且处于“最佳”配置。同时通过从My SQL读取数据段的元数据信息,来决定哪些数据段应该在集群中被加载,使用 Zookeeper 来确定哪个 Historical 节点存在,并且创建Zookeeper 条目告诉 Historical 节点加载和删除新数据段。该节点可以是一个,多个的节点进行选举产生 Leader,其余节点作为备份,一般两个也是满足需求的。
实时节点Realtime是实时摄取数据,负责监听输入数据流并让其在内部的 Druid 系统立即获取。如果不需要实时加载数据就可以将该节点去掉,他只会响应broker请求将数据返回给broker。如果Realtime和Historical节点同时返回同一种数据,Broker会认为Historical节点数据是可信的,如果数据进入深度存储Druid默认数据是不变的。该节点本身会存储数据,如果超过一段时间窗口会将数据传入深度存储,深度存储将数据提供给Historical节点。
MySQL、zookeeper、深度存储都是Druid的外部依赖,Deep Storage:可以是 HDFS 或 S3 或本地磁盘,用来保存“冷数据”,有两个个数据来源,一个是批数据摄入, 另一个来自实时节点;ZooKeeper 集群:为集群服务发现和维持当前的数据拓扑而服务; My SQL 实例:用来维持系统服务所需的数据段的元数据,比如去哪里加载数据段、每个数据段的元信息。
总结下各节点间分工明确,而且职责分离,挂掉某一个节点不影响其他节点的工作,对扩容友好,容错率高。冷热数据分离,不同数据通过硬件资源进行物理隔离。查询需求与数据如何在集群内分布的需求分离:确保用户的查询请求不会影响数据在集群内的分布情况,避免局部过热,影响查询性能。没有绝对master结构,不仅仅是一个内存数据库,增加了内存映射的能力。Lamada架构,能够实时校正数据,如果数据进入进来节点没有被消费掉会被丢弃掉,就会出现数据库性能问题。社区比较成熟的框架就是数据实时进来写到kafka,kafka数据两次消费,一次在存储节点上,一次在Hadoop上,如果数据不完整就再在Hadoop做一次embedding操作,补回数据。
上面是一个推荐的架构,希望broker节点越多越好,Coordinator节点两个,overload两个,realtime 其他节点也是越多越好。性能方面也会做不同性能的转换。调优方面经验,对于broker消耗内存大户 ,建议 20G-30G 堆内存,历史节点除了内存还有硬盘消耗,希望用更多的内存去释放硬盘的IO,Coordinator 消耗内存相对较小,只需要满足要求即可。查询时尽量做一些聚合优化,在摄入就做聚合,尽量少去group by。Historical 和 Realtime 分离,Coordinator 和 Broker 分离,在 Broker 上加 Nginx 做负载均衡,并高可用。异构硬件方面通过划分 Tier,让 Historical 加载不同时间范围的数据。
接下来讲一下具体项目应用,产险原使用 Cognos ( Oracle )处理清单报表,上线有十年历史。随着数据量的增长、以及分析处理的诉求增加,Cognos 在 cube 过大时受限的弊端日益体现,无法满足实际生产需要。需要实现的第一个就是要快,第二个是想实现行级别的全列控制。
DBAS系统从去年五月份调入Druid,九月份上线了清单功能,直接查hive上数据做业务分析,12月份完全引入Druid,实现多维分析功能。线上一共有数十个数据源,最大数据源有上百个维度,单一维度最大属性有几十万万。聚合后单表行记录有几十亿,最大单一数据源有几十G,日均访问量数千级,主要应用于产险内部分析,并发峰值数百,平均响应时间 <2s。
接下来介绍下在HDFS下的使用场景,第一种是透视图概念,用户在某一定条件(不断衰减)查看数据大体概要,一般采用Top N查询,秒级响应。响应方式是在前端一个维度一个维度拖动,后端将上一次结果缓存,最后只查询几个维度。Top N查询第一次查询只查单一维度,当增加维度在redis中取上一次缓存结果加下一维度,多维度会呈指数级增长,查询速度明显下降。我们引入单线程当初考虑了两种方式,第一种方式是依次将N个维度的top N都查出来,然后构造M*N*P个多线程,这样查询速度会很快,大概就是一个top N的时间,这样存在一个问题就是顺序不能保障。第二种方式采用递归的方式,并统一由线程池执行(是不是线程开线程?不是)更细粒度的缓存:如由维度A ,维度A 维度B 改为 维度A A1,维度A A2,维度A A1 维度B B1 ,这样可以充分利用Druid的升降序,花费的时间可能多点,,大约需要N*M个top N的时间。
第二种场景是交叉表,分析人需要看到全量数据而不是概要数据。开始就是无论查多少维度都将其组装成一起,当超过4-5个维度就会效率很低。改进的方式也是采用多线程,前面基本按照top N的方式构造,保留最后两个维度进行group by,A1 B1 C维在查询时有缓存策略,由于小集群采用block缓存,这样可以省去网络传输。两种场景一种采用top N,一种采用group by。两者区别top N可能会不准确,top 1000能保证前900是准确的。
第三种场景就是指标计算,第一种方式是先将其计算出来存储到hive上,到进入Druid,这样消耗很大。第二种方式是在Druid中计算,每次查询自定义就可以比较快的得到结果。
维度合并和隐藏,合并是用户希望把一些属性值统一对待,隐藏就是减少眼睛干扰,其实更好的方式是减少一个维度就好。第四个就是实现行全权控制,这是需要接入用户账号才能实现,用户有一个department code,因此在每个数据源都设置了四个列,过滤后达到行全权控制。
作者介绍:
李凯勃、关志华俩位老师在大数据领域有多年工作经验,对bdas系统、Druid数据库系统等有深入了解,专注于研究和开发大数据技术在金融领域的落地应用。
——END——