- 大数据与机器学习:实践方法与行业案例
- 陈春宝 阙子扬 钟飞
- 402字
- 2023-01-13 13:45:20
第1章
数据与数据平台
合抱之木,生于毫末;九层之台,起于垒土;千里之行,始于足下。
——《老子》
世界的本质是数。
——毕达哥拉斯
数据时时刻刻在伴随着我们的工作和生活,就像空气围绕着我们一样,以致于我们常常忽略了它的存在。但如果你立志做一个崇尚数据的人,静下心来像科学家研究空气一样研究数据,就会发现数据为我们认知事物打开了一条全新的途径。
可以通过数据认识自身:人类全身的肌肉大约有639块,由60亿条肌纤维构成,而起着重要作用的大脑则由140亿个细胞构成。
可以通过数据描述我们的工作:周一上午10:00~11:30召开会议,讨论公司第三季度的销售目标。
可以通过数据描述我们的行为:花费6088元购买一台iPhone 6手机,中速游泳60分钟消耗约1000千卡的热量。
还可以通过数据认识我们所处的环境:现在时间是14:00,当前温度为28℃,上个月的CPI同比上涨1.4%,蔬菜和水果价格上涨了6.7%。
甚至可以通过数据认识遥不可及的物体:太阳直径为1392 000公里,表面温度达57809开尔文……数据让我们认识了世间万物,那么我们该如何认识数据本身呢?
数据的本质是一个十分深奥且宽泛的话题,甚至带有哲学的意味。作为技术类书籍,本书不尝试从哲学的角度研究数据,而是基于实践,从思维和技术手段出发来认识、理解、处理并分析周围的数据。为了更加具体,本书研究的数据定位于企业经营数据。
本章首先将从数据的基本形态入手,介绍企业中数据的来源和表现形态;然后介绍与之相关的数据平台,并简单介绍两类应用系统。在着手处理数据之前,让我们先对数据有一个清晰的认识。
1.1 数据的基本形态
我们不是自然科学家,但是可以借鉴自然科学的思路来看待数据问题。问题是数据具有形态吗?虽然数据并不具有固态、液态或气态等形态,但是可以根据需要为数据定义属于自己的专属形态。
一旦为数据赋予了恰当的形态,并在一定范围内(比如在一个公司内部)达成共识,形成对数据的系统化认识,就可以基于这些数据形态提出相应的管理和使用方案,提升数据的效率和价值。
一般情况下,对于企业经营中产生的数据,可以定义为三种形态:生产数据、原始数据和分析数据。这些数据形态的产生,是基于企业应用系统所在的生产环境和分析环境而存在的,在深入讨论数据形态之前,我们先来熟悉一下数据所在的环境。
1.1.1 数据环境与数据形态
数据环境是指数据存储、处理、转换所处的物理环境,常见的数据环境有生产环境、分析环境和测试环境。
生产环境是生产应用系统实时运行所在的环境,而生产应用系统则是一系列业务逻辑的组合。我们可以把生产环境想象成人的身体,生产应用系统就是人体中的各个系统(消化系统、呼吸系统等),业务逻辑则是这些系统中的“经络”,而数据便是运行于经络之中的“气血”。数据从“经络”中的一个“穴位”流转到另一个“穴位”,并在“流淌”中发生变化,所以,生产环境中的数据是“动态变换”的数据,我们称为生产数据。
分析环境是与生产环境物理解耦的一个数据环境。在生产环境中,由于数据总是处于不停变化中,这些数据的变化将直接反映为业务逻辑结果的变化,因此不应该尝试在生产环境中对数据进行分析处理。为了不影响生产环境的正常运行,需要将生产环境中的“动态”数据的快照保存下来(例如每日凌晨将时间戳为昨日的数据导出),这些数据快照是“静态”的,我们称为分析数据,保存分析数据的物理环境即我们所说的分析环境。
在实际中,还有另外一个环境,即测试环境。测试环境中的数据也是独立于生产环境和分析环境的,由于测试环境的数据通常不是有效的数据,因此本书不关注测试环境的数据。
至此,根据数据所处的环境,我们将数据定义为三种基本形态:生产数据、原始数据和分析数据。
生产数据存在于生产环境之中,分析数据存在于分析环境之中。此外,在生产数据和分析数据之间,还存在一种过渡形态的数据,即原始数据。图1-1展示了数据环境及其对应的数据形态。
图1-1 数据环境及其对应的数据形态
注意,图1-1中所示的原始数据,既不属于生产环境也不属于分析环境,这意味着它不直接用于生产,也不直接用于分析。原始数据作为生产数据到分析数据的中间形态存在,本书随后的章节将进一步讨论原始数据的相关问题。
1.1.2 生产数据
生产数据是应用系统中在线使用的数据,它可能是一个生产系统的生产环境数据库中的数据,比如在一个P2P借贷平台的系统中,用户进行注册、充值、投资等行为产生的数据将被记录到生产环境数据库中,这些数据即为生产数据。
生产数据是动态的,会随着业务应用的变化而变化,比如用户账户余额数据,会随着用户投资的变化而变化。任何存在于生产环境中的数据,都在时刻准备发生改变,只不过有些生产数据的变化频率特别低而已,比如用户的年龄信息。
正常情况下,数据分析师并不直接接触生产数据,但需要注意的是,有些生产数据是从分析数据而来的。比如用户标签数据,它本身是从分析数据构建的,属于分析数据。但这些标签数据一旦用于应用系统,例如作为推荐系统的底层数据,即转化为生产数据,这种情况下,应用系统输出结果的质量将受到分析数据的直接影响。
1.1.3 原始数据
由于生产数据是动态的数据,而过去大量的分析工具和分析方法很难处理动态改变的数据(流处理已经改变了这种情况)。为了在不影响生产应用系统的情况下分析和处理这些数据,我们需要将这些数据从生产系统解耦。
从生产系统解耦的数据即是原始数据。数据解耦的过程一般包括数据脱敏(如屏蔽电话号码、去除住宅详细信息等)、信息筛选(抛弃不需要的字段)、批量导出(如在T日凌晨批量导出T-1日的交易明细数据)等。
原始数据可以以多种形式存在,例如存储在生产数据库备库中,或者以文本文件的格式存放在文件服务器中。无论以何种形式存在,原始数据都应该独立于生产环境和分析环境,这可以避免分析环境对生产环境的干扰。
存放这些原始数据的地方,我们称为数据缓冲区。在很多企业中,数据缓冲区和原始数据并未得到足够重视,它们大多为了前期的方便,省略了数据缓冲区和原始数据形态,就像图1-2所示的那样。
图1-2 省略数据缓冲区的数据直连
显然,数据直连的方式让生产环境直接暴露在分析环境之上,两者之间的ETL(Extract-Transform-Load)过程将对双方的性能造成影响。随着数据量的增加,这可能会带来数据管理和应用上的灾难。
本书极力推荐图1-1所示的方式,虽然它比图1-2要复杂,但在数据管理和可扩展性方面有非常大的优势。后面的第2.1节中会深入讨论该问题。
1.1.4 分析数据
分析数据是对原始数据进行属性筛选、标准统一之后,使用优化存储的方式存放于分析环境中的数据。从原始数据到分析数据的关键步骤在于ETL过程。
比如,原始数据中的一张表A可能包含100个字段,经过ETL之后,得到了一个包含45个字段的表B,其中的日期格式进行了统一,且滤除了一些特殊字符,并将表B存放于分析环境数据平台的关系数据库Oracle中。这样,原始数据中的表A完成了属性筛选和标准统一(日期格式),转换成了分析数据表B。
另一种需要标准统一的情景根源于原始数据本身的多样性。由于原始数据来源于不同的生产应用系统,其数据格式及字段含义均存在差异。例如,原始数据存放的格式可能有Windows文本、Linux文本、主机格式文本、数据库文件等多种形式;字段含义上的差异则更加多样,比如,由原始数据文件A中性别字段使用1表示男性、2表示女性,而原始数据文件B中性别字段使用M表示男性、F表示女性。通过标准统一,可以约定所有的分析数据统一使用1表示男性、2表示女性。数据统一可为数据分析和数据应用铺平道路。
经过ETL之后的分析数据,为了进一步提高存储效率和读取效率,需要使用技术手段进行存储优化,比如创建索引、进行分区、分表存储、使用大数据平台等。
通过对原始数据的提炼和优化,分析数据具有了信息集中、标准统一、分析效率高等特点,便于数据进一步的分析和应用。
分析数据需要依托数据平台而存在,数据平台的性能对其上的数据分析和应用有决定性影响。数据平台是分析环境的基础,在随后的“数据平台”章节中,我们将详细介绍。
1.2 数据平台
数据平台是存放分析数据的平台,也是支持大多数数据分析和数据挖掘应用的底层平台,它使用了统一的数据清洗与处理规则,因而可以保证从基础平台上输出的数据内容是一致的。
传统的数据平台基本等同于大家熟悉的“数据仓库”,但互联网浪潮让人们对数据采集、存储和应用提出了越来越高的要求,传统数据仓库平台独力难支,因此“现代化”的数据平台是多种数据库产品的融合。图1-3是一个精简化的现代数据平台架构图。
图1-3 数据平台架构示意图
现代的数据平台融合了传统数据仓库、大数据平台、MPP数据库、NoSQL数据库等多种数据产品,这些数据库产品之间互为补充,组成统一的数据平台。
从传统的关系型数据库开始,数据库产品逐渐细分,这些细分产品在特定场景中比传统的关系型数据库表现出了更好的性能。图1-4展示了一些主流的数据库产品,注意到有很多数据库产品是“跨界”产品,例如,Oracle同时属于关系型、分析型、操作型三类数据库。
图1-4 数据库产品图册
图1-4可为数据库选型提供参考。比如,如果希望寻找一款关系型数据库,既可以作为数据仓库底层数据库使用,也可以作为生产应用系统的数据库服务器(即操作型数据库)使用,那么从图1-4中可供选择的有Oracle、DB2、SQL Server等。
如果你希望从大量数据中发现隐含的关系网络,那么图1-4中的数据库是一个很好的选择,从图1-4的Graph小框中可以发现有Spark Graphx、InfiniteGraph、Neo4j等可供选择。
在一些大型企业的数据平台中,可能会出现Teradata、GreenPlum、Vertica的身影。这三种数据库属于MPP(Massive Parallel Processing)数据库。虽然它们属于关系型数据库,但它们采用了一种与传统关系型数据库不同的存储方式,即列存储方式(Oracle、DB2、SQL Server等使用行存储方式)。这种列存储方式在面对大规模数据时,能表现出更好的效率,比如创建列索引、并行处理、集群操作等。
MPP数据库的问题在于其高昂的价格,这对于很多小型企业来说是一个“致命伤害”,目前国内使用该类产品的企业一般为大型企业。第1.2.4节会对MPP数据库做进一步介绍。
NoSQL数据库是伴随着互联网应用中崛起的新星。通过简化数据存取模式(相对RDBMS而言),减少了数据库管理系统(DBMS)的附加开销,专注于读/写效率的提升,非常适合对读取速度要求较高,且对数据不一致性有容忍的应用环境中。
归为NewSQL数据库之列的SQL Azure和MySQL Cluster是传统关系型数据库的云计算版本。目前该类数据库在国内企业的应用中尚不常见,但随着云计算的普及,预计国内一些大型企业有可能选用NewSQL数据库。
下面,我们对图1-3中组成数据平台的各主要模块分别进行介绍。
1.2.1 数据仓库平台
传统数据仓库平台,大多是基于关系型数据库搭建的。在Windows服务器平台上,一般选用SQL Server、Oracle、DB2、MySQL数据库中的一种或者两种混合搭建。
在Linux服务器平台上,可选择的数据库有DB2、Oracle、MySQL(虽然SQL Server正在准备发布Linux版本)。数据仓库平台的建模过程已经有了完善的理论基础,并且目前市面上也有很多相关书籍,限于篇幅,本书不做详细介绍。数据仓库工程师可以参阅以下书籍。
❑《数据仓库》(《Building The Data Warehouse》),机械工业出版社。
❑《数据库系统:数据库与数据仓库导论》(《Database Systems: Introduction to Databases and Data Warehouses》),机械工业出版社。
数据入口和出口
数据仓库本质上是解决大批量数据的入口和出口问题(简单来说,即数据写入数据仓库和从数据仓库中读取出来),并为大数据量的分析和应用提供基础支持。通常,在数据仓库设计时,虽然我们会尽其所能满足各种经典范式理论,但却忽略了一个简单且致命的问题:数据应该如何高效地进出数据仓库平台,并且对其他应用尽量透明呢?
很多数据仓库平台在运行初期看起来非常完美,但在持续运行一段时间后,各种问题逐渐显现出来,如数据库死锁、作业运行缓慢、ETL过程卡死等。这种现象总是伴随着数据量的增长、数据仓库数据表的增加以及数据仓库访问用户的增加而出现,这是典型的数据入口和出口问题顽疾。
一个能够长期稳定提供“顺滑”数据服务的数据仓库才是一个“好”的数据仓库,而不仅仅是看这个数据仓库在设计时采用了什么数据库产品、满足了多少范式理论。
根据笔者的经验,“好”的数据仓库的入口和出口至少要关注两条规则,即数据的更新规则和存储规则。更新规则会影响数据的入口效率和出口效率,存储规则主要影响数据的出口效率。
(1)数据的更新规则
实际场景中,一个数据库中的数据表总是面临数据加载(Load)的问题。比如一张交易明细表,每天凌晨需要将昨日的交易明细数据插入进去;而一张账户信息表,则需要将每天新增的账户信息插入及将状态改变的账户信息更新。
归纳起来,数据的更新规则分为两种:增量更新和全量更新。上面的交易明细表即是增量更新方式,每天将新增的数据插入;而账户信息表则可以采用全量更新的方式,每天将最新的账户信息表从数据源重新加载进来,并覆盖原有数据。图1-5展示了这两种更新方式。
图1-5 增量更新与全量更新
需要明确的是,在数据仓库中进行的数据更新一般是大数据量的数据更新,这与生产应用系统中的单条数据更新在处理方式上有很大的不同。在数据仓库场景中你将会面临百万(条)或者千万(条)级别的数据量,如果使用SQL中的Insert(或update)语句,则Insert语句带来的事务管理、日志记录等会严重降低数据库性能,并且整个插入过程的耗时巨大,导致数据表长时间不可用。
通常在面临大批量数据更新时,正确的做法是使用批量导入命令进行批量导入操作,并尽可能避免Update操作。我们将在第2章详细介绍批量导入命令。
但是使用批量导入命令面临的主要问题是“部分提交”的问题,因为数据库管理系统(DBMS)通常不将批量导入命令置于DBMS的事务控制范畴之中,这意味着整个批量导入过程并不验证数据库范式,也不记录操作日志,因此,如果批量导入中途出现异常,那么之前导入的数据将无法回滚。
比如,一个信用卡中心每天的交易明细数据有900万条,在往交易明细表中批量导入该批数据的时候,由于网络问题导致数据库连接中断,此时500万条记录已经导入交易明细表中且无法回滚,这就出现了“部分提交”问题。
这种情况是一个让人郁闷的问题,因为必须将导入的500万条数据删除(delete),然后重新运行批量导入命令。而从一个庞大的数据表中删除500万条数据简直是一种灾难,相信经历过的人肯定不想再来一次。
当全量更新时,除了“部分提交”的问题,还可能面临“数据断档”的问题。因为在全量更新时,首先需要将原表中的数据清空,之后再将新数据批量导入至清空后的表中。从数据清空到新数据全部导入,这个过程即为“数据断档”,因为在这个过程中数据表是无法对外提供服务的。
“部分提交”和“数据断档”是数据仓库运行过程中经常遇到的问题。一种可行的方案是增加中间表,即数据先完整导入中间表,然后再从中间表插入目标表,如图1-6所示。
图1-6 通过中间表进行批量加载
在图1-6中,原来交易明细表的增量更新拆分为以下两步。
1)清空“交易明细中间表”,并将T-1日交易数据全量更新至“交易明细中间表”中。
2)将“交易明细中间表”中的数据全部insert into到“交易明细表”中。
上述两个步骤中,假设步骤1)出现问题,则清空“交易明细中间表”后重新执行步骤1);而如果步骤2)出现问题,则数据库通过自动回滚会将已经Insert的数据回滚掉,不会出现数据不完整的情况。
在图1-6中,对于全量更新的账户表,拆分为以下三步完成。
1)清空“账户中间表”,并将T-1日账户数据全量更新至“账户中间表”。
2)Drop掉原账户表(T-2日账户数据)。
3)将“账户中间表”重命名为“账户表”,实现数据切换。
上述三个步骤,规避了“部分提交”的问题,同时在数据加载至中间表的过程中,原“账户信息表(T-2日数据)”仍可以正常提供数据服务(只不过数据并不是那么“新鲜”),因此解决了数据空档期的问题。
通过中间表,上述的增量更新、全量更新出现问题后,可以简单地通过重新执行解决,因此可以方便地通过程序自动完成上述过程。这种自动化过程规避了人工操作风险,降低了数据仓库维护成本,使数据仓库更加“顺滑”。第3章将详细介绍如何采用这种理念实现数据的增量更新和全量更新。
(2)数据的存储规则
数据仓库在运行初期一般都表现良好,这是因为运行初期的数据量、用户数、作业数量等均比较少,数据服务器本身的硬件性能可足以支撑这些业务。但是,随着数据量、用户数、作业数量的逐步增长,数据仓库的运行效率会逐渐降低。需要运维人员通过技术手段进行持续优化(如分区、创建索引、优化作业脚本等)。除此之外,数据的存储规则也是需要重点对待的事情。
对于一个每日增量更新的表,如上述交易明细表,其每天新增的数据量是非常惊人的,如某商业银行信用卡每天交易数据达860万条,这些数据每天导入到交易明细表中,每年的交易数据可以达到3TB左右,传统关系型数据库这种大表的检索效率是非常低的,更不用说在这种大表上进行更新、删除等操作了。
经过分析发现,数据仓库用户使用的交易明细数据90%集中在近三个月,而60%的作业仅使用近一个月的交易数据。因此,可以将交易明细数据分表存储,即一个月的交易数据存放在一个表中,并以月份为表名后缀,这样每个月的交易明细数据仅为250GB,通过适当的索引优化,传统关系型数据库在数TB级别仍然可以支持90%的业务场景。
图1-7中,原交易明细数据表按月拆分成小表,并使用yyyyMM格式日期作为表名后缀。拆分成小表后,如果是访问最近一个月的交易明细数据,则直接访问表trx_dtl_yyyyMM即可;而如果想访问最近三个月的交易数据,则通过视图v_trx_dtl_r3m访问即可。这样,在存储空间几乎不变的情况下,大幅度提高读取效率,从而使整个数据仓库的出口变得“顺滑”;另外,按月存储的交易明细表由于体积小,能更方便进行优化管理(如创建索引、迁移数据等),间接提高了数据仓库的入口效率。
图1-7 分表存储
拆分成小表后,由于表的命名规则固定(原表名+月份后缀),因此整个拆分表以及组合成视图的过程均可以通过脚本作业自动完成,提高了管理效率且节省了人力成本。
总体来说,在面对增量更新的“大表”时,数据的存储规则是“大表拆小表,小表组视图”,基本依据在于小表的访问效率高于大表的。
而对于全量更新的“大表”,也可以借鉴“大表拆小表”的方式。比如对于全量的账户信息表,可以根据账户状态将表分为多张小表,也可以根据账户号按一定的规则进行切分得到小表,总之需要根据实际情况,将大表切分成合适的小表,以提高访问效率。
尽管有诸多的优化方法,传统数据仓库在面临大数据量的时候,仍然无法规避存储空间和计算效率的问题,这要求我们在传统数据仓库解决方案之外寻求突破。
1.2.2 大数据平台
大数据的发展异常迅猛,各种开源或商用平台层出不穷,在选择大数据产品的时候,应该本着务实的原则,从实际情况出发选择真正需要的功能,毕竟技术的核心价值是帮助我们解决问题,一味追求新潮技术并不可取。
大数据平台的基础是分布式:分布式存储和分布式计算,它们分别用于解决单机数据库面临的两大困境,即数据量的问题和计算效率的问题,如图1-8所示。单机数据库由于无法支持分布式,所以其存储容量和计算能力的瓶颈难以突破,而大数据平台通过分布式扩展轻易解决了这两个问题。
图1-8 大数据平台的基础:分布式
分布式与扩展性密不可分,当存储和计算能力不足时,显而易见的方案就是增加集群中的机器,在存储价格和CPU价格日益下降而人力成本日益上涨的今天,这比从优化传统数据库系统着手要简单高效,且成本更低。这也是为什么说“能通过增加机器解决的问题都不是问题”的原因。
目前实际应用中的大数据平台基本是以开源的Hadoop平台为核心,不同厂商在此基础上进行封装和扩展,形成自己的产品线。比较知名的商用大数据平台产品有:Cloudera的CDH,华为的FusionInsight。
图1-9为大数据平台的核心组件。其底层基础模块HDFS(Hadoop Distribute File System, Hadoop分布式文件系统)用于提供分布式存储能力;MapReduce分布式计算框架用于提供分布式计算能力。HDFS可支持多种组件,包括Hive数据仓库、数据挖掘、流处理等,其外围的Zookeeper负责集群之间的协作管理,使众多的机器可以成为统一的集群,Flume和Sqoop则是大数据平台数据的入口和出口,负责与其他系统的数据交互。
图1-9 大数据平台的核心组件
需要注意的是,Hadoop的核心模块提供的是离线、批量的计算,本身并不适合强实时环境,这也是为什么把Hadoop归为分析系统的原因。不过,很多实时计算的组件经常与Hadoop结合使用,如Spark、Storm、Mahout等,这使得扩展后的Hadoop平台具备了一定的实时处理能力。
下面让我们快速认识Hadoop的核心组件。
1. HDFS
HDFS(Hadoop Distributed File System, Hadoop分布式文件系统)是Hadoop平台的文件基础,就如Windows环境的NTFS (New Technology File System)、Linux环境的ext文件系统(Extended File System)一样。
你可能并不需要关心HDFS的技术细节,但需要了解HDFS的主要设计理念是针对超大文件存储(几百MB~PB级别),一次写入、多次读取。它不适合用在低延迟的场景,也不适合存储大量小文件,这是因为HDFS文件的元数据(文件基本信息,如文件名称、路径、存放的DataNode节点信息等)是存放在NameNode的内存中的,大量的小文件会消耗NameNode的内存,而且会影响MapReduce对文件的处理效率。
建立在HDFS之上的Hive数据仓库也是针对大数据量的数据分析工具的,在数据量未达到一定规模时,Hive并不能体现出效率优势(在小数据量时,Hive的效率远低于传统关系型数据库的)。这里有一个经验值,当一个表中的数据达到数百GB的时候,使用关系型数据库进行读/写、Join、Sum、Group By等操作时会耗费大量时间,运行一段SQL脚本可能需要数个小时,这时Hive将体现出绝对的优势。
可以使用Hadoop Shell命令进行HDFS文件的交互式操作。Hadoop Shell命令与Linux的Shell命令非常相似,多数情况下只需要在Linux Shell命令前加上“hadoop fs”即可。常用的命令列举如表1-1所示。
表1-1 Hadoop Shell常用命令
表1-1中的这些命令可以方便我们操作HDFS中的文件,第3章将使用Java调用Hadoop Shell命令的方式实现多线程批量导入数据至Hive数据仓库。详细的Hadoop Shell命令可访问官方网站:http://hadoop.apache.org/docs/r1.0.4/cn/hdfs shell.html。
2. MapRecude
MapReduce是Hadoop平台的分布式计算框架,采用“分而治之”的思想,把对大规模数据集的操作分发给多个节点共同完成,然后通过整合各个节点的中间结果,得到最终结果。
MapReduce是一个简单易用的编程模型,这个模型包括两个部分:Map过程和Reduce过程,由Map和Reduce两个函数分别实现。
Map函数接受一个键-值对,经过处理产生一组中间键-值对。MapReduce框架会将Map函数产生的键-值对里键相同的值传递给一个Reduce函数。
Reduce函数接受一个键及相关的一组值,将这组值进行合并产生一组规模更小的值(通常只有一个或没有值)。
深入了解MapReduce的实现方式,对理解大数据平台的设计理念很有帮助,虽然类SQL语言Hive已经代办了MapReduce的大部分工作,但是本书建议读者能够理解MapReduce的原理和简单实现,这样对于深入理解大数据大有帮助。
关于MapReduce的更多内容可以参考《Hadoop权威指南(第3版)》(《Hadoop: The Definitive Guide,3rd Edition》),清华大学出版社。
3. Hbase
Hbase是基于HDFS的列式存储分布式数据库,属于NoSQL中的Big Tables范畴(见图1-4)。Hbase号称“能够提供高可靠性、高性能、列存储、可伸缩、实时读/写的数据库系统”,因此在一些时效要求相对较高的场景,会出现Hbase应用的身影。
但是需要注意的是,Hbase是基于HDFS文件存储的,而HDFS并不是针对低延时的场景设计的,因此Hbase本身的实时性能并不高,所以它比较适用于“异步的、准实时、高维度”的场景中。在后面第三部分中介绍的“实时数据营销平台”即使用了Hbase作为数据存储。
如果你已经对Hbase比较了解,则可以跳过本节下面的内容。
(1)Hbase的概念视图与物理视图
通过概念视图和物理视图,可以帮助我们快速理解Hbase的列存储模式。首先看一下Hbase的概念视图,如图1-10所示。
图1-10 Hbase的概念视图
从图1-10中可以看到Hbase的几个关键概念,即Row Key、Time Stamp和Column Family。
Row Key是Hbase的标识行字段,相同的Row Key在数据逻辑上属于同一行,图1-10中的数据均属于Row Key=“Charles”这一行。
Time Stamp是Hbase的时间戳,在写入数据时自动记录。因此Hbase可以自动记录列的历史版本,在需要保存历史变动记录的场景里,这个特征非常有用。图1-10中的finance:balance列即有三个版本T5、T3、T2,在读取数据时,Hbase默认读取最新版本。
Column Family是Hbase的列族,Hbase按照列族组织物理存储。每个列族可以随意增加列,因此Hbase的列一般表示为“列族名称:列名称”方式。图1-10中列族finance含有一个列html,写作finance:balance;列族status含有两个列,分别是weight和height。
在图1-10的概念视图里,尽管表可以看成是一个稀疏的行的集合,但在物理上,它是按列族分列存储的。图1-11是Hbase的物理视图。
图1-11 Hbase的物理视图
注意到图1-10中的空白格在物理上是不存储的,因为根本没有必要存储。因此,若要获取T8时间的finance:balance,结果就是空;同样,若获取T5时间的status:height,结果也是空。
(2)Hbase的读/写操作
Hbase在实际应用中一般用作准实时分布式数据库,在数据量较小的时候表现并不突出,但在数据量巨大时的点写入和点查询性能均高于关系型数据库的。
Hbase的另一个优势在于,可以几乎无限制地进行列的扩展,这非常适用于替代传统关系数据库的“大宽表”,因此用于客户标签体系(客户会拥有成千上万个的标签,并且是一个非常稀疏的数据表)的存储表是非常合适的,在后面第三部分的介绍中,将会使用Hbase作为用户标签系统的数据表。
Hbase表可以通过Hbase shell进行交互读/写。代码清单1-1的Hbase Shell脚本创建了一个Hbase表,并向其中插入了数据,然后读取了相关信息。
更多关于Hbase Shell命令的使用方法可参考Hbase官网。
代码清单 1-1
#创建表clt_tag,两个列族,即base_info和trx_info create 'clt_tag', 'base_info', 'trx_info' #往表clt_tag中插入数据,rowkey=10001,列族base_info增加一个列name,值为queziyang put 'clt_tag', '10001', 'base_info:name', 'queziyang' #往表clt_tag中插入数据,rowkey=10001,列族base_info增加一个列age,值为31 put 'clt_tag', '10001', 'base_info:age', '31' #往表clt_tag中插入数据,rowkey=10001,列族trx_info增加一个列total_amt,值为1500.3 put 'clt_tag', '10001', 'trx_info:total_amt', '1500.3' #读取clt_tag中rowkey=10001、列base_info:name的数据 get 'clt_tag', '10001', 'base_info:name'
(3)Hbase的批量读/写
Hbase适用于点读/写场景,在进行大批量数据操作的时候会面临一些问题。同时,存在这样一种情况:Hbase用于点查询数据库,但是数据本身需要定期更新,例如每天晚上需要将数千万条数据更新至Hbase表中,以便第二天进行查询。在后面第三部分介绍的实时客户标签系统中,就需要每天将更新后的用户标签批量导入至Hbase表中,因此Hbase面临批量更新的问题。
由于Hbase的更新等同于插入,因此批量更新方式即批量导入的方式。比较高效的批量更新操作,一般通过MapReduce程序直接生成Hbase存储文件HFile,然后将生成的HFile加载到Hbase表中。具体参阅第2章批量导入Hbase的章节。
关于Hbase的更详细内容,可以参考《HBase权威指南》(《HBase: The Definitive Guide》),人民邮电出版社。
4. Hive
Hive是Hadoop平台的数据仓库工具,它将HDFS文件直接映射为数据表,并提供类SQL(Hive SQL, HQL)语句进行数据表操作。HQL在执行时转化为MapReduce作业,在实际的数据批量操作场景中,Hive可以完成绝大部分本来需要MapReduce程序完成的任务,因此Hive的出现降低了MapReduce的使用成本,仅仅通过撰写简单的HQL语句就可以享受到MapReduce的强大功能,如图1-12所示。Hive是使用Hadoop大数据平台数据仓库的必备技能。
图1-12 HQL转为MapReduce作业
Hive作为大数据平台数据仓库工具,同传统数据仓库平台一样需要有统一的设计原则。在设计大数据平台数据仓库系统时,可以借鉴传统数据仓库的设计理念,把Hive表看成是关系型数据库的表。
同传统数据仓库一样,Hive数据仓库中表的数据加载同样分为增量更新和全量更新的情况,但由于Hive本身的分布式特点,其数据表的存放规则与传统数据仓库的有所不同。
(1)Hive分区表与增量更新
Hive数据仓库与传统关系型数据仓库一样,也需要解决数据的出口与入口问题,不过由于Hive数据本身基于HDFS分布式存储,因此我们更关注Hive数据仓库制定数据的更新规则。回忆关系型数据仓库的更新规则:增量更新与全量更新,这两条规则也可以用于Hive数据仓库。
增量更新可以使用Hive的分区表来“完美”解决。Hive的分区表类似于关系数据库的表分区,即根据分区键将数据分散在不同的分区(partition),通过并行读取提高效率。Hive的分区表可以方便指定每个分区的HDFS路径,因此可以通过程序自动完成,代码清单1-2给出的脚本作为示例创建了一个分区表,并指定了load_day作为分区键。
代码清单 1-2
create table adobe_log_app( id int, name string, age int, tel string ) partitioned by(load_day string) row format delimited fields terminated by '\t' stored as textfile; alter table adobe_log_app add partition (load_day='20150927') location '/hive/data/adobe_log_app/20150927';
“alter”命令往表adobe_log_app中增加了一个分区,并通过location指定了该分区的HDFS路径。显然,通过程序只需要将表名、分区键值作为参数传入,即可以实现增量数据的自动加载,其过程如图1-13所示。
图1-13 Hive增量更新过程
第3章将依据图1-13所示的流程实现Hive表的增量更新。
(2)Hive外部表与全量更新
Hive的全量更新可以使用Hive外部表(external table)实现。Hive外部表在创建表的时候会同时指定数据文件路径,该文件路径一般处于Hive默认数据文件路径之外,这也是Hive外部表名称的由来。
Hive外部表通过在创建表的脚本中指定external关键字和location关键字(指定数据文件的存放路径)实现,代码清单1-3是创建外部表的示例脚本。
代码清单 1-3
create external table clt_act_info( act_id bigint, act_typ string, limit int, ... ) row format delimited fields terminated by '\t' stored as textfile location '/hive/data/clt_act_info';
Hive外部表进行全量更新时,分为三个步骤:①删除原数据所在的HDFS目录(location所指目录); ②创建新的HDFS目录(目录的路径与原目录保持一致); ③将新数据文件复制到新的HDFS目录。图1-14展示了其整个过程。
图1-14 利用Hive外部表实现数据的全量更新
5. Spark
Spark是基于内存的类MapReduce通用并行框架,它拥有MapReduce所具有的优点,并且抛弃了MapReduce的文件中转功能,不需要读/写HDFS,因此Spark能更好地适用于数据挖掘与机器学习等需要多次迭代的计算场景。
Spark包含四大主要组件,即Spark SQL、Spark Streaming、MLlib(Machine Learning)、GraphX,如图1-15所示。
图1-15 Spark的主要组件
Spark SQL同Hive SQL一样,都是类SQL语言,Spark SQL是基于Spark进行分布式计算的,而Hive SQL是基于MapReduce进行分布式计算的。由于Spark计算框架是内存实现,因此理论上Spark SQL的速度要比Hive SQL的快,当然其对内存的要求也比Hive SQL的高。
Spark Streaming是Spark的流数据处理组件,它将流式计算分解成一系列短小的批处理作业,也就是把输入数据按照batch size(如1秒)分成一段一段的数据,对每一小段的数据进行处理,整个过程可以看成是一连串非常小的批处理过程。Streaming流处理一般使用消息队列(如Kafka)作为输入端,实时收取消息队列中订阅的消息并进行处理。Spark Streaming能够与Kafka完美对接,有兴趣的读者可以参阅官网文档。
MLlib是Spark的机器学习组件,它提供协同过滤、回归、聚类、分类、人工神经网络等主流机器学习算法,这些算法都提供了API接口,可以方便通过Java或Python进行编程调用,因此非常容易结合大数据平台使用。
GraphX是Spark的图计算和图挖掘引擎,图计算在目前的关系网络分析中受到越来越多的重视。关系网络涉及多个主体之间的复杂联系,如果使用关系型数据库或者主流的数据分析工具(如SAS、R、Python)实现对整个关系网络的描述,已经是一件非常困难的事情,更不用说进一步的分析和计算了。Graphx融合了图并行以及数据并行的优势,虽然与单纯的计算机段的性能相比不如GraphLab等的计算框架,但是如果从整个图处理流水线的视角(图构建、图合并以及最终结果的查询)看,那么性能就具有明显的优势。另外,由于Spark还提供了流处理、机器学习等组件,这为企业在构建大数据平台时提供了一站式服务,预计Spark GraphX的应用将在大数据平台中占据主导。后面的第三部分将使用GraphX实现一个关系网络案例。
1.2.3 MPP数据库
MPP数据库即大规模并行处理数据库,它是一种分布式关系型数据库。MPP数据库继承了传统关系型数据库的用户友好的交互界面,同时提供了大数据平台具有的分布式存储和计算的功能。
图1-4中的GreenPlum和Vertica即属于MPP数据库。Greenplum号称支持50PB(1PB=1000TB)级海量数据的存储功能,目前国内的大众点评、阿里巴巴、华泰保险、中国远洋等均使用了该产品。
Vertica在FaceBook上的成功应用使得最近几年在国内市场有了快速增长。Vertica使用标准的SQL语句,因此对于熟悉传统关系型数据库的用户来说,学习成本非常低。另外Vertica的架构非常适合云计算,包括虚拟化、分布式多节点运行等,并且可以和Hadoop/MapReduce集成,因此非常利于市场推广,目前国内的招商银行已经引入该产品。
MPP数据库在架构上可以分为Master-Slave架构(这其实也是Hadoop的架构)和Share-Nothing架构(无共享节点架构)两种,如图1-16所示。GreenPlum属于Master-Slave架构,但其随后的产品可能转换为无共享节点架构,Vertica属于无共享节点架构。
图1-16 MPP的两种架构
在Master-Slave架构中,Master有可能成为系统瓶颈,但是Master节点本身并不负责计算,仅用于Slave节点之间的控制以及交互数据的转发,因此在实际使用中,Master成为瓶颈的场景并不常见。
在无共享节点架构中,各个处理单元都有自己私有的CPU、内存、硬盘等资源,不存在共享资源,各节点之间通过协议进行通信,各节点独自处理自己的数据,处理后的结果可能向上层汇总或在节点间流转。
MPP数据库定位于高端数据分析市场,因此价格比较昂贵,且对硬件有特殊要求(例如Teradata采用软硬件一体销售策略)。另外,虽然MPP数据库一般都支持使用标准SQL语句,但很多传统关系型数据库的功能并不完全支持(例如Vertica本身没有存储过程)。因此,在构建数据平台时需要根据公司的实际情况设计合理的产品选型方案—让需求选择技术,而不是让技术选择需求。
1.2.4 NoSQL数据库
NoSQL即Not Only SQL,是对非关系型数据库的泛称(参考图1-4)。NoSQL数据库不遵循传统关系型数据库的ACID原则,并且抛弃了磁盘存储,转而走向了内存存储。NoSQL数据库大多应用于分布式应用系统中。
相对于传统关系型数据库的ACID理论,NoSQL理论基础主要基于CAP原则(也叫CAP定理,见图1-17)。CAP定理中的C、A、P分别指Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性)。NoSQL理论对分布式系统中的三个特性进行了如下归纳。
图1-17 CAP定理
1)一致性(C)。一致性被称为原子对象,任何的读/写都应该看起来是“原子”的。写后面的读一定能读到前面写的内容,所有的读/写请求都好像被全局排序。
2)可用性(A)。对任何非失败节点都应该在有限时间内给出请求的回应(请求的可终止性)。
3)分区容错性(P)。允许节点之间丢失任意多的消息,当网络分区发生时,节点之间的消息可能会完全丢失。
CAP定理由Eric Brewer教授提出,并由Lynch等人于2002年证明了Brewer的猜想。CAP定理告诉我们,一个分布式系统不可能同时满足一致性、可用性和分区容错性这三个需求,最多只能同时满足两个。
根据CAP定理,可以根据不同的应用场景选取其中的两个作为设计方向。目前,NoSQL数据库作为实时应用系统数据库一般遵循AP原则。满足AP原则的NoSQL数据库一般采用Key-Value内存数据库,如Redis(参考图1-4)。
但CAP定理正在面临诸多质疑。CAP的概念定义比较模糊,而且CAP没有考虑不同的基础架构、不同的应用场景、不同的网络基础和用户需求,而C、A、P在这些不同场景中的含义可能完全不同,这种无差异化的定义直接导致了概念的不明确,同时也成为CAP被质疑的源头。
不过,CAP定理仍然有显著的指导意义,它至少告诉我们在设计分布式系统和选择NoSQL数据库产品时需要考虑的基本方向,它还提醒我们分布式系统与传统系统架构存在的差异性,你必须根据实际的应用慎重选择分布式框架。
1.3 应用系统
前面已经为数据定义了三种基本形态,并认识了各种数据平台。从图1-1中还可以看到,原始数据主要是由应用系统产生的。作为数据的源头,我们有必要从数据的角度重新认识应用系统。
从数据角度看,应用系统可以分为两类:业务驱动的应用系统和数据驱动的应用系统,如图1-18所示。
图1-18 应用系统的分类
业务驱动的应用系统侧重于业务逻辑的处理,数据是业务逻辑运行的直接结果,它不依赖于现有数据。例如,信用卡交易系统即是一个业务驱动的应用系统,持卡人每一次刷卡消费(触发业务逻辑),都会产生交易相关的各种数据,这些交易数据的产生是一个从无到有的过程。
数据驱动的应用系统,其主要特点在于业务逻辑需要作用于基础数据,才能产生新的数据。数据驱动的应用系统一般需要与数据模型、数据标签等结合使用,这些数据模型及数据标签均基于已有的历史数据构建而成。数据模型通常作为业务逻辑的一部分,为业务逻辑提供决策支持。典型的数据驱动的应用系统如个性化推荐系统、数据营销系统等。
概括来说,业务驱动的应用系统是数据从无到有的过程,数据驱动的应用系统是数据产生数据的过程。无论是业务驱动的应用系统还是数据驱动的应用系统,其产生的数据经过数据脱敏、数据解耦后才能成为原始数据。
数据要产生价值,归根结底要体现在应用系统中。国内有很多企业在数据离线应用中做得很好,包括数据的分析、数据模型等,但这些数据在系统化、自动化的过程中产生了严重的滞后,这显然是数据向价值转换路上的一个不足。后面第三部分的内容正是着眼于将数据的离线应用推广到在线应用,将离线创建的数据模型和推荐模型系统化、自动化,从而更好地实现数据价值。
1.4 本章小结
首先,本章介绍了数据的基本形态以及与之相关的各种数据平台,从数据分析和应用角度来看,数据的基本形态包括生产数据、原始数据和分析数据三种,它们分别对应于三种环境,即生产环境、数据缓冲区和分析环境。
其次,本章着重介绍了分析环境的数据平台,包括传统数据仓库平台和大数据平台。在数据仓库平台中介绍了数据的更新规则和存储规则,这是数据仓库平台解决数据入口和出口问题的重要方法。大数据平台主要介绍了大数据平台的基本组件,目的在于给读者一个整体概念。
然后,本章介绍了MPP数据库和NoSQL数据库的CAP定理。MPP数据库是一种定位于高端分析市场的数据库产品,一般应用于大型企业数据平台之中;NoSQL数据库的CAP定理虽然备受争议,但是了解该定理仍然可以为我们构建数据应用提供参考。
最后,本章简单介绍了应用系统的分类。数据由应用系统而来,最后仍然需要应用到系统中去,才能转化为价值。第2章的内容正是围绕让数据更好地产生价值这一主题展开的。