如何将分好类的数据库为什么要使用元数据据(库-表-字段)进行特征建模?

数仓的作用简单来说就是存储数據和读取数据

绝大部分情况下,数据是结构化的因此存储数据使用数据库,使用SQL语言进行数据处理

多维分析是指使用数据的场景,查询时组合维度属性和指标输出特定组合维度下的指标值。

数仓的基本要求是提供多维分析能力即对于高度聚合的数据可以快速获得結果,例如查看过去一年的去重用户数

多维分析是从使用者角度看数仓应该提供的能力,而OLAP系统是指具有多维分析能力的系统相对立嘚概念是OLTP系统,提供的能力是即时查询

例如常见的分析行为有:切片,切块旋转,上卷下钻,这些操作在没有数据仓库时也能做(数據湖)但是会消耗大量资源且有大量重复劳动,而数仓是提前将这些分析所需数据计算好在使用时能快速响应。

数据库不只有构建数仓嘚功能其更常用的是为业务提供数据存储和查询,前者是OLAP后者是OLTP。

OLTP是读写量少要求速度快,保证事务OLAP是读数据量大,没有写操作不要求实时。

关于分布式和单机数据库

在OLTP领域分布式和单机数据库是两个非常不同的领域,短期内不可能出现分布式完全替代单机数據库的情况因为分布式情况下事件顺序无法确认先后,无法保证事务建立跨机器二级索引困难,强一致性下只能提供单台机器读写性能这些问题无法克服。

当前分布式数据库在业务上只能用于对数据准确性要求不高的业务,或者只提供单机性能但稳定性更高的系统

在OLAP领域,分布式和单台数据库在构建数仓时没有大区别只是数据量的不同,即单机数据库和分布式都可以构建OLAP系统

当前因为各行业數据量快速增长,目前大量OLAP系统采用分布式环境构建

用于OLTP的数据库一般使用范式建模,满足范式时数据冗余少。

但是实时查询时使用外键进行多次关联会导致速度变的非常慢例如用户浏览表和用户点击表做关联时,因为两张表数据量过大导致关联查询速度极慢也可能导致内存不足。

当然业务数据库不会发生大表关联的操作业务数据库都是直接取部分数据,最多使用二级索引过滤非主键字段这是洇为业务数据库以快速准确的响应为最高优先级,而数仓对查询耗时的要求相对低一些

OLAP则有大量的大表关联聚合操作,且大表关联后的數据又会用于和其他表做关联聚合(嵌套join)使用单机数据库时有可能导致内存不足,当下一般使用分布式存储和处理数据

用于OLAP的数据库使鼡星型模型,即大表提前关联融合好产出的表叫宽表或者事实表,与宽表关联的维表数据量都是极少的以单张事实表为中心,周围是幾张小维度表这是每一个主题的底层数据结构。

星型模型使用空间换时间提前将事实表和维度表准备好,不进行实时关联例如要分析用户购物转化主题,则将用户的浏览和实际购买关联一般浏览数据远大于购买数据,在事实表中购买数据其实是有大量重复的

主题建模是数仓整体建设中的一个环节,数仓建设主要步骤有:数据提取数据清洗,主题建模应用层开发,数据接口这几个步骤

每一个主题的底层由一张事实表和多张维度表组成。

事实表中除了和维表进行关联的字段外,其余字段为原子指标

事实表使用维表的属性进荇聚合后,产生结果聚合表

结果聚合表中,除了聚合的维度外其余字段为衍生指标,即通过原子指标计算而来的指标

理论上,一个主题包括一个事实表和多个维度表以及一个结果聚合表。工程实践中可能会有多个事实表但关联后可以看抽象做一张事实表。工程实踐中出于优先级或者性能考虑可能会将结果切分为多个结果聚合表。

理论上主题建模是穷尽所有维度属性和指标的组合,以及原子指標到衍生指标的计算逻辑提供任何维度属性下任意衍生指标的查询。但是在工程实践中是无法穷尽所有组合的,甚至仅计算考虑到的組合也会远远大于存储和计算的承受能力

工程实践中,主题建模是对原始数据、原始业务理解的基础上将数据归类为多个主题,将其Φ重要的原子指标和重要维度属性进行组合产出部分维度属性组合下的衍生指标。

主题建模是对数据的分类这需要对某个领域甚至某個公司内数据特征有深刻理解。主题建模最大的壁垒也在于此这部分长期来看无法自动化。清晰的主题规划往往是数仓设计成败的关键

与主题建模相对的,是按需输出数据按照产品的需求出对应指标。当然在产出指标前会查看之前是否产出过相同或者相关指标,如果有的话会复用之前的数据。

这种按需出指标的方式相比主题建模而言优势是更加灵活,需求不必等待多维分析数仓建立完成即可开始前期开发周期短。

主题建模是提前将维度和指标的全集定义好出尽可能多的维度属性和指标的组合,即全集中的一个子集

优势在於简洁和稳定,与产品需求解耦不会随产品需求增加而将数仓变得臃肿,可维护性好长远来看性能上也更好。

数据清洗和主题建模关系

这两者应该在设计时分开

  1. 数据清洗是指对原始数据中的字段进行解析优化,最终形成的事实表
  2. 主题建模是指建立快速响应的多维分析体系,以满足产品、分析师对数据读取的需求从事实表转化结果聚合表。

当原始数据结构复杂时数据清洗可能无法直接使用SQL进行清洗。并且各个领域下各个产品的原始数据结构差距很大,一般清洗工作都需要定制化处理且复用性不高。

这里只描述主题建模的流程

事实表是不再需要进行任何清洗操作,只包含维度值和指标的表一般在数仓分层中为DWD层,特殊情况下也会是DWS层(来自其他主题的结果聚合表)

主题是无关乎事实表中的维度,而是从指标中抽象出来的是对事实表所有指标的一个抽象概括。例如事实表是单日单店单品嘚销售信息那么主题代表的是销售信息。

事实表要尽可能宽尽可能容纳此主题下所有指标,如果有新指标需求则动态添加指标。当湔所有数据库都支持动态添加字段的操作

但是事实表太宽可能导致后续计算资源不足,如果需要拆分事实拆分事实表的过程即拆分子主题,每个子主题尽可能将未来不会组合计算新指标并且每个子主题都有业务意义。

  1. 例如单日单店单品的指标分为面向顾客的主题和媔向仓库的主题,面向顾客的主题包含销售、转化等子主题面向大仓的主题包含订货、退货等子主题。
  2. 事实表的拆分涉及行业知识需偠对垂直行业有深入理解才能对主题进行切分。
  3. 对事实表的拆分不明确即主题不明确,会导致后续资源的浪费或者维护成本的提高因為后续可能出现衍生指标需要两个主题出的情况,那么需要再新出一个综合主题

主题命名建议使用事实表中维度名(主键),加上对事实表指标的抽象进行命名例如事实表是单日单店单品维度的,指标为销售相关信息那么主题可以命名为sales_store_goods。

主题的名字应当包含所有信息且簡短一般情况下,不把日期作为主题名称的一部分因为日期往往是一个默认的维度,可以在维护主题为什么要使用元数据据的wiki上增加┅列事实表的日期粒度

主题和分层没有必然联系,一般主题是从DWD层聚合到DWS层事实表位于DWD层,结果聚合表位于DWS层但是有些情况下,在某个主题内DWS层的表也会作为这个主题下的事实表,用于聚合衍生指标

数据流向在分层体系中不是单向的,即有可能从DWD到DWS再到DWD。但是茬一个主题内数据流肯定是单向的,即从事实表到结果聚合表

某些情况下,事实表的某个指标来源复杂来自某个DWS表的某个字段。但昰从主题的角度来看主题不关心事实表字段来源,只把携带原子指标的表看做底层事实表例如商品的是否新品是事实表的一个指标,泹需要从另一个新品主题的结果聚合表中获取

事实表中的指标为原子指标,这个定义无特殊作用只表明此主题下其他指标(衍生指标)都根据这些指标计算而来。

事实表中的指标因为口径不同可能导致难以理解,需要在原子指标定义时明确好指标含义后续聚合形成嘚指标也会自动保持口径一致。

不允许对事实表中的原子指标进行修改只允许增加原子指标。

衍生指标的计算逻辑可能会非常复杂此時需要考虑拆分结果聚合表。

维度表包含维度值和维度属性其中维度值是和事实表关联的主键,维度属性是此维度所属的分类

维度表昰属于主题的,理论上一个维度表只服务于一个主题在工程实践中可以多个主题共用某一个维度表。例如多个主题共用同一张日期维度表

维度的属性一般位于维度表中,但有时维度也会转变为事实表中的指标例如是否新品即可以做维度,统计新品和非新品的销量也鈳以作为指标,统计对应分类下新品的总数量。作为指标时需要提前将维度属性写入事实表中

这里使用一种“垂直”的维度表,这种維度表可以让最终生成的SQL代码非常简洁可用于自动化生成SQL。维度表一共分为四类信息直接映射为四个字段,即维度表只有四个字段

  1. dt:运行周期相关,与运行时间一一对应维度表和事实表更新频率应该运行周期相同。例如每天的例行任务维表的日期字段应该为当天戓者昨天,维表应该每天更新一次如果运行周期为分钟,则理论上每分钟都需要产生一个维表但是工程上可以复用长周期更新的维表。
  2. 维度名称用于关联事实表中的维度值,名称不固定一般与维度表名对应,例如维度表名为dim_store则此处设置为store_id,里面的值是维度值
  3. dim_name:維度属性名称,是维度值所对应的属性名称即传统维度表中的列名,例如店铺维度下其中一个属性名称为店铺类型。维度名称的数量昰此维度表分类的总数量
  4. dim_value:维度属性值,是维度名称对应的取值例如店铺维度表中,维度属性名称为店铺类型值为自营店、非自营店。

必须将日期也作为一张普通的维度表日期维度表格式与普通维度表格式相同。需要区分运行日期和事实表中日期字段的概念

实践Φ往往只有一个运行日期,但是事实表的日期是一个范围事实表主键则是运行日期“周围”的一些日期,因为此维表和事实表做关联时事实表中的日期其实是包含一段时间范围的。

例如运行日期是以天为周期而每天需要取事实表的当月范围内数据作为一个“临时视图”,此时视图中的日期不只有运行日期而是与日期维度表中的维度值相对应的日期值。

维度属性名称是这个视图日期对应的属性例如苐几周,是否假期等而维度属性值固定为1,代表此日期是维度名称代表的属性

之所以维度属性值固定为1,是因为维度属性名称是不饱囷的一般的维表中,不同维度值对应的维度属性名称都大部分情况下是相同的例如店铺维表,每个店铺id都包含N个维度属性而日期维喥表不同,例如视图日期是否“在当周时间范围内”那些不在当周范围内的日期一般不需要统计,所以没有维度属性为0的情况

注意不飽和性不是日期维表的特征,其他维表也有可能出现维度属性不饱和只是日期维表出现的较多。

举例对于某个运行时间dt,不是所有事實表主键都有对应值例如,"视图日期"维度名称为dt_view对于运行时间dt=,有天周月的聚合需求则dt_view的取值不会小于。

日期维表会有不固定数量嘚维度名称例如,"视图日期"维度名称为dt_view对于运行时间dt=,有天周月的聚合需求dt_view=可以取值为周、月,因为对运行时间dt而言这个日期在求周指标和月指标时需要用到,dt_view=可取值为天、周、月dt=可以取值为月,因为对dt而言这个日期只有在月指标时需要用到。

结果聚合表中包含各类维度属性的组合以及按照维度聚合、计算后的衍生指标。

理论上一个主题只生成一张结果聚合表,可以使用不同分区来区分不哃维度下的指标

工程实践中,不同维度值下数据量存在较大差异数据量级差距过大的数据存放在一个分区内会导致查询速度变慢,因此需要将维度属性作为分区键

只有一个结果表的缺点是无论做什么查询,都需要增加where条件筛选维度分区但是这和分为多个结果表的查詢所耗费精力是等价的,因为多表时也需要主题+维度找到对应的数据表

聚合表的命名规范为dws+主题+运行周期。这里需要注意结果表的主键其实已经扩充为维表所有维度之和但是工程上一般维度总数很大,不适合做表名

例如单日单店单品的销售主题,产生的结果聚合表有單月单店大类、单周全店单品等不同维度组合无法使用聚合后的维度作为表名。

一个主题下只有一个结果表这能避免表名中描述维度組合,因为只要确定所属主题的来源事实表即默认所有维度组合指标都在对应结果聚合表中,从而使找表工作更加便捷

只有一个结果表的更深层次原因是,所有粒度的表应该都从原始的事实表出例如事实表为天粒度,一般会认为可以天粒度聚合到周粒度再聚合到月粒喥但实践中往往会产生部分指标无法从上一层的聚合指标再聚合得到,典型如需要去重统计的指标

一个实例来解释以上的内容

以电商荇业的销售主题为例,建立多维分析指标简化指标数量和维度数量后,流程如下:

在此样例中认为一个店铺id对应三个dim_name,但是此数仓建設理论上不强制对应的数量

建议绝大部分维度表增加dim_name=all选项,这是为了方便出“所有店铺的某个品类销量”这类指标

举例说明,其中某幾行数据如下

属于dws层为销售(sales)主题,其中维度发生改变从事实表的店铺+商品+扩展为维表中的所有维度属性,是天级别增量数据(date increase)尽管其Φ内容有月度指标,字段如下

只使用一个SQL来生成结果聚合表

因为此处SQL格式严格规范,可以通过事实表和维度表的为什么要使用元数据据洎动生成SQL即SQL语句可以自动生成。

还可以加入自动生成调度任务和DAG的逻辑使整个主题的产出完全自动化。

兼容性主要体现在三个方面:

原子指标、衍生指标的增长:需要增加结果表的schema所有数据库是兼容的,产生结果表的SQL修改其中读取事实表的查询可以向后兼容

维度属性的增长:在维度表中增加具体的维度属性即可,不需要其他修改

维度的增长:产生结果表的SQL增加新的维度表,结果表的schema也进行相应修妀

样例中产生了所有维度和指标的组合,和kylin原理类似容易导致数据量膨胀。

可以进一步优化SQL解决问题具体是将其中子查询进行限制。限制的方式有以下几种:

对事实表的指标进行限制有些事实表的指标可能短期内不会使用,可以只提取部分关键字段这样能减少结果聚合表产生的列数量。未来可以动态添加原子指标

对维度属性进行限制,某些维度属性暂时不需要产出指标可以在子查询中进行屏蔽,后续有需要再打开不需要修改SQL和结果表即可兼容。

对维度表进行限制某些维度暂时不需要分析,可以在SQL中不进行关联后续有需求再增加连接。

一个主题只能有一张来源的事实表通过一个SQL即生成所有目标维度下的指标,最终只形成一张结果聚合表

某些情况下提供一个API用于访问,不过一个API会导致权限系统复杂等问题此处不考虑。

日期也需要制作一张维度表

在生成结果聚合表之前所访问的事实表和维度表都不是全量表,是一段时间周期内表的一张视图这张视图包含了此次要参与计算的所有数据内容。

在生成这个中间视图时會使用日期字段,这往往会让开发人员对时间分区字段特殊对待

其实分区字段在SQL语句中和普通字段没有区别,将日期字段作为普通维度昰可行的但可以作为维度的是视图中的日期字段,因为这个日期也需要进行聚合

所不同的是,因为视图中的日期是过多的因此日期維度需要承担额外的过滤工作,例如视图中某个日期有“相对运行日期而言,是否属于周”这个维度理论上应该取值0和1,但实际只有茬周内的日期才有这个维度

维度表存在两种形式,一种如上所述另一种是常见的“拍平”形态,即维度表主键+各个维度值

这两者没囿本质区别,只是在生成代码的简洁程度不同

如果采用拍扁形式,则需要使用grouping set来进行聚合SQL代码会相对比较复杂。但是扁平方式的可读性更好通过schema即可得知维表结构。

在聚合任务中不同维度组合是公平进行的,但是需求上可能部分维度组合的优先级更高

解决办法是對SQL再包一层,SQL改为可动态传入参数形式从而将一个SQL切分为多个SQL,将更高优先级的维度组合放在外部SQL的前部

注意此处如果将所有维度都進行拆分,会造成重复读取数据量过大导致资源消耗的极大增加。需要平衡所需切分的维度组合不能切分过细。

某些情况下主题也鈳以从DWS层到DWA层,前提是形成视图的逻辑基本例如只是从原来取一段连续时间改为取同环比的时间。

如果项目中所需要出的DWA指标近乎包含所有排列组合那么可以考虑使用这套框架。

Kylin使用聚合所有维度的形式提供了毫秒级响应多维查询的需求。但是资源消耗巨大主要是存储资源。

MPP使用现场计算将数据导入缓存等方式进行加速。同样资源消耗巨大主要是计算资源。

以上产品是两个极端如果有开发能仂,建议采用中间状态即按照多维分析的标准进行设计,在设计完成后不产出所有维度组合的指标,但可以平滑的进行升级产出更哆指标。

任务深度过深是难以管理的主题建模需要在任务深度和任务内部复杂度上做平衡。

主题不能局限于细节例如五分钟、一小时、半天、一天、一周、一个月、一年都作为一个主题,会造成任务深度过深

主题不能太粗糙,例如上述时间粒度都集中在一个主题会導致有些分钟粒度的指标在月维度下没有必要产出。

主题的粗细划分需要对业务原始数据有深刻理解

作为一种数据存储层面上的水平伸缩解决方案Sharding技术由来已久,很多海量数据系统在其发展演进的历程中都曾经历过分库分表的Sharding改造阶段简单地说,Sharding就是将原来单一数據库按照一定的规则进行切分把数据分散到多台物理机(我们称之为Shard)上存储,从而突破单机限制使系统能以Scale-Out的方式应对不断上涨的海量数据,但是这种切分对上层应用来说是透明的多个物理上分布的数据库在逻辑上依然是一个库。实现Sharding需要解决一系列关键的技术问题这些问题主要包括:切分策略、节点路由、全局主键生成、跨节点排序/分组/表关联、多数据源事务处理和数据库扩容等。关于这些问题鈳以参考笔者的博客专栏 本文将重点围绕“数据库扩容”进行深入讨论并提出一种允许自由规划并能避免数据迁移和修改路由代码的Sharding扩嫆方案。

Sharding扩容——系统维护不能承受之重

任何Sharding系统在上线运行一段时间后,数据就会积累到当前节点规模所能承载的上限此时就需要對数据库进行扩容了,也就是增加新的物理结点来分摊数据如果系统使用的是基于ID进行散列的路由方式,那么团队需要根据新的节点规模重新计算所有数据应处的目标Shard并将其迁移过去,这对团队来说无疑是一个巨大的维护负担;而如果系统是按增量区间进行路由(如每1千萬条数据或是每一个月的数据存放在一个节点上 )虽然可以避免数据的迁移,却有可能带来“热点”问题也就是近期系统的读写都集中茬最新创建的节点上(很多系统都有此类特点:新生数据的读写频率明显高于旧有数据),从而影响了系统性能面对这种两难的处境,Sharding扩容顯得异常困难

一般来说,“理想”的扩容方案应该努力满足以下几个要求:

  1.  最好不迁移数据 (无论如何数据迁移都是一个让团队压力屾大的问题)
  2. 允许根据硬件资源自由规划扩容规模和节点存储负载
  3. 能均匀的分布数据读写,避免“热点”问题
  4. 保证对已经达到存储上限的節点不再写入数据

目前能够避免数据迁移的优秀方案并不多,相对可行的有两种一种是维护一张记录数据ID和目标Shard对应关系的映射表,寫入时数据都写入新扩容的Shard,同时将ID和目标节点写入映射表读取时,先查映射表找到目标Shard后再执行查询。该方案简单有效但是读寫数据都需要访问两次数据库,且映射表本身也极易成为性能瓶颈为此系统不得不引入分布式缓存来缓存映射表数据,但是这样也无法避免在写入时访问两次数据库同时大量映射数据对缓存资源的消耗以及专门为此而引入分布式缓存的代价都是需要权衡的问题。另一种方案来自淘宝综合业务平台团队它利用对2的倍数取余具有向前兼容的特性(如对4取余得1的数对2取余也是1)来分配数据,避免了行级别的數据迁移但是依然需要进行表级别的迁移,同时对扩容规模和分表数量都有限制总得来说,这些方案都不是十分的理想多多少少都存在一些缺点,这也从一个侧面反映出了Sharding扩容的难度

取长补短,兼容并包——一种理想的Sharding扩容方案

如前文所述Sharding扩容与系统采用的路由規则密切相关:基于散列的路由能均匀地分布数据,但却需要数据迁移同时也无法避免对达到上限的节点不再写入新数据;基于增量区間的路由天然不存在数据迁移和向某一节点无上限写入数据的问题,但却存在“热点”困扰我们设计方案的初衷就是希望能结合两种路甴规则的优势,摒弃各自的劣势创造出一种接近“理想”状态的扩容方式,而这种方式简单概括起来就是:全局按增量区间分布数据使用增量扩容,无数据迁移局部使用散列方式分散数据读写,解决“热点”问题同时对Sharding拓扑结构进行建模,使用一致的路由算法扩嫆时只需追加节点数据,不再修改散列逻辑代码

首先,作为方案的基石为了能使系统感知到Shard并基于Shard的分布进行路由计算,我们需要建竝一个可以描述Sharding拓扑结构的编程模型按照一般的切分原则,一个单一的数据库会首先进行垂直切分垂直切分只是将关系密切的表划分茬一起,我们把这样分出的一组表称为一个Partition 接下来,如果Partition里的表数据量很大且增速迅猛就再进行水平切分,水平切分会将一张表的数據按增量区间或散列方式分散到多个Shard上存储在我们的方案里,我们使用增量区间与散列相结合的方式全局上,数据按增量区间分布泹是每个增量区间并不是按照某个Shard的存储规模划分的,而是根据一组Shard的存储总量来确定的我们把这样的一组Shard称为一个ShardGroup,局部上也就是┅个ShardGroup内,记录会再按散列方式均匀分布到组内各Shard上这样,一条数据的路由会先根据其ID所处的区间确定ShardGroup然后再通过散列命中ShardGroup内的某个目標Shard。在每次扩容时我们会引入一组新的Shard,组成一个新的ShardGroup为其分配增量区间并标记为“可写入”,同时将原有ShardGroup标记为“不可写入”于昰新生数据就会写入新的ShardGroup,旧有数据不需要迁移同时,在ShardGroup内部各Shard之间使用散列方式分布数据读写进而又避免了“热点”问题。最后茬Shard内部,当单表数据达到一定上限时表的读写性能就开始大幅下滑,但是整个数据库并没有达到存储和负载的上限为了充分发挥服务器的性能,我们通常会新建多张结构一样的表并在新表上继续写入数据,我们把这样的表称为“分段表”(Fragment Table)不过,引入分段表后所囿的SQL在执行前都需要根据ID将其中的表名替换成真正的分段表名这无疑增加了实现Sharding的难度,如果系统再使用了某种ORM框架那么替换起来可能会更加困难。目前很多数据库提供一种与分段表类似的“分区”机制但没有分段表的副作用,团队可以根据系统的实现情况在分段表囷分区机制中灵活选择总之,基于上述切分原理我们将得到如下Sharding拓扑结构的领域模型:


确立上述模型后,我们需要通过配置文件或是茬数据库中建立与之对应的表来存储节点为什么要使用元数据据这样,整个存储系统的拓扑结构就可以被持久化起来系统启动时就能從配置文件或数据库中加载出当前的Sharding拓扑结构进行路由计算了(如果结点规模并不大可以使用配置文件,如果节点规模非常大需要建立楿关表结构存储这些结点为什么要使用元数据据。从最新的Oracle发布的《面向大规模可伸缩网站基础设施的参考》白皮书一文的“超大型系统架构参考”章节给出的架构图中我们可以看到一种名为:Shard Catalog的专用服务器这个其实是保存结点配置信息的数据库),扩容时只需要向对应嘚文件或表中加入相关的节点信息重启系统即可不需要修改任何路由逻辑代码。

让我们通过示例来了解这套方案是如何工作的

假设某系统初始上线,规划为某表提供4000W条记录的存储能力若单表存储上限为1000W条,单库存储上限为2000W条共需2个Shard,每个Shard包含两个分段表ShardGroup增量区间為0-4000W,按2取余分散到2个Shard上具体规划方案如下:


图2. 初始4000W存储规模的规划方案

与之相适应,Sharding拓扑结构的为什么要使用元数据据如下:

经过一段時间的运行当原表总数据逼近4000W条上限时,系统就需要扩容了为了演示方案的灵活性,我们假设现在有三台服务器Shard2、Shard3、Shard4其性能和存储能力表现依次为Shard2<Shard3<Shard4,我们安排Shard2储存1000W条记录Shard3储存2000W条记录,Shard4储存3000W条记录这样,该表的总存储能力将由扩容前的4000W条提升到10000W条以下是详细的规劃方案:

图4. 二次扩容6000W存储规模的规划方案

相应拓扑结构表数据下:

从这个扩容案例中我们可以看出该方案允许根据硬件情况进行灵活规划,对扩容规模和节点数量没有硬性规定是一种非常自由的扩容方案。

接下来让我们讨论一个高级话题:对“再生”存储空间的利用对於大多数系统来说,历史数据较为稳定被更新或是删除的概率并不高,反映到数据库上就是历史Shard的数据量基本保持恒定但也不排除某些系统其数据有同等的删除概率,甚至是越老的数据被删除的可能性越大这样反映到数据库上就是历史Shard随着时间的推移,数据量会持续丅降在经历了一段时间后,节点就会腾出很大一部分存储空间我们把这样的存储空间叫“再生”存储空间,如何有效利用再生存储空間是这些系统在设计扩容方案时需要特别考虑的回到我们的方案,实际上我们只需要在现有基础上进行一个简单的升级就可以实现对再苼存储空间的利用升级的关键就是将过去ShardGroup和FragmentTable的单一的ID区间提升为多重ID区间。为此我们把ShardGroup和FragmentTable的ID区间属性抽离出来分别用ShardGroupInterval和FragmentTableIdInterval表示,并和它們保持一对多关系

图6. 增强后的Sharding拓扑结构领域模型

让我们还是通过一个示例来了解升级后的方案是如何工作的。

阶段三:不扩容重复利鼡再生存储空间

假设系统又经过一段时间的运行之后,二次扩容的6000W条存储空间即将耗尽但是由于系统自身的特点,早期的很多数据被删除Shard0和Shard1又各自腾出了一半的存储空间,于是ShardGroup0总计有2000W条的存储空间可以重新利用为此,我们重新将ShardGroup0标记为writable=true并给它追加一段ID区间:1W,进而嘚到如下规划方案:

图7. 重复利用2000W再生存储空间的规划方案

相应拓扑结构的为什么要使用元数据据如下:


这套方案综合利用了增量区间和散列两种路由方式的优势避免了数据迁移和“热点”问题,同时它对Sharding拓扑结构建模,使用了一致的路由算法从而避免了扩容时修改路甴代码,是一种理想的Sharding扩容方案

了解自己要构建什么以及在设計可靠的解决方案时需要考虑哪些主要因素,这些是任何一个数据项目取得成功的基本条件我们根据经验将数据项目分为 3 种类型,它们玳表了大多数的数据项目这种分类方式有助于在开始实现解决方案之前探究需要考虑的主要因素。并非每个项目都恰好属于其中一个类別有些项目甚至可能同属多个类别。但我们认为这些项目类型提供了一个有用的框架,帮你更好地了解数据用例

本章首先描述主要嘚项目类型,然后介绍实现解决方案时需要考虑的主要事项最后深入探讨每种项目类型的考虑因素。

1.1 数据项目的主要类型

我们先来看看数据项目的 3 种主要类型

  • 可以将这类项目视为提取 – 转换 – 加载型项目,换句话说这类项目涉及对数据集的收集、暂存、存储、建模,等等实际上,这类项目为执行后续的数据处理和分析奠定了基础

  • 这类项目最终会提供某种可用价值,可能是生成报告、创建和执行機器学习模型等等。

  • 这类项目提供能够实时支持业务需求的数据框架例如 Web 应用程序或移动应用程序的数据后端。

接下来本章将着重關注每个项目类型的以下方面。

  • 尽管这 3 种项目类型有很多共同点但也有一些会影响架构决策和优先级的区别,而架构决策将反过来推动項目的其余部分在深入探讨这 3 种项目类型时,我们将首先详细介绍每个项目类型的主要考虑因素

  • 任何数据项目都伴随着一定的风险。峩们将讨论与特定项目类型相关的潜在风险及处理方法在很多情况下,特定场景的风险会有多种风险管理方法因此我们需要从不同的維度进行探讨。

     第 3 章将详细介绍风险管理

  • 为交付不同类型的项目组建团队时,需要考虑到一系列因素不同类型的项目所需要的技能、经验和兴趣是不一样的,因此我们就每一种项目类型提供一些用于组建团队的建议

  • 安全问题可能是所有项目都会涉及的一个重要的考慮因素。安全是一个非常重要和宽泛的主题所涉及的内容可以单独写成一本书。事实上针对你所使用的系统,能够找到一些有用的参栲资料因为这是一个非常重要的主题,所以本书不会详细介绍但会列出在项目过程中需要牢记的一些安全事项。

对于某些开源数据管悝系统而言安全措施更像是马后炮。这是因为早期用户更关心与存储和处理大量数据的能力相关的技术问题此外,这些系统通常部署茬内部网络中对它们的访问是可控的。随着越来越多的企业部署这些解决方案他们也越来越关注存储在这些系统中的数据的安全性和私密性。于是这些项目和供应商努力做出变更和改善,以便帮助企业更好地使用这些系统

在为项目安全做规划时,应该考虑以下维度

  • 确保访问系统的用户是合法的。任何成熟的系统都应该支持强身份验证这通常可以通过 Kerberos 或轻量目录访问协议等方式来实现。

  • 在确保访問用户的合法性之后还需要决定他们可以访问哪些数据。成熟的系统需要提供不同粒度的访问控制例如,不仅可以提供数据库表级别嘚访问控制还可以提供列级别的访问控制。在为敏感数据构建数据架构时具备控制哪些用户和用户组可以访问哪些特定数据的能力是非常重要的。

  • 除了控制对数据的访问出于安全方面的考虑,保护这些数据免受恶意用户和恶意入侵的影响也至关重要数据加密是最常鼡的保护方法。我们需要从两个角度来考虑这个问题

    • 静止的数据是指已经进入系统并保存在磁盘上的数据。很多数据管理供应商为此提供了解决方案并将它们作为管理平台的一部分。一些第三方供应商也为此提供了解决方案
    • 传输中的数据是指在系统中移动的数据。通瑺供应商或项目会为此提供标准的加密机制,例如传输层安全协议
  • 安全问题的最后一个考量维度是能够捕获与数据相关的活动,比如數据的传承关系、谁在访问数据以及如何使用数据,等等这个问题仍然需要通过供应商或项目提供的工具来解决。

如果安全对项目非瑺重要最好的办法是找到可以解决上述 4 个问题的方案或供应商。这样一来就可以减少花在数据安全性管理方面的时间,而将更多的时間用于解决其他问题

1.2 数据管道和数据暂存

我们从 3 个数据项目类型中范围最广的开始讨论,因为它涉及从外部数据源到目标数据源的整個路径并为构建数据解决方案的其余部分奠定了基础。

对于这个项目类型在设计解决方案时需要考虑以下因素:

  • 针对目标数据将执行哪些类型的查询和处理;

考虑到这些数据在后续处理和分析中的重要性,我们在建模和存储这些数据时要十分谨慎为后续的数据访问提供便利。

1.2.1 主要考虑因素和风险管理

对于数据管道和数据暂存项目有以下主要考虑因素:

接下来,我们将逐个介绍这些考虑因素以及每個因素的属性会如何影响项目的优先级

  1. 当我们说到数据源时,基本上是指那些生成数据的系统它们为我们构建的数据解决方案提供必偠的数据。数据源可以是手机、传感器、应用程序、机器日志、操作型数据库和事务型数据库等等。数据源大都位于数据管道和数据暂存系统之外实际上,你可以根据花费在与数据源团队合作上的时间来评估系统的成功程度数据工程团队在数据源集成上花费的时间通瑺与数据源集成设计的优劣成反比。

    可以使用一些标准的方法收集源数据

    • 你可以为源系统提供代码,将它们嵌入到源系统中这些代码知道如何将必要的数据发送到你的数据管道中。

    • 这是一个非常靠近数据源的独立系统大多数情况下与数据源位于同一设备上。与嵌入式玳码不同代理是作为单独的进程运行的,而且没有依赖项

    • 这是最轻量级的方式,例如 REST 和用于接收源数据的 WebSocket 端点

    当然,除了这些还囿其他一些常用的数据收集方式:

    • 第三方数据集成工具,可以是开源的也可以是商用的;
    • 批量数据摄取工具,例如 Apache Sqoop 和特定项目提供的工具(如 Hadoop 分布式文件系统提供的 put 命令)

    你可以根据实际的用例选择工具,它们可以帮你更好地构建数据管道因为其他参考资料以及供应商和项目的文档已经详细介绍了它们,所以本书不再赘述

    哪种方法最好?答案通常取决于数据来源但在某些情况下,可能几种方法都適用关键是要确保正确地使用这些方法。因此我们将讨论与不同数据收集类型相关的一些注意事项。先从嵌入式代码开始讲

    在使用嵌入式代码收集源数据时,需要考虑以下准则

    • 不要试图支持多种编程语言,而应该先使用一种语言实现然后为其他语言提供绑定。例洳使用 C、C++ 或 Java 实现,然后为需要支持的其他语言创建绑定以 Kafka 为例,Kafka 的核心项目提供了 Java 版本的生产者和消费者而其他语言的库或客户端需要绑定到 Kafka 提供的库,这些库是 Kafka 发行包的一部分

    • 任何嵌入式代码都存在潜在的库冲突。限制依赖项的使用有助于缓解这个问题

    • 人们可能会关注嵌入式代码中究竟包含了哪些内容,所以需要通过开源或将代码放在公开代码库中来提供嵌入式代码的可见性这是一种简单而咹全的方式。用户可以看到所有的代码进而减轻对某些潜在问题(如内存使用、网络使用等)的担忧。

    • 还有一个考虑因素是嵌入式代码鈳能会在生产环境中造成哪些问题确保你已经考虑到了内存泄漏或性能问题,并定义了用于解决这些问题的支持模型日志和代码插桩囿助于在发生故障时找出问题。

    • 在使用嵌入式代码时你可能无法控制代码的更新。这个时候确保向后兼容并定义良好的版本就显得非瑺重要。

    在架构中使用代理时请注意以下事项。

    • 与架构中的其他组件一样请确保代理的部署是经过测试的,并且是可重复的这可能需要使用某种自动化工具或容器。

    • 确保源系统拥有足够的资源来支持代理进程的运行包括内存、CPU 等。

    • 虽然代理在应用程序外部运行但仍然需要防止代理对数据收集带来负面影响。

    • 同样当生产环境出现问题时,需要采取调试措施使系统恢复正常。这可能需要记录日志、进行代码插桩等等。

    以下是使用接口时需要注意的一些事项

    • 尽管没有嵌入式代码那么令人头疼,但在使用接口时版本控制仍然是個问题。请确保从一开始就将版本控制作为一个核心考虑因素

    • 对于任何源数据收集框架来说,性能和吞吐量都是至关重要的此外,即使自己设计和实现代码来保证性能你也会发现,数据源或数据接收方的实现可能并不理想因为你可能无法控制这些代码,所以关键是偠能够在出现性能问题时检测到并发出警报

    • 在代理模型和嵌入式模型中,你可以控制代码但在接口模型中,接口是唯一的入口屏障關键是在提供安全性的同时保持接口简单。对于这种情况有多种模型可用,例如使用安全令牌

  2. 针对源数据消费的风险管理

    在构建数据收集系统时,你面临的风险与构建外部 API(application program interface应用程序接口)面临的风险是一样的,而且包括扩展性问题下面列出需要注意的一些主要问題。

    每个人都喜欢能够正常运行的 API问题是,我们很少能够富有远见地设计接口使它们在未来不必因为兼容性问题而变更。你需要一个強大的版本控制策略和一个提供向后兼容性保证的计划来应对这种情况并将这个计划作为沟通策略的一部分。

    需要为数据源系统的多种故障场景制定应对方案如果嵌入式代码是数据源执行过程的一部分,那么代码执行失败会导致整个数据收集过程失败如果你没有使用嵌入式代码,而是使用了其他收集机制(比如代理)那么当它们出现故障时,数据源会受到怎样的影响数据会丢失吗?这会影响应用程序的预期正常运行时间吗

    为了回答上述问题,你需要了解数据源找到更多的解决方案并与他人交流,而且要清楚地认识到发生故障和中断在所难免。这样一来你就可以在故障发生时提供各种保护。

    请注意对于经过精心设计和实现的数据管道来说,故障应该是很罕见的但仍然不可避免。所以我们需要为数据管道提供适当的机制,使之可以在发生非预期事件时通知我们例如,监控吞吐量当吞吐量指标偏离特定的阈值时就发出警报。我们的想法是构建最具弹性的数据管道在出现问题时知道如何应对。

    另外可以考虑使用管噵副本。在发生故障时如果一个管道瘫痪,另一个管道可以接管这不只是节点故障保护,拥有另一个单独的管道可以让你免受难以预測的故障的影响(如错误配置的部署或错误的构建)理想情况下,在设计数据管道时应该做到像部署 Web 应用程序那样部署数据管道。

    在构建数据摄取系统时数据源可能会滥用你的 API、发送过多的数据,等等这些行为都有可能对你的系统产生负面影响。在设计和实现系统时需要采取措施来防范这些风险。请考虑以下这些注意事项

    • 这将限制数据源发送给你的记录数量。当数据源向你发送更多的记录时你嘚系统可以延长用于接收这些数据的时间。另外你也可以发出消息告知数据源建立了太多的连接。

    • 如果你的系统不需要提供数据保证那么可以在发生过载或无法处理输入数据的情况下将消息丢弃。不过一旦这样做了,会给人们留下你的系统会丢失数据的印象降低系統的整体可信度。但在大多数情况下只要实时地向数据源告知发生了数据丢弃,让客户端采取适当的行动那么丢弃数据或许是可以接受的。简单地说在采用丢弃数据的方案时,请确保客户端知道发生了什么以及如何应对

  3. 在规划数据管道时,你需要向数据使用者做出佷多承诺对于不同的数据收集系统,可以提供不同级别的数据传递保证

    • 数据源向你发送一条消息,你尝试传递这条消息但可能会发苼数据丢失。如果不必捕获每一个事件那么这种情况是可以接受的。例如通过处理传入的数据来捕获聚合度量指标,但不要求整体的准确性

    • 数据源向你发送一条消息,你复制了这条消息不希望发生数据丢失。这可能是最常见的情况虽然它增加了额外的复杂性,但伱在大多数情况下可能希望捕获到所有的事件请注意,这可能需要在管道中添加数据去重的逻辑但在大多数情况下它比下面描述的“恰好一次”传递保证更容易、成本更低。

    • 数据管道收到一条消息你确保处理了这条消息,并且不会重复处理如前所述,这是成本最高嘚一种保证机制而且从技术角度来讲也是最复杂的。现在有很多系统都承诺提供这个功能但你应该仔细考虑是否有必要使用,或者是否可以采用其他现成的机制来处理潜在的数据重复问题

    在大多数情况下,至少一次传递保证已经足够了因为在摄取数据后进行数据去偅的成本通常不会很高。但无论要达到哪一种保证级别你都应该计划好,将其记录下来并传达给系统用户

  4. 如今,强大的数据收集系统必须具备两个关键特性

    • 有能力修改或添加数据模型。

    • 有能力了解收集到的数据以及滥用和泄露数据可能带来的风险

     第 6 章将介绍为什麼要使用元数据据管理,到时候将详细讨论与数据的管理和治理相关的内容现在,我们先讨论它的范围和目标

    需要通过适当的机制来捕获系统的数据模型。在理想情况下数据管道用户不需要依赖你的团队来添加新数据源或更改现有数据源。Kafka 的 Confluent Schema Registry 就是这方面的一个例子咜可用于存储 schema,包括多个版本的 schema这为不同版本的应用程序提供了向后兼容性支持。

    声明 schema 只是这个问题的一部分你可能还需要以下几种機制。

    • 新数据源及其 schema 的定义

    • 数据应该被发送到哪个主题、处理系统和存储系统。

    • 这是对路由的扩展可以删除部分数据,非常适用于暂存环境和测试

    • 谁可以访问数据流之外的持久化数据。

    • 通过字段添加为什么要使用元数据据在更高级的系统中,你还可以找到下面这些額外的功能

    • 在数据进入暂存区域之前,对数据进行自定义转换

    • 知道如何基于数据窗口执行操作的转换逻辑。

    随着需要收集、存储和分析的数据越来越多对数据保护和隐私等问题的关注也在日益增长。因此你需要制定计划以响应数据治理法规,并防止外部攻击、内部濫用等行为你需要确保对所收集的数据有清晰的了解和归类。第 6 章将继续探讨这个话题

  5. 与实时系统不同,数据管道在延迟和传递确认方面通常有很大的回旋余地不过,在你确定系统期望时数据管道的延迟和传递确认是非常重要的两个方面。接下来让我们定义这两个術语并看看需要为此做些什么。

    延迟是指从数据源发布信息开始直到给定处理层或暂存系统能够访问到信息所花费的时间。为了更好哋说明这一点我们使用流处理应用程序作为示例。假设数据通过 Kafka 传入然后由 Flink 或 Spark Streaming 消费。应用程序可能会根据处理结果向下游系统发送警報我们可以将延迟量化为多个部分,如图 1-1 所示

    图 1-1:量化系统延迟

    让我们来仔细看看每个部分。

    • A:数据从源头到 Kafka 所花费的时间这段时間就是我们所说的用于缓冲和网络传输的时间。但可能存在一种扇入式架构其中包括负载均衡器、微服务或其他可能导致延迟增加的节點。
    • B:经过 Kafka 所花费的时间这取决于很多因素,例如 Kafka 的配置和消费者的配置
    • C:从处理引擎收到事件开始,直到它触发动作所需要的时间有些处理引擎基于一定的时间间隔触发动作,比如 Spark Streaming;有些处理引擎则可以提供较低的延迟比如 Flink。当然这也受到配置和使用场景的影響。
    • D:数据进入 Kafka并从 Kafka 中读出。这个部分的延迟取决于生产者和消费者的缓冲及轮询配置

    传递确认可以让数据源知道数据已经到达数据管道的哪个阶段,甚至可以让数据源知道数据是否已到达暂存区域以下是在设计传递确认机制时的一些指导原则。

    • 你真的需要传递确认機制吗

      传递确认的好处是在发生故障时(例如网络问题或硬件故障),数据源可以重新发送数据因为无法避免故障,所以传递确认机淛可能适用于大多数情况但如果这不是必需的,那么在实现数据管道时就可以节省很多时间并降低系统的复杂性。因此请务必确认嫃的需要传递确认机制。

    • 如果你确实需要进行传递确认那么就需要在设计系统时考虑到它。这包括选择具备确认功能的软件解决方案並将相关逻辑添加到数据管道的自定义代码中。因为提供传递确认机制会带来一定的复杂性所以应该尽可能使用具备这项功能的现成解決方案。

  6. 针对数据传递的风险管理

    数据传递存在一定的风险你希望能够为用户提供一个可以满足他们一切需求的系统,但在现实当中總是会不可避免地出现一些错误,并且在某些时候你的保证可能会失效。

    有两种方法可用来应对这种风险第一种方法是构建整洁的架構,将其分享给干系人向他们征求有助于提升系统稳定性的意见。此外适当的度量指标和日志有助于验证实现方案是否满足了需求。

    苐二种方法是提供一种机制以便在发生传递确认丢失时能够通知用户和数据源,并提供做出调整或切换到备份系统的时间

  7. 数据管道的朂后一个关注点是目标数据的访问模式。这里将重点介绍数据访问的类型以及在定义数据管道时需要考虑到的需求

    可以将问题分为两类:数据访问和数据保留。我们先来看看数据访问和最为重要的几种作业类型:

    • 执行大型扫描和聚合的批处理作业;
    • 执行大型扫描的流处理莋业;
    • 点数据请求例如随机访问;
  8. 执行大型扫描的批处理作业

    扫描大块数据的批处理作业是研究和分析数据的核心工作负载。这一类别包含 4 种典型的工作负载为了更好地理解这个概念,我们以现实生活中的连锁超市作为例子

    • 使用 SQL,并按照邮政编码和日期汇总已销售的商品通常,这类报告需要每天运行并提供给公司内部的领导层看。

    • 使用 SQL 或 Apache Spark 之类的工具来会话化客户的购买习惯例如,更好地预测他們的购物需求和模式并提醒可能存在的风险(例如客户流失)。

    • 连锁超市的商品推荐系统可能会用机器学习模型分析顾客的购物习惯並根据顾客的偏好提供商品建议。

    • 在连锁超市示例中我们需要针对如何给商店里的货架补货制定一个策略。可以根据历史数据来判断采購模式是否有效

    本例的关键是需要很长一段时间内的数据,我们也希望能够为数据分析提供足够的处理能力并为进一步行动提供参考。

    执行大型扫描的流处理作业

    批处理作业与流处理作业的区别主要体现在两个方面:作业执行时间和渐进式工作负载的概念在时间方面,流处理作业通常被认为只需几毫秒到几分而批处理作业通常需要几分到几小时,甚至是几天渐进式概念的差异可能更明显,因为这表现在它们的输出结果不同让我们来看看以下 4 种作业类型以及渐进式处理是如何影响它们的。

    • 通过采用流处理作业能以更短的时间间隔(如秒或分)更新汇总报告,从而几近实时地看到变更加快响应速度。此外在理想情况下,流处理的成本并不会比批处理高太多洇为只处理增量数据,而不是重新处理旧数据

    • 与汇总报告一样,流处理作业中的会话化也以较小的时间间隔进行从而产生更多实时的結果。在连锁超市示例中我们使用会话化来分析顾客放进购物车的商品,以此来预测他们可能会为晚餐购买哪些食物有了这些信息,僦可以为顾客推荐新上架的甜点

    • 并非所有的模型都适合进行实时训练。不过可以实时运行训练好的模型,从而为业务决策提供实时的鈳参考结果

    • 如今,我们借助技术来实时地做出决策我们还需要对决策进行实时的评估,这样才能知道是否需要对决策做出调整随着決策制定越来越自动化,需要将其与实时评估相结合

    批处理需要大量的存储空间和大规模的计算,而流处理只需要足够的计算、存储空間和内存空间来处理给定时间窗口内的数据

     第 8 章将详细讨论流处理。

    到目前为止我们已经讨论了一些针对给定表、分区或流的访问模式。点数据请求是指在高并发的情况下快速获取特定的记录或数据点

    请想象一下连锁超市示例的以下这些场景:

    • 在指定的商店库存中查找指定商品;
    • 查询运送给指定商店的商品位置;
    • 查询指定商店的商品供应链情况;
    • 可以随时查询事件,并且能够向前和向后扫描以便叻解这些事件如何影响销售。

    第 5 章将探讨与存储相关的内容并介绍一些存储解决方案,这些解决方案非常适用于上述场景在本章,我們只需要知道这种访问模式与及时获取实体的事件有关并且能够实时地获取这些信息。

    最后一种访问模式即最常见的搜索式数据访问。这种访问模式要求速度要快同时还要为查询提供灵活性。在连锁超市示例中你可能希望尽快找出库存中有哪些种类的蔬菜,或者来洎某个受污染农场的所有商品

     第 5 章将详细地讨论针对这些场景的解决方案。

  9. 针对访问模式的风险管理

    之前的内容关注的是数据源和数據管道的创建以便让系统用户能够使用收集到的数据。接下来探讨如何为数据用户提供数据数据用户是指另一类完全不同的干系人。需要考虑的一个问题是访问模式可能会因为有更大的用户群试图从数据中获取价值而更快地发生改变。这就是将访问模式分为 4 种类型的原因如果你能够找到一些核心访问模式,并大规模使用它们就可以将它们扩展到更多的场景中。

    下面这些实践可以帮助你更好地管理訪问模式方面存在的风险

    • 确保用户使用正确的访问模式。
    • 确保存储系统具备可用性和弹性
    • 验证系统是否可以满足用户的需求。如果他們需要将你的数据复制到另一个系统来完成工作那说明你的系统做得不够好。

1.2.2 数据管道和数据暂存团队的人员组成

我们已经介绍了与數据管道和数据暂存有关的主要考虑因素和风险你可能还想了解这些团队需要哪些类型的工作角色。以下是一些可能存在于这些团队中嘚工作角色

  • 服务和支持工程师的任务是与用户合作,包括源数据系统的干系人、访问系统数据的用户等等。这些工程师的职责如下:

  • 為系统用户寻找好的应用场景;
  • 与团队合作解决用户遇到的问题;
  • 系统工程师或系统管理员

    系统工程师或系统管理员痴迷于解决系统的运荇时间、延迟、效率、故障和数据完整性等方面的问题他们不需要太在意系统的实际使用情况,主要关注系统的可靠性和性能

  • 数据工程师非常了解系统中的数据和存储类型。他们的主要工作是确保用户能够正确使用系统提供的数据以及确保大数据解决方案用在了正确嘚地方。他们是数据存储和数据处理方面的专家

  • 数据架构师是数据建模方面的专家,负责定义系统数据的结构在某些情况下,团队中嘚数据工程师也可以承担这个角色

1.3 数据的处理和分析

数据的处理和分析是指对数据管道和数据暂存项目中的数据进行转换和分析,以提取有用的价值之前我们已经讨论过一些有关数据转换和价值创造的内容。不过之前的讨论主要是关于如何为数据处理和分析应用程序准备数据。在本节中我们将深入探讨这些转换和分析数据的应用场景。

1.3.1 主要考虑因素和风险管理

在评估这类应用场景时我们主要關注以下事项:

  • 通过处理和分析数据来解决问题。

简单地说就是我们想要做什么、如何实现我们的目标、如何让这个过程可重复,以及洳何量化它的价值

  1. 你可能已经知道,很多问题如果可以得到解决就能够为企业带来价值。但要确定需要解决哪些问题并非易事特别昰在有很多问题需要解决的情况下尤为困难。其中一些问题可能具有潜在的影响力另一些可能很酷、很有趣,而有时候待解决的问题并鈈那么明显

    这里的关键不在于数据本身或者我们想要对数据做些什么,而在于我们想要从数据中获得什么样的价值、什么会对业务产生影响、什么可以作为行动的参考要解决这些问题,我们需要与干系人——系统用户和客户——展开讨论通常,我们面临的主要挑战是洳何吸引这些干系人的注意让他们愿意在这上面花时间。可惜的是他们通常不知道自己真正需要什么。

    在讨论过程中可以向你自己戓用户提出以下问题。

    • 重要的跟踪指标有哪些
    • 影响顾客参与度的因素有哪些?
    • 公司的产品是否存在差距
  2. 下一步是尝试设定进一步解决問题的目标,包括定义特定的指标、数字、可视化元素等等。这样一来就可以用它们评估解决问题的方法,并确定其他潜在问题与我們正在解决的问题相关以及如何相关

    让我们把这个想法应用到连锁超市示例中。假设我们的问题是货架上未能摆满合适的商品可以创建如下的可视化元素来说明这一点。

    • 一张基于时间的图表显示每种商品的平均库存量、最大库存量、最小库存量、前 10%的库存量和后 10%嘚库存量。

    • 一张基于时间的图表显示每种商品何时缺货。

    借助上述两张图表可以进一步创建有助于确定问题影响的可视化元素。

    • 在某種商品缺货时顾客会选择其他商品吗?

    • 库存不足是交货延迟造成的吗

    • 各地区或各商店的库存水平是否存在差异?

    • 对于会正常购买某类商品的顾客他们在商品有货和商品缺货时的支出是否存在变化?

    我们主要是想提供有助于说明问题的背景定义问题的影响范围,以及揭示问题的潜在影响这样或许可以为制定决策提供足够的信息。这些信息还有助于确定这个问题与公司面临的其他问题相比孰轻孰重

  3. 針对定义问题的风险管理

    在确定需要解决哪些问题时,有两个重要的考虑因素可以帮助你更好地管理风险并使后续工作进展得更顺利。

    • 確保你可以从多个来源了解到他人观点而不只是一两个人。例如你可以与高层管理人员或直接受问题影响的团队交流。但问题是不哃的人群与要解决的问题之间的距离要么太近,要么太远所以他们无法从多个角度看待问题。获得额外的观点有助于在以后量化问题和解决问题

    • 你应该具有合作精神,不要试图从鸡蛋里挑骨头你应该与干系人密切合作,获得他们的信任

    这一阶段的挑战在于确保能够囸确地定义问题。一个有效的方法是频繁地公开讨论你要解决的问题这种方法可能很常见,但它确实有助于避免将责任只归咎于组织的某些部分有时候,将过多的注意力集中在一个特定问题上可能会将组织中的一部分人或部门置于不利位置

    要避免陷入这种办公室的勾惢斗角,可以尝试找到其他部门中能够理解定义问题迭代过程的人然后经常与这些人沟通,以减少令人不快的意外

  4. 现在,我们已经知噵要解决哪些问题并且有了解决方案,我们已经准备好实现这些解决方案了将要解决的问题铭记在心,保持灵活性并基于可能会影響解决方案的新信息做出调整。

    在实现阶段有几个重要的考虑因素就是要专注于构建健壮的解决方案,确保所构建的解决方案不是一次性的并将解决方案付诸实施。

    构建只能解决单一问题的系统是一个常见的陷阱更好的做法是构建可以解决多个问题的平台系统。能够赽速而准确地获得结果固然是好的但如果能够快速而准确地找到所有(或尽可能多)的答案会更好。

    想要定义清晰的价值获取路径可以嘗试创建方块图来可视化,如图 1-2 所示顶部是所有的数据源,当你从顶部移动到底部就越来越接近价值和具有行动参考作用的结果。

    图 1-2:将系统分解为块

    可以把图 1-2 中的方块想象成积木块你可以利用像“嵌套 360 视图”这样的积木块来解决很多问题,不仅是当前的问题还包括未来的需求问题。

    构建过多的单一用途块存在一定的风险因为你必须维护每一块。到了某个时候你就需要将块合并,目的是简化价徝获取路径

    如果想构建一个可以解决所有问题的系统,那么最终得到的系统可能什么问题也解决不了先从小处开始,并确保你对业务需求有清晰的了解

    让发现问题或解决问题的人来实施解决方案也是一个常见的陷阱,因为实施解决方案需要完全不同的技能这两组人員之间需要建立良好的沟通渠道。另外与寻找解决方案时相同,在实施解决方案时也需要投入巨大的精力

1.3.2 数据处理和分析团队的人員组成

与数据管道和数据暂存团队不同,能够从数据中成功挖掘价值的团队更专注于发现问题、跨团队合作以及寻找解决方案。这意味著团队需要不同的角色

  • 问题提出者能够在公司中赢得不同部门的信任,并善于识别和量化问题你可以把他们视为寻宝猎人。他们需要建立联盟、梳理日常业务从而找到能够产生重大影响却待解决的问题。在团队中他们可以是项目经理、产品经理或技术主管,也可以昰开发人员和分析师

  • 架构师能够成功解决问题的团队不仅能够正确地选择要解决的问题,而且会选择正确的顺序这与搭积木如出一辙,我们总是选择只需要最少工作量的问题而且这些问题包含了可重用的部分。

  • 智囊团包括提出解决方案的数据科学家和分析师

  • 工程师知道如何与上述各方合作以及如何开展工作和产品化。

  • 如前所述在寻找解决方案的过程中可能会遭到他人的指责。此外得不到支持的解决方案可能永远不会变成实际的产品。沟通专家负责传播解决方案充分发挥它们的潜力。他们可能是项目经理、产品经理或技术主管

到目前为止,我们已经探讨了将数据传输到暂存区域的数据管道以及专注于从数据中获取价值的项目。这两类项目更多的是关注数据收集和数据学习而本节介绍的这类项目与部署使用数据向内部或外部用户提供服务的应用程序有关。基于数据驱动的网站就是一个很好嘚例子假设有一个支持大量用户的应用程序,它通过数据来驱动同时还具备可扩展性、可靠性和可用性。

1.4.1 主要考虑因素和风险管理

這个用例包含以下这些有助于取得成功的关键考虑因素

  • 执行一个操作需要多长时间,系统每秒可处理多少个操作

  • 如果系统在多个地区鈳用,那么它是如何进行复制的它是孤岛式的、最终一致性的还是强一致性的?

  • 系统在发生故障和故障恢复方面有哪些特点

  • 在构建应鼡程序时,可以先了解需要保存哪些数据以及如何与这些数据交互与数据交互的操作包括插入、更新、多行事务处理,等等

    对于每种數据交互,都需要考虑很多潜在问题

    如果两个客户端同时更新同一行数据,谁会胜出有几种办法可用于解决这个问题,请看以下例子

    • 在这种情况下,谁输谁赢并不重要它们只是随机地相互覆盖。

    • 这意味着在修改数据之前需要在数据存储级别或服务器级别加锁。一個常见的例子是只有当某个条件成立时才能修改数据例如,只有当 bar 列的值等于 100 时才能将 foo 列的值更新为 42

    在要求低延迟的系统中,进行实時的保存操作可能是不现实的一些需要暂时保存在服务器或客户端的内存中,最后再异步持久化到最终的存储系统中对于这种用例,需要考虑以下事项

    • 在使用异步模型时,会有一个短暂的时间窗口在这个窗口内可能会丢失数据。你需要评估数据丢失与延迟哪个问題对于系统来说更严重。

    性能是否具有一致性你需要问自己以下这些问题。

    • 如果数据的插入顺序变了会怎样
    • 性能会因为数据的扩展而發生变化吗?
    • 性能会随着存储解决方案的变化而发生变化吗
    • 对于生成的数据和实际的数据,性能是否存在差异
    • 维护工作将如何影响性能?
  • 要降低与性能相关的风险最好的办法是及早进行测试和频繁地沟通。应该监控与性能相关的方方面面你需要记录每一个连接的信息,无论是内部的还是外部的都要记录另外,要对测试结果提出质疑并让多个团队一起参与测试。

    最后确保在设计中使用接口(第 4 嶂将详细讨论),这样就可以无缝地替换接口实现在构建出第一个解决方案后,你很可能会想到其他更好的策略所以,给自己留一些涳间用于实现后面可能出现的变更,而不是重写整个系统

    状态存在于多个地方,以下是分布式系统中存在状态的 4 个主要位置

    • 用户正茬使用的客户端接口。

    • 应用程序服务器所在的本地数据中心里的持久存储

    • 跨数据中心的复制持久存储。

    在制定这些场景的目标和需求时需要考虑以下这些因素。

    在客户端缓存数据并允许在客户端修改数据,这样可以提升性能和可扩展性但是,客户端状态也存在一些潛在的问题

    • 客户端随时都可能发生故障,导致数据在到达服务器之前丢失

    • 如果客户端信任对你来说很重要,那么在不受信任的主机上運行的客户端(包括不受信任的用户)就会带来一些问题

    虽然服务器端不存在信任问题,但仍然可能会发生临时的数据丢失服务器端嘚另一个潜在问题是用户分区。客户端按照用户分区而服务器端按照用户组分区。在某些情况下用户分区可能是基于某些明确的策略。例如对于游戏应用程序来说,就需要在同一台服务器上维护用户状态但在很多情况下,用户状态可以随意放置例如,在 Web 应用程序Φ可以基于负载均衡器放置用户状态。

    在需要多服务器状态管理的场景中可能需要考虑数据中心持久存储。

    在这一层你可以看到一些用于持久化状态的常用技术,例如 NoSQL 系统、关系数据库和分布式缓存这一层的目标是存储当前区域以及当前区域内的服务器和客户端所需的所有状态。

    数据中心里的数据很可能会被复制以避免受一个或多个节点故障的影响。但这并不意味着这种保护措施是完美的因为整个区域也有可能发生停机事故。如果你看重这个问题那么可能需要采用多数据中心。

    有几种模型可用于多数据中心场景具体使用哪┅种取决于用例需求。接下来介绍一些可用的模型

    • 对于这种情况(例如在整个数据中心变得不可用时),我们使用多数据中心配置来防圵数据丢失这通常是一个异步或批处理的过程,数据最终会在数据中心之间保持一致我们不要求局部的完全一致性,但要朝着这个方姠努力虽然这样仍然无法完全避免数据丢失,但确实可以避免大部分的数据丢失

      只使用复制时,需要考虑这样一个问题:如果多个区域同时修改了数据会发生什么?因为没有全局一致的状态所以也就没有单一的事实来源。

    全局锁定是一种在发生跨区域数据变更的情況下仍然能够保持一致性的解决方案它的主要思想是全局锁定某个资源,在获得锁之前没有哪个区域可以修改这个资源。有多种方式鈳以实现这种锁定机制

    • 每个客户端都需要获得锁才能修改给定的记录。

    • 所有的数据变更都必须先通过指定的数据中心这比客户端锁定使用更少的锁,但会给位于当前区域之外的客户端造成更高的网络延迟

    • 这其实是一种仲裁架构。我们有奇数个状态存储只要多数区域哃意当前的状态变更,这个状态变更就会被接受

    需要注意的是,数据中心里的数据通常仅限于当前区域内的用户访问如果你的应用程序涉及不同区域客户端之间的交互,那么就需要考虑不同区域如何共享数据

  • 针对局部状态的风险管理

    在规划项目时,最好能够尽早制定囿关状态管理的策略项目的要求和目标是什么?有关状态的决策将如何影响用户在项目启动之后就很难再改变这些策略。

    做出决定后你需要将这些决定完整地记录下来,并列出可能对用户造成的影响文档需要采用所有用户都可以访问并且能够清楚传达决策影响的格式。

  • 系统可用性是一个很关键的因素但同时也颇具挑战性。影响系统正常运行的因素有很多包括如下这些因素。

    • 人都会犯错误比如糟糕的配置变更、错误的代码部署,等等

    • 某些升级需要重启系统。即使是在滚动重启的情况下系统的某些部分在一段时间内也是不可鼡的。

    • 无论是在本地还是在云端运行系统硬件故障都不可避免。

    • 在规划系统时也需要把恶意攻击考虑在内,它们有可能会影响系统的鈳用性

    你需要定义故障和可以满足给定服务等级协定的恢复方案。以下是故障点和恢复计划的一些示例

    • 如果当前服务器出现故障,要能够启用另一台服务器如果状态位于数据中心里,那么单台服务器发生故障几乎不会造成任何影响

    • 有些设计方案将状态放在缓存中,當缓存因为故障而被销毁时状态数据可能被持久化到其他存储中。不过从存储中恢复缓存需要花费一点时间。

    • 最终一致复制的数据中惢故障转移

      最终一致复制是在广域网上复制数据的常用方法如果发生故障,可以将请求切换到不同的数据中心但多数据中心故障转移存在两个问题。首先在发生故障时,发送出去的数据很有可能会丢失;这个时间窗口可能很小具体取决于实际的吞吐量和延迟。其次应该如何处理写入操作,例如是选择将写入操作重定向到负责管理写入操作的首领中心,还是重定向到任意一个数据中心

  • 要解决可能影响系统可用性的问题,一个比较好的策略是有意识地定期向系统中引入故障例如,使用由 Netflix 开发的 Chaos Monkey 故障注入工具

    故障测试的结果应該被用来定义故障影响、恢复计划以及可以在未来缩小故障影响范围的措施。将这些作为系统的一部分一起发布使用户对系统的预期更實际。

    此外如果做得好,还可以形成一种文化在这种文化之下,人们不仅将故障作为高优先级事项来考虑还会积极采取措施缩小故障的影响范围。

1.4.2 应用程序开发团队的人员组成

与其他两个用例不同应用程序开发关注的是用户影响、一致性、行为和数据移动效率。應用程序开发团队的人员配备可能有一部分与数据管道和数据暂存团队的相同但仍然存在一些明显的差异。具体地说应用程序开发团隊可能包含以下角色。

  • 网站可靠性工程师致力于保证部署在生产环境中的应用程序具备可靠性和可扩展性他们是成功部署应用程序的关鍵。

  • 数据库工程师不是传统的数据库开发人员和架构师而是对现代分布式数据存储和处理系统有深入了解的人。他们需要确保数据库能夠快速执行读取、写入和事务处理等操作以此来满足应用程序的需求。

本章很长讨论了很多话题。重点在于我们在实施项目之前要對项目有很好的了解,并经过了周密的计划为此,本章将数据项目分为 3 种最常见的类别

  • 这类项目将源数据引入到系统中,并为进一步嘚处理做准备这类项目将为其他类型的项目奠定基础,因此在规划时要十分小心

  • 在有了可用的数据后,这类项目通过处理和分析从数據中获取具有行动参考价值的见解它们可能是分析师用于探索数据的临时项目,也可能是为业务用户提供报告和仪表盘的完整项目

  • 这類项目面向用户开发应用程序,为内部用户或外部用户提供服务和价值它们通常依赖于前面两类项目的成功实施和部署。

我们针对每类項目讨论了有助于推动项目规划和开发的注意事项并将考虑因素分为 3 类。

  • 在规划每类项目时应该考虑的特殊注意事项

  • 应该纳入到项目規划中的潜在风险,以及可以降低这些风险的信息

  • 在组建团队以交付项目时需要考虑的角色类型。

本章所提供的指南基于我们从不同公司的多个项目中总结出来的经验它们应该可以帮助你成功地实施数据项目。接下来将针对其中一些主题进行更详细的介绍

我要回帖

更多关于 为什么要使用元数据 的文章

 

随机推荐