您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
   
 
 
     
   
 订阅
  捐助
规模要素:数据平台的组成和扩展
 
译者  乔永琪  来源:伯乐在线  火龙果软件  发布于 2015-8-05
  1932  次浏览      30
 

作为软件工程师,不可避免地受到周围计算机工具的影响,语言、框架、甚至执行过程都会影响我们构建的软件。

数据库亦如此,基于一种特殊的方式,不可避免地影响到我们对应用程序中易变和共享状态的处理。

过去的十多年,我们采用不同的方式去探寻这个世界。采用不同理念的一小众开源项目,它们不断成长,你中有我,我中有你。平台集成了这些工具,每个控件通常都能提高某些基础硬件或者系统效能。结果是平台无法通过任何单一的工具解决某些问题,不是太过笨重,就是局限于某一特定部分。

因此当今数据平台多种多样,从简单的缓存层、多语言持久化层到整个集成数据管道,针对多种特定需求的多种解决方案。在某些方面,确实有不错的表现。

因此本对话的目的就是解释一些流行的方式方法如何发挥作用,为什么会有如此表现。我们先来考虑组成它们的基本元素,这样便于在后续的讨论中对这些认识通盘地考虑。

从某种抽象的角度看,当我们处理数据时,实际上就是对其进行局部性(locality)处理,局部性到CPU、局部性到我们需要的其它数据。有序地获取数据是其中很重要的部分,计算机很擅长序列化的操作,这些操作是可以预测的。

(译者注:局部性是计算机中一种预测行为,通过缓存、内存中预取指令、处理器管道分支预测等技术来提高性能;更多参见《操作系统精髓与设计原理》。)

若是有序地从硬盘中获取数据,数据会预获取存入硬盘缓存、页缓存、以及不同层级的CPU缓存中,这可以极大地提升性能。但这对随机数据寻址意义不大,这些数据存于主内存、硬盘或者网络中。实际上,预获取反倒会拉低随机负载能力:不论是各种缓存或是前端总线,充满了不太会被用到的数据。

硬盘通常被认为性能稍低,而主内存稍快些。这种认识不见得一直是对的,随机和有序主内存负载之间相差一两个数量级。用某种语言管理内存,事情往往会变得更加糟糕。

从硬盘有序获取的数据流性能确实好过随机寻址主内存,或许硬盘并不像我们想的那样跟乌龟似的,至少在有序获取的情况不会很慢。固态盘(SSD),特别是采用PCIe接口,正如它们显示不同的权衡,将事情复杂化。但采用这两种获取模式带来的缓存收益是不变的。

译者注:数据流就是大量连续到达的、潜在无限的有序数据序列,这些数据按顺序存取并被读取一次或有限次。

假设我们要创建一个简单的数据库,首先从基础部分文件开始。

保持有序读和写,文件在硬件上会表现地很好。我们可以将写入的数据放入文件的末尾,可以通过扫面整个文件进行数据读取。任何我们希望的处理过程可以随着数据流穿过CPU而成真,比如过滤,聚合、甚至做一些更复杂的操作,总之非常完美。

倘如数据发生诸如更新这样的变化会怎样?

我们有多个选择,在某个位置更新这个值。我们需要利用固定长度的字段,在我们浅显的思想实验中这是没有问题的。不过在某个位置更新数据意味着随机输入输出流(IO),这会影响性能。

替代的办法是将更新值放置在文件的末尾,在读取值时对过期的数据进行处理。

我们第一次做出权衡,将“日记”或者“日志”放在文件末尾,就能保证有序获取进而提高性能。另外倘若某处需要更新数据,可以实现每秒300次左右的读取,前提是更新数据刷入底层介质中。

实际上完整的读取文件是很慢的,获取十亿字节(GB)数据,最好的硬盘也需要花费数秒,这是一个数据库全表扫描所花费的时间。

我们时常只需要一些特定的数据,比如名为“bob”的客户,这时扫描整个文件就不妥当,我们需要一个索引。

我们可用许多不同类型的索引,最简单的一种是固定长度的有序数组,比如本例中的客户名,和对应的偏移量一起存放在一个堆文件中。有序数组可以进行二进制搜索查找。同样,我们可以用树结构、位图索引、哈希索引、字典索引等。这里是一个树的结构图。

索引就像是在数据中添加了一个总览结构,值是有序排放的,这样我们就能快速获取我们想要读取的数据。但总览结构有个问题,数据进来时需要随机写。因此理想的、写优化仅仅追加文件;考虑到写会打散文件系统,这会使一切变慢。

如果你将许多索引放入一个数据库表中,那你一定熟悉这个问题。假定我们使用的机械盘,用这种方式维护某个索引的硬盘完整性,速度大约慢1000倍。

幸运的是,这里有几种解决方案。这里我们讨论三种,它们都是一些极端地例子。在现实世界中,远没有这么复杂,但在考虑海量存储时这些概念会特别有用。

译者加:

  • 第一种内存映射文件
  • 第二种较小的索引集合,采用元索引或者布隆过滤算法(Bloom Filter)做一些优化
  • 第三种简单匹配算法(brute force)又叫面向列(Column Oriented)

第一种方案是将索引放入主内存,随机写问题分隔到随机存储存储器(RAM),堆文件依旧在硬盘中。

这是一种简单但行之有效的方案,可以解决我们随机写的问题。这种方式在许多数据库中已得到应用,比如MongoDB、Cassandra、Riak、以及其他采用此优化类型的数据库,它们常常用到内存映射文件。

译者注:内存映射文件是虚拟内存单个分段,可以与文件或者类文件资源的某部分建立直接字节对字节的关联,即文件中的数据存放位置在内存中有对应的地址空间,这时对文件的读写可以直接用指针来做,而不需要read/write函数,处理大文件时可以显著提高输入输出流(IO)性能。

倘若数据量远超主内存,这种策略就失效了。特别是存在大量小的对象时,问题特别显眼;索引增长很大,最后存储越过了可用主内存的容量。多数情况下,这样做是没有问题的,但如果存在海量数据,这样做就会成为一种负担。

一种流行的方式抛开单个的“总览”索引,转而采用相对较小的索引集合。

这是一个简单的理念:数据进来,我们批量地将其写入主内存。一旦内存数据足够多,比如达到MB,我们就对它们进行排序,而后将它们作为单个小的索引写入硬盘中。最后得到的是一个小的、由不变索引文件组成的年表。

那么这样做的好处是?这些不变的文件集合被有序地流化处理,这样就能快速地写,最重要的是无需将整个索引加载入内存中。真棒!

当然它也有一个缺点,当读操作时需要询问非常多的小索引。我们将随机IO(RandomIO)写问题变为读问题。不过这确实一个很好的权衡策略,而且随机读比随机写更容易优化。

存储一个小的元索引(meta-index)在内存中或者采用布隆过滤算法(Bloom Filter),提供一种低内存方式,评估单个索引文件在读操作中是否需要被询问。即使保持快速地、有序化写操作,这种方式的读操作性能几乎可以和单个总览索引相媲美。

实际开发中,偶尔也需要清理孤子更新,但它有序读和写确实不错。

我们创建的这个结构称作日志结构合并树(Log Structured Merge Tree),这种存储方式在大数据工具中应用较大,如HBase、Cassandra、谷歌的BigTable等,它能用相对较小的内存开销平衡写、读性能。

将索引存储在内存中,或者利用诸如日志结构合并树(Log Structured Merge Tree)这样的写优化索引结构,绕开“随机写惩罚”(random-write penalty)。这是第三种方案为纯粹的简单匹配算法(Pure brute force)。

回到开始的文件例子,完整地读取它。如何处理文件中的数据,可以有许多选择。简单匹配算法(brute force)通过列而非行来存储数据,这种方法叫做面向列。

需要注意的是真实的列存储及其遵循的大表模式(Big Table pattern)之间存在一种不好的命名术语冲突。尽管它们有一些相似的地方,事实上它们是不同的,所以将它们视为不同的事情是一件明智的。

面向列是一种简单的理念,和行存储数据不同,通过列分割每一行,将数据追加到单个文件末尾。接着在每个单独的文件中存储每一列,一旦需要只需读取需要的列。

这样可以确保文件的含有相同的序列,即每个列文件的第N行含有相同的地址或者偏移量。这个很重要,在某一时刻读取多列,来服务一个单一的查询。意味着“连接(joining)”列速度飞快,倘若所有的列含有相同的序列,我们就能在一个紧凑的循环中这么做,此循环有很好缓存和CPU利用率。许多实现大量使用向量( vectorisation)进一步优化简单连接和过滤操作吞吐量。

写操作可以提高只在文件末尾追加( being append-only)性能。不利的地方是很多文件需要更新时,文件的每个列需要单独写入数据库。最常见的解决方案是采用类似日志结构合并(LSM)方式,进行批量化的写操作。许多列类型的数据库通过给表添加一个完整的序列来提升读的性能。

通过列分割数据可以极大地减少从硬盘中读取的数据量,只要查询操作在所有的列的子集中。

除此之外,单独列中的数据通常可以很好的压缩。可以利用列数据类型优势去压缩,特别是在我们熟悉列的数据类型时。这意味着我们能利用有效的、低成本的编码方式,比如行程长度编码、delta、位组合(bit-packed)等。对一些编码来说,谓词可以直接用来做压缩流。

一种简单匹配算法(brute force)特别适合大规模扫描操作,诸如平均值、最大值、最小值、分组等聚类函数就是这方面的典型。

这和先前提到的“堆文件和索引(‘heap file & index)”方式不同,很好的理解这一点可以问自己,诸如此类的列方式和每一个字段带有索引的“堆和索引”方式有什么不同?

问题的关键是索引文件序列:多路查找树(Btree)等会依据检索的字段排序,两次检索的数据连接一端涉及流操作,另一端第二个索引位置进行检索随机读取。平衡树总体上说效率低于包含两个相同序列索引列连接,我们再一次提高了序列化访问。

译者注:结论是平衡树连接性能不如两个相同序列索引列连接

我们都想将最好的技术作为数据平台控件,提升其中的某种核心功能,胜任一组特定的负载。

将索引存于内存而非堆文件为丛多非关系型数据库(NoSQL)所喜爱,比如Riak、Couchbase或者Mongodb,甚至一些关系型数据库,这种简单的模型效果不错。

设计用来处理海量数据集的工具乐意采用LSM方式,这样可以快速获取数据,得到基于硬盘结构 读一样好的性能。HBase、 Cassandra、RocksDB、 LevelDB 甚至Mongo现在也支持这种方式。

每个文件的列(Column-per-file)引擎常用于数据库大规模并行处理(MPP),比如Redshift或者Vertica,以及Hadoop stack中的Parquet。这些数据引擎最大的问题是需要大的遍历,聚合是这些工具最重要的特质。

诸如卡夫卡(Kafka)采用一个简单的、基于硬件的高效消息规范。消息可以简单地追加到文件的末尾,或者从预定的偏移量处读取。可以从某个偏移量读取消息,来来回回,你可以从上次结束的偏移量处读取。看得出是很不错的有序输入输出(IO)。

这和多数面向消息的中间件不同,JMS(Java消息服务)和AMQP(高级消息队列协议)说明文档需要额外的索引,来管理选择器和会话消息。这意味着它们结束某个行为的方式更像数据库,而非某个文件。著名的论述是1995年Jim Gray发表的队列就是数据库(Queue’s are Databases).

可见所有的方式都需要这样那样的权衡,作为一种分布式手段,使事情变得简单、硬件更加用户友好。

我们分析了存储引擎的一些核心方法,其实只是做了一些简要说明,现实世界这些是要复杂的多,不过概念确实是很有用的。分布式数据平台不仅仅是一个存储引擎,还需要考虑并行。

对于横跨多台计算机的分布式数据我们需要考虑两个核心点,分区(partition)和复制(replication)。分区有时指的是分库分表(sharding),在随机读取和简单匹配工作负载(brute force workloads)表现不俗。

如果是基于哈希的分区模型,借助哈希函数,数据就能均摊到一组机器上(译者注:理想的结果是这样的)。同哈希表工作方式相似,每个桶(bucket)盛放某个机器节点。

这样通过哈希函数,直接访问包含此数据的机器读取来数据。这是一种很经典的分布式模式,也是唯一一种随着客户端请求增加呈现线性分布的模式(译者注:简单点说就是均摊)。请求隔离到单台计算机上,由集群中的单台计算机为其服务。

利用分区提供并行批量计算,比如聚合函数或者诸如聚众或者机器学习的复杂算法。最大的不同是所有的计算机在同一时刻采用广播的方式,在很短的时间采用分治的策略解决大规模计算问题。

批量处理系统很好地处理大规模问题,但在执行过程中少有并发,容易耗尽集群资源。

两个极端且特别简单的方式:一端直接访问,另一端分治地进行广播。需要注意的是终端之间的中间地带,最好的例子就是非关系型数据库(NoSQL)中跨越多台计算机的二级索引。

二级索引有别于主键索引,这就意味着数据分区不再借助索引中的值。不再使用哈希函数直接分发,而是广播请求给所有的计算机。这会制约并发,任何一个节点与每一个请求都有关。

也是这个原因许多键值存储不愿采用二级索引,即使它的应用很广泛,Hbase和Voldemort就是如此。不过诸如MongoDb、Cassandra、Riak等数据库采用二级索引,不管咋说二级索引还是蛮有用的。但理解它们在整个系统并发的影响还是很重要的。

复制解决并发瓶颈,或许你熟悉备份,不论是异步到从服务器,还是复制到诸如Mongo或者Cassandra这样的NoSQL存储中。

实际上备份是不可见的(仅仅用于恢复)、只读(增加并发量)、或者读写(增加网络分区下的可用性),选择哪种方式需要从系统的一致性出发做出权衡。这是CAP(Consistency、Availability、Partition-Tolerance)理论的简单应用,当然CAP理论远非我们想象中的那么简单。

译者注:网络分区( network partitions)指某个网络设备出错导致网络分离,比如某个数据库挂掉。

权衡一致性给我们带来一个重要的问题,什么时候需要保证数据的一致性?

一致性的代价是昂贵,在数据库的世界里,原子性由线性化(linearisabilty)做保障,这样可以确保所有的操作有序排列.但代价也是昂贵的,实际上这完全是被禁止的,许多数据库并不将此作为一个独立(isolation)执行单元。鉴于此,很少将此设为默认值。

简而言之,你想分布式写的系统保持强一致性,系统会变慢。

注意一致性这个术语有两个应用场景,在原子性和CAP中,当然其意思是不同的。我通常采用CAP中的定义,对所有的节点而言数据在某一时刻是相同的。

解决一致性问题的方法其实很简单,就是避免它。如果无法避免,隔离它为其分配尽可能少的写操作和计算机资源。

避免一致性问题一般不难,特别是数据为不变的事实流时,网络日志集合就是一个很好的例子。无需关注一致性,因为这些日志作为事实是不会改变的。

需要一致性的用例,比如转账、使用优惠码这种非交换行为。

当然从传统的眼光看一些事情需要一致性,但实际上却也未必。比如一个行动从一个可变状态变成一个新的相关事实集合,就可以避免这种变化状态。通常是直接对新字段进行更新,考虑到标记一个事务存在潜在的欺诈,我们可以简单地利用某个事实流和原始的事务进行关联。

译者:好观点

在数据平台中移除所有一致性需求、或者隔离它都是很有用的。一种隔离方式是利用单个写原则,涉及几个方面,比如Datomic;另一种方式是拆分可变的和非可变的来隔离一致性需求。

诸如Bloom/CALM扩展了这些理念,支持默认状态下的无序概念,除非需要才做排序。因此我们有必要做一些基本的权衡,那我们如何利用这些特性去建立一个数据平台?

一个典型的应用架构或许应该是这样的:有一组处理将数据写入某个数据库,然后将其读出,对于许多简单的工作负载这是没有问题的,许多成功的应用都是基于此模式。但随着吞吐量的增加,此模式越来越难以适用;在应用领域这个问题或许可以通过消息传递、演员(actors)、负载均衡加以解决。

另外一个问题是这种方式将数据库作为一个黑盒,数据库是一个透明的软件。它们提供了海量的特征,但也提供了极少的原子拆分的机制。这样做有很多好处,默认状态下是安全的;但保护过度地扼杀我们的需求进而限制系统的分布式,这就很烦人。

命令查询职责分离(CQRS Command Query Responsibility Segregation)可以简单地解决此问题。

译注:

  • 实现一Druid
  • 实现二操作分析桥(Operational/Analytic Bridge)
  • 实现三批量管道
  • 实现四拉姆达框架(Lambda Architecture)
  • 实现五卡帕(Kappa)框架又叫流数据平台

想法其实很简单,分离读写工作负载:最佳写入状态时写入,最切贴的例子比如某个简单日志文件;最佳读取状态时读取。有多种实现方式,比如用于关系型数据库的Goldengate工具、内部复制集成的诸如MongoDB的Replica Sets这样的产品。

许多数据库底层的行为就是这样,Druid是一个不错的例子,它是一个开源的、分布式、时序化、列式分析引擎。列式存储表现不俗,特别是大规模数据录入,数据必须分散到许多文件中。为了得到更好的写性能,Druid存储近期的新数据到某个最佳写入状态中,然后逐渐转移到最佳读取存储状态。

一旦查询Druid,请求就会同时派发到最佳写和最佳读控件中,对结果进行组合(移除冗余),返回给用户。Druid借助时间标记每条记录来进行排序。

诸如此类的组合方式提供了单个抽象下的CQRS好处。

另一种相似的方式是操作分析桥(Operational/Analytic Bridge),利用单个事件流拆分最佳读以及最佳写视图。流处在一种不断变化的状态,因此异步视图可以在随后的日子里被重写和增强。

前端提供了同步读和写,这么做即可以简单快速地读取已写入的数据,又可以支持复杂的原子事务。

后端采用异步、不变状态的优势来提高性能,比如借助复制、反范式化、甚至完全不同的存储引擎扩展线下处理。前后端之间的消息桥连方便应用通过平台去监听数据流。这种模型很适合中等规模的部署,可变视图至少存在一部分、不可避免的需求。

设计不变的状态,以便容易地去支持大规模数据集和更加复杂的分析。Hadoop栈中独一无二的实现——批量管道,就是一个典型的例子。

Hadoop栈最精彩的地方就是其丛多的工具,不管是快速读写访问、还是廉价地存储、抑或批量处理、高吞吐消息、或者提取、处理、分析数据,hadoop生态体系应有尽有。

批量管道从多种资源中获取数据,将其放入HDFS,接着对其进行处理,进而提供一个原始数据持续优化的版本。

数据可能得到富集、清理、反范式化、聚集、移到一个诸如Parquet的最佳读模式,或者加载进服务器层或者数据集市,处理之后的数据可以被检索和处理。

此框架适用于不变数据、以及对数据进行大规模获取和处理,比如100太字节(TBs)。此框架处理过程很缓慢,以小时为单位。

批量管道的问题是通常我们不想等几个小时去获取一个结果。常见的做法是添加一个流层,有时又叫拉姆达框架(Lambda Architecture)。

拉姆达框架保留了批量管线,不过增加了快速流层实现迂回,就像在忙乱的小镇架了一个支路,流层采用诸如Storm、Samza流处理工具。

拉姆达框架核心是我们最乐意快速粗略作答的,但我想在最后做一个精确的回答。

流层绕过了批量层,提供了最佳回答,它的核心就在流视图中。这些会写入一个服务器层。稍好批量管道计算出精确的数据并覆盖之前的值。

用响应来平衡精度是个不错的做法,两个分支在流和批量处理层都有编码,这种模式的一些实现是有问题的。解决办法,一是将此逻辑简单抽象到一个可复用的通用库中,比如处理都写入了诸如Python、R语言这样的外源库中。二是诸如Spark这样的系统同时提供了流和批量处理功能,当然spark中的流只是少量的批处理。

因此这种模式适合比如100TB的海量数据平台,将流和已存、富集的、批量分析函数结合起来。

另外一种解决慢数据管道的方式,称之为卡帕(Kappa)框架。起初我以为这个架构名称不对,现在我不太确定。不管它是什么,我叫它流数据平台,其实这个已经有人这么叫了。

流数据平台相对批量模式更有优势:与将数据存储在HDFS中划分给新的批量任务不同,数据分散存储在消息系统或者诸如kafka日志中。批处理就变成了记录系统,数据流经过实时处理生成三层结构:视图、索引、服务或者数据集市。

与拉姆达(lambda)框架的流层相似,不一样的是没有批处理层。显然这就要求消息层能够存储、供应海量数据,并且具有强大有效的流处理器来处理此过程。

天下没有免费的午餐,问题很棘手,流数据平台运行速度并没有同等批量处理系统快多少。但将默认的方法“存储和处理”切换为“流和处理”,可以极大地提高快速获取结果的可能性。

流数据平台方式还可以用来解决“应用集成”问题,应用集成这个棘手的问题困惑Informatica、Tibco和Oracle等大的供应商好多年了。对许多数据库而言是有益的,但不是一种变革性方案。应用集成至今停留在找寻切实可行方案的话题上。

流数据平台提供了一个潜在的解决方案:利用操作分析桥的丛多优势—多种异步存储格式以及重新创建视图的能力—但这会增加已有资源中一致性需求:

系统记录变为日志,易于增强数据的不变性。诸如Kafka等产品内部保留了足够的数据量和吞吐量,将其作为历史记录来用。这就意味着回复是一个重演、重新生成状态的处理过程,而非常态化地检验。

相似的方式很在就有应用,早于最新出现的数据湖或者Goldengate等工具,后者将数据放入企业级数据仓库。复制层缺乏吞吐量和管理复杂的schema变化使此方法大打折扣。看似最后第一个问题已经解决,但作为最后一个问题,还没有定论。

~

回到局部性,读和写按序寻址,是控件内部最需要权衡的部分。我们观看了如何拓展这些控件,提高了分库分表和复制最基本的性能。重新审视一致性将其作为一个问题,在构建平台时隔离它。

不过数据平台本身需要用单一、全局的方式来平衡这些控件达到最佳状态。不断重建,从最佳写状态迁移到最佳读状态,从一致性约束转移到流、异步、不变状态的开放地带。

需要记住几件事,一是schema,二是时间、分布式、异步系统风险。但这些问题都是可控的,前提是你认真对待。未来大数据领域可能会出现这样一些新的工具、革新,逐渐掺入到平台中,解决过去和现在更多的问题。

译者注:schema 指数据库完整性约束。

   
1932 次浏览       30
相关文章

基于EA的数据库建模
数据流建模(EA指南)
“数据湖”:概念、特征、架构与案例
在线商城数据库系统设计 思路+效果
 
相关文档

Greenplum数据库基础培训
MySQL5.1性能优化方案
某电商数据中台架构实践
MySQL高扩展架构设计
相关课程

数据治理、数据架构及数据标准
MongoDB实战课程
并发、大容量、高性能数据库设计与优化
PostgreSQL数据库实战培训
最新活动计划
嵌入式软件架构设计 12-11[北京]
LLM大模型与智能体开发实战 12-18[北京]
嵌入式软件测试 12-25[北京]
AI原生应用的微服务架构 1-9[北京]
AI大模型编写高质量代码 1-14[北京]
需求分析与管理 1-22[北京]

MySQL索引背后的数据结构
MySQL性能调优与架构设计
SQL Server数据库备份与恢复
让数据库飞起来 10大DB2优化
oracle的临时表空间写满磁盘
数据库的跨平台设计
更多...   


并发、大容量、高性能数据库
高级数据库架构设计师
Hadoop原理与实践
Oracle 数据仓库
数据仓库和数据挖掘
Oracle数据库开发与管理


GE 区块链技术与实现培训
航天科工某子公司 Nodejs高级应用开发
中盛益华 卓越管理者必须具备的五项能力
某信息技术公司 Python培训
某博彩IT系统厂商 易用性测试与评估
中国邮储银行 测试成熟度模型集成(TMMI)
中物院 产品经理与产品管理
更多...