前言
设想有一批各种类型的离线(或实时)数据(文本、csv、Excel 等),我们如何挖掘这些数据背后的价值,分析这些数据之间的关联?
很容易想到的就是,写程序把每种数据按照某种规则抽取出来放到关系型数据库中进行分析。
这样做可能存在什么样的问题?
- 按照某种规则进行抽取,是否会导致原始数据信息的丢失?
- 因为前期设计不足或者后期需求变化导致数据的重复处理?
- 面对上千亿,甚至上万亿的数据,如何进行分析?
面对上面这些问题,现在比较常见的解决方案是 基于大数据平台构建数据仓库。
下面对数据仓库的设计和技术选型,结合自己的工作经历谈一些看法。
设计数据仓库的分层模型
1. 常见的分层模型
现在常见的分层模型,一般是参考阿里五层模型做的。如下
- ODS,Operational Data Store,原始数据
数据准备区,数据来源是各业务系统,表结构与原始表一致,增加导入时间,对数据不做清洗转换。 - DWD,Data Warehouse Detail,明细数据
数据来自 ODS,将增量数据进行合并生成全量数据,对数据不做清洗转换,方便后期进行溯源。 - DW(B/S)
基于 DWD 的数据,可以进行2个维度的数据抽取聚合,期间需要进行数据清洗和转换。
B:Base,公共维度的数据。
S:Service,面向主题的宽表。 - DM,Data Mart,数据集市
数据挖掘,自定义查询,应用集市。 - ST,数据应用
前端报表展现,主题分析。
下面是各层的依赖关系和数据流向图:
基于 Hive 来实现前三层是非常合适的,hive 的数据存储在 hdfs 上。hdfs 的扩容是很容易的,故大数据量不再成为问题。另外 hive 提供了类似 sql 的查询语句,提供了无需编程就可以对海量数据进行查询分析的能力。
实际应用中 ODS 和 DWD 一般可以合为一层,建一个库特地用来存放原始数据,务必要增加一个导入时间的字段,后期会发现有很多好处。
DW 可以细分为抽象的公共数据和主题数据。抽象的公共数据,可以理解为设计一种非常通用的抽取规则,适用于大部分原始数据。主题数据,这个粒度更细一些,将原始数据进行分类,同一类别的数据放到同一张表里,方便应用进行对接。
DM 即融入了一些业务逻辑,将 DW 层的数据进行了一些处理和分析,并将结果根据需求存到不同的存储系统里。比如 本月最受欢迎的商品、购买力较高的用户等。有时也会直接对接原始数据,针对单表进行分析还好,如果是多表可能存在效率问题,而且这种方式跟原始数据耦合度太高了,不太推荐这种方式。
ST 这个就理解为对 DM 计算好的数据进行展示的界面,也可以是跟用户实时交互的,比如 用户输入一些分析参数,DM 层将结果计算好后再展现给用户。
2. hive 分区方案
需要设置分区字段吗?如果需要,如何设计分区方案?
可以先看一个 hdfs 的路径:/apps/hive/warehouse/origin.db/shopping
仔细看一下,这个其实就是 hive 数据在 hdfs 上的存储位置。如果不设置分区,那么所有的数据都将放到 shopping 这个目录下,每次查询就需要遍历一遍所有的文件,想想有点可怕。
数据量大且数据是多次入的,那么设置分区字段是很有必要的。
下面看下两种比较好的分区方式:
- 按批次号分区
比如:
/apps/hive/warehouse/origin.db/shopping/sp001
/apps/hive/warehouse/origin.db/shopping/sp002
这种分区方法适合数据是分批入的,每次入数据时设置一个批次号。这样在分析数据时可以指定分析某个批次或者某些批次的数据,可以减少输入数据的大小,加快分析过程。 - 按时间分区
比如:
/apps/hive/warehouse/origin.db/shopping/202001 或者 /apps/hive/warehouse/origin.db/shopping/202001/20200115
这种分区方法适合准实时(或者按月、按天)接入的数据,这种方式很通用,也很实用。比如 某个需求是分析用户最近一月的商品浏览记录。
根据每层的特点选用技术
这里主要谈一下前四层实现时涉及到的技术和存储系统。
1. 原始数据层(ODS,DWD)
回到一开始的场景,这一批离线(或实时)数据如何入到 hive 里面呢?中间需要做什么处理?
先列举几个关键词,ETL,预处理(python/groovy),后处理,hive 入库。
ETL(Extract,Transform,Load):
提取,转换,加载 是很常见的数据处理模式。将处理流程分为三段,属于将流程标准化,每一步骤由专门的程序去做,如果某一步骤失败了,那么只需要重做这一步即可,不用重复整个数据处理流程,想想就很开心。
- E,Extract
提取数据一般用预处理程序,常见的技术选择是 python 或 groovy,因为这两种脚本语言支持即改即用。
常见的使用场景是针对不同类型的文件写预处理脚本,设置一个输入目录,将原始文件放进去,程序根据文件名匹配不同的预处理脚本进行处理。
结果可以输出为结构化的文件,比如 csv。也可以将结果发送到 kafka ,然后交由下一步的程序处理。 - T,Transform
这一步处理预处理生成的文件,可以对原始数据进行简单的转换,比如 统一字符集(全角/半角),统一时间格式,注意不能导致原始数据信息的损失。
然后,将数据转换成可以入库到 hive 的格式,这一步还涉及到获取数据的 schema,不展开讲了。
曾经做过实验对比,一次入库10万条数据跟一次入库100万条数据到 hive 里面,时间消耗是差不多的,所以这里务必要缓存数据,数据达到一定量级再输出到文件。
在程序里面缓存数据可能存在什么问题?
- 数据丢失
设想,数据输出阈值是10万,目前仅拿到5万条数据,此时因为某个原因,程序被 kill 了,那么这缓存的数据是不是就丢了。
解决,监听程序关闭事件,程序关闭时将所有的缓存数据输出(kill -9 例外,所以不要轻易用这个指令)。 - 有数据一直无法输出
设想,数据输出阈值是10万,目前仅拿到5万条数据,然后过了好几天都没有新的数据,缓存的数据是不是一直无法输出到文件。
解决,比较简单,设置数据超时时间,比如2个小时。
- L,Load
这里需要编写一个入库程序,功能是监听输入目录,将数据导入到 hive 里面(入库时指定分区),失败的数据需要单独保存起来。
至此,这一批数据经过 ETL 过程就来到了原始数据层。
2. 抽象数据层(DWB,DWS)
- DWS,主题数据
根据业务需要设计多个主题,然后将原始数据抽到不同的主题库里面。以购物举例,比如 用户浏览商品,高流量的商铺等。
这里的技术选择一般有两种:
- hive sql + shell + cron
编写 sql 将数据从原始数据抽到主题库中,编写好的 sql 放到 shell 脚本里面,由 cron 去定期调度执行。
优点是实现比较简单,能够快速响应修改。
缺点很明显,sql 能够提供的数据处理和聚合能力有限,cron 的执行情况只能去日志里面看。 - spark + airflow
编写 spark 程序,将原始数据抽出来,然后进行过滤、转换、聚合等操作,结果再入到主题库里面,有 airflow 定期调度执行。
优点是可以对数据执行更多、更复杂的处理操作,airflow 提供了专门的管理界面展示任务的执行情况,功能比 cron 强大很多。
缺点就是对需求的响应比 sql 慢了,另外就是属于比较重量级的方案了,需要花费更多的精力安装、部署、调试了。
- DWB,抽象的公共数据
处理流程和技术选择和主题数据基本是一致的,区别在于,它比主题库的抽象程度更高,如果主题库面对的是用户浏览商品的记录,那么它面对的就是用户这个对象了。高层次的抽象是有必要的,可以更加全面的分析一个事物,类比下 宏观经济和微观经济(暂且认为贴切)。
3. 数据集市层(DM)
这一层,就是各种不同业务发挥作用的地方了,我们一般称为各个应用。比如 展示分析结果的应用、与用户交互执行查询分析的应用。
先来回忆下我们的支付宝年账单,出行、线下消费、网购等等,这个是不是跟前面的主题库对应上了。这种类似报表一样的就是针对分析结果进行展示的应用。
现在我们站在网店卖家(假设食品店)的角度,需要获取哪些信息来提升网店的销售额呢?
- 经常浏览店铺的都是哪些年龄段的人呢? ——决定我宣传和销售的策略
- 最近一个月哪些商品(食品)比较畅销? ——决定我后面进货的策略
…
针对第二个问题分析一下需求:
输入:时间范围,商品种类
输出:畅销的商品,即在给定时间范围内销量 top n 的商品。
处理流程:把全网给定时间范围内食品类商品的销售记录拿出来,根据销量排序后取 top n ,然后返回
这种就是需要跟用户交互的查询分析类应用。
那么实现这些功能,需要哪些技术和存储系统呢?
先看一组关键词:Spark,HBase,Solr,ElasticSearch,HiveSql,MapReduce
技术和存储系统有了,如何来解决上面的问题呢?
- 支付宝年账单问题
以出行为例,假设出行库已经构建好,可以使用 Spark(MapReduce) 程序将出行库中的数据读出来,然后按账户进行分组聚合、统计,将结果发送到 hbase。
接下来,提供一个 hbase 查询服务,当有用户需要查看自己的年账单时,就可以实时从 hbase 里将计算好的结果取出来进行展示。 - 食品网店卖家问题
这个问题看下是不是可以用 sql 来解决。
时间范围:where sale_time >= ‘20200101’ and sale_time < ‘20200201’
商品种类:where product_type=‘食品’
根据销量排序后取 top n:order by sale_num desc limit 10
到此可以生成一条完整的 hive sql :
select product_name,sale_num from product_sale
where sale_time >= '20200101' and sale_time < '20200201'
and product_type='食品'
order by sale_num desc limit 10;
因为 hive sql 是比较慢(由 hive 特性决定)的,所以用户需要创建一个离线任务,等待任务完成后再查看结果。
任务结果可以发送到 Solr,ES,HBase 都可以,这三种存储系统都支持实时查询。
实现过程可能遇到的困难
1. hive 数据如何实现更新
hive 里面的数据一般是不会进行修改的,因为 hive 本身的定位就是用来做查询和分析的,因此默认是不支持 update 和 delete(高版本 hive 可以增加一些附加配置来实现)。
实际场景中,一般也是不会对某些行执行更新操作的。通常是某批数据处理得有问题,然后又和前面正常的数据混在一起了,如果设置了分区字段,可以很容易的通过删除分区,然后重新入库数据来实现,如下:
ALTER TABLE table1 DROP PARTITION (day='20190722');
2. hive sql 执行慢
- 避免全表扫描
hive 里面的数据是以一个个文件的方式存储在 hdfs 上,如果全面扫描则意味着需要操作很多的文件。
所以,尽量使用分区字段进行过滤。 - 布隆过滤器
简单的介绍下布隆过滤器(BloomFilter),使用布隆过滤器后,可以在扫描文件前,知道所查找的值是否在此文件中,因此可以避免扫描很多文件,速度自然就快了。当然这种方式属于用空间换时间。
总结
本文从个人实际经历出发,介绍了数据仓库的分层模型(基于 hive)和相关技术选型,以及设计和开发中的一些注意点。数据仓库的内容和涉及技术是比较多的,相比起来,本文还是比较浅显的,但是个人觉得做技术是需要多总结和分享的。后面还需要多深入研究下数仓,与君共勉。