本篇主要是根据AnalyticDB的论文,来讨论AnalyticDB出现的背景,各个模块的设计,一些特性的解析。可能还会在一些点上还会穿插一些与当前业界开源实现的比对,但愿可以有一个更加深刻的探讨。OK,那咱们开始吧。html
要说AnalyticDB,那起码得知道它是干什么的。这里直接贴下百度百科的介绍:算法
AnalyticDB是阿里云自主研发的一款实时分析数据库,能够毫秒级针对千亿级数据进行即时的多维分析透视。sql
简单地说,就是实时OLAP型数据库,它的对标产品是Apache Kylin,Apache Druid,Clickhouse这些。而后AnalyticDB的特色,包括高并发实时摄入数据,兼容Mysql协议,无需预计算便可有的极快响应时间,多种数据源接入,大规模集群管理等。好吧,这几个特色都很官方,不急,接下来会逐渐讨论各个点。数据库
而后介绍下AnalyticDB的背景。json
首先先说说传统的OLAP型数据仓库,以往构建OLAP型数据仓库一般都是采用离线模式,即在晚上设置定时任务将前一天的数据同步到数据仓库中,次日数据分析师或报表工具就能够根据数据产出分析结果。但这样的问题是数据延迟过高了,商业瞬息万变,可能今天线上出现了什么订单激增的状况,数据分析师却要等明天才能进行分析,这谁受得了呀。因此近几年的趋势就是实时数仓,简单说就是增长一个实时接收数据以供查询的模块,这也叫作lambda架构。如图,就是用一个Batch层和一个Real-time层共同提供查询结果。[1]后端
好像有点扯远了,说回AnalyticDB,它就是在大背景下提出的,因此它的一个主要特性就是实时。而后因为它自己是云原生的结构,也就是自己就是根植于阿里云上面的,面向的客户更加普遍,因此是有通用性的要求的。好比传统企业都是使用Mysql,Postgresql等关系型数据库,这些企业也没有人力去搭建和维护Hadoop和Kylin,Druid这些集群。而Postgresql这类关系型数据库可能会有对复杂结构对支持,好比json,vector等,因此AnalyticDB也提供了对这种复杂类型的支持。数组
在性能方面,AnalyticDB维持全部列的索引,用以快速检索数据。在存储方面,使用行-列混合存储,使得AnalyticDB能够同时对OLAP分析和行级查询快速响应。而后为了高并发的查询和高吞吐的写入,又提出了读,写分离。这几个性能方面的特性,以及这些优化如何与实时查询结合起来,在后面会详细介绍。缓存
总而言之,目前业界对海量数据的OLAP分析查询方案无非两种,经过预计算构建多维立方体,在查询的时候直接读取预计算好的数据作一些简单的合并(由于分区存储)而后返回给用户。这种类型的表明是Kylin和Druid,它们的好处是比较简单,OLAP分析查询速度很快,缺点是不够灵活,好比Kylin一点改动可能就要所有数据rebuild。服务器
另外一种是非预计算,充分利用各类资源(CPU,内存,列存储,向量化执行),或是架构尽可能优化(如AnalyticDB),来让海量数据快速查询获得结果。比较典型的表明是Clickhouse,查询性能不赖,也相对灵活,但缺点是集群数据量无法拓展到很大。网络
这两种方案都有办法这实时这个点上进行拓展,只是实现思路也不大同样。第一种是添加一个流式层,OLAP查询的时候分别查询历史数据和流式层数据而后合并返回。第二种则是用微批方式倒入数据仓库中实现流式查询。
总体上,AnalyticDB更加偏向于第二种非预计算的方式实现,不过在不少设计上还考虑了行级查询的实现和性能,因此要比Clickhouse这种要复杂一些。下面咱们从几个方面来讨论它的实现。
AnalyticDB是一个可以在PB数据集上高并发,低延迟,实时分析查询,而且可以在2000+云服务器上运行的OLAP数据库。在设计上有多个挑战,须要兼顾多种查询类型的性能要求。这里的多种情境包括全表扫描分析,多表join的点查询操做,多个列的多个筛选条件等等,而这些操做又难以优化。
第二个挑战是要设计一个底层存储,应对不一样类型的查询所须要的不一样存储结构。好比OLAP查询须要列式存储,而点查询(行级查询)须要行式存储。如何将这两种存储结构(列式,行式)结合起来以供不一样查询类型使用,同时还须要考虑到复杂类型,json,vector,text等,这也是一个难点。
第三个是实时方面的,要如何作到每秒数百万数据写入吞吐的同时,呈现给用户低延迟的查询响应和数据延迟。之前的作法将读写操做交由同一进程处理,但这样一来读写操做的性能是互斥的,即高吞吐的写入会影响到查询性能和数据延迟[2]。
为了解决上述挑战,AnalyticDB引入如下特性:
这些特性暂时就有个映像就好,后面会详细对这部分阐述。
要说这块,咱们先来看看总体的架构设计图。
前面与说到,AnalyticDB是云原生的,AnalyticDB主要依赖于两个外部结构,任务管理与调度组件Fuxi,和分布式存储系统Pangu,而这几个组件又都是基于阿里云的Apsara(负责管理底层的物理主机并向上层提供服务)。
总体架构上看仍是比较简单的,主要就是对外提供JDBC/ODBC接口,内部由多个协调器(Coordinator)负责统一管理写节点和读节点(读写分离)。
在具体的处理流程中,Fuxi资源分配和异步调度(相似yarn)。而数据计算则是使用管道的方式进行计算,以下图:
图中,数据按页(Page)进行切分(Pangu的存储特性),数据处理以管道的方式进行处理,且数据流转的不一样阶段均在内存中执行。看这张图其实有点像Spark的数据处理流程,固然AnalyticDB自己也是使用DAG模型进行数据处理。
不过按照论文中说的数据彻底在内存中处理仍是有点不现实,虽然这样能极大提升处理的效率,但遇到数据量太大致使内存装不下的状况,仍是须要暂时落到磁盘上,就相似Spark有提供多种persist方案同样。不然查询的并发量势必会受到一些影响,但这样一来可能查询响应又下降了,鱼与熊掌不可兼得啊。
在一开始建立表的时候,能够分配数据按照两级分区进行存储,这里经过论文中的小例子阐述两级分区的实现,如如下建表语句:
CREATE TABLE db_name.table_name ( id int, city varchar, dob date, primary key (id) ) PARTITION BY HASH KEY(id) PARTITION NUM 50 SUBPARTITION BY LIST (dob) SUBPARTITION OPTIONS (available_partition_num = 12);
第一级索引,可让数据按照指定列进行hash分区以及指定分区数,好比上述建表语句指定一级索引为id,分区数是50个。这样可让数据根据id的hash值分布到不一样的50个分区中,这一列一般是使用高基数的列,诸如用户Id等。
第二级索引(SUBPARTITION,可选)能够针对某个列指定最大分区数,用来对数据保留和回收,一般使用日期类型数据。好比若是指定按天进行分区,最大分区为12,那么数据仅会保留12天内的数据。
大多数传统的OLAP数据库,都是使用一个线程负责处理用户SQL的操做,无论是写请求(Insert)仍是读请求(Select)。这在查询和写入的并发量都很高的状况下会出现资源争用的状况,针对这种状况AnalyticDB提出读写分离的解决方案。写节点负责写,读节点负责读数据,两种节点彼此分离,这样就避免了高并发场景下读写资源互斥的状况。
写节点主要是master和worker架构,由zookeeper进行协调管理。写master节点负责分配一张表的分区给不一样的写worker节点。在一个SQL到达的时候,Coordinators会首先识别是读仍是写SQL语句,如果写,那么会先发送到对应的写worker节点,写worker节点先将数据存到内存,按期以日志的形式持久化到Pangu中造成Pangu日志。当日志必定规模的时候,才会构建真正的数据和全量索引。
而对于读节点,一样每一个节点会被实现分配不一样的分区。功能上,AnalyticDB有两种读模式,实时读取(real-time read),写入数据当即可读,和延迟读(boundedstaleness),即容忍必定时间的写入数据延迟。延迟读是默认采用的方式,虽然与必定数据延迟,但查询响应更快,一般而言也足够了。
而实时读,那么能够当即查询到刚刚写入的数据。之因此能这么快,其中一个缘由是读节点会直接从写节点中获取更新数据,也就是说写节点在某种程度上说充当了缓存。其余的OLAP数据库的作法一般有两种,一种是用一个segment专门存储实时数据,OLAP查询的时候,会扫描实时segment和离线数据,合并后返回用户,好比最新的kylin streaming就是这样实现。一种是微批导入数据到存储引擎中,而后用以检索,这样的话写入频率(微批的间隔)会大大影响检索性能。AnalyticDB的方式能够说是一种比较新颖的方式,借助读写分离的架构和强大的索引能力(下面介绍),能够实现实时写入且低延迟检索。
不过其实这会面临一个问题,数据同步的一致性问题(读写节点数据不一致),AnalyticDB是怎么作的呢?这里也不卖关子,主要是使用一个版本号来处理。Coordinators在分发写请求给写节点,写节点更新后会返回更新后的分区版本号给Coordinators。Coordinators分发读请求给读节点时,也会带上这一个分区版本号,读节点就会与本身缓存的版本号对比,发现本身小的话,就会去拉取写节点的最新数据(写节点有必定的缓存功能)。
能够发现,经过读写分离的机制,以及预先分配好读/写节点的数据分区(hash),能提升数据处理的并行度,而且减小数据计算产生的数据传输网络开销,好比join的shuffle操做就不须要进行大规模的数据再分区。然后有可以将两种请求相互解耦,每种操做关心自身就能够,方便之后的拓展。
OK,到这里系统的架构,数据分区,读写流程就差很少说完了,接下来再讨论下它的其余特性。
先说下背景,OLAP查询通常会有全表扫描操做,因此主流作法是使用列式存储,由于列式存储能够极大减小磁盘IO操做,提升提升全表扫描性能,但这种对点查询(即行级别)查询和更新等不甚友好。而如Mysql这种行级存储,点查询方便,但OLAP操做又会又额外更多开销(数据压缩比低)。许多主流系统的作法是,基本摒弃另外一种功能,好比Mysql不适合作大规模OLAP查询,Kylin,或者说hive这种不支持行级别更新(特殊状况下能够,但支持很差),Druid则更加极致,直接就不存明细了。
而AnalyticDB却经过行-列混合存储结构,不只兼顾OLAP分析和点查询,还实现了复杂类型的存储(json,vector)。不过在介绍它的行-列混合存储结前,先来看看流行的列式存储结构,而后再引出AnalyticDB的行列混合。
咱们以开源的列式存储结构Parquet为例来看列式存储是怎么存储数据的。
Parquet自己是hadoop底层使用的存储引擎,其强大毋庸置疑。所谓列式存储,能够简单理解成就是将一整列数据压缩打包,而后按顺序存储。
存储中有三级结构:
在最后是Footer模块,这里存储的是数据的元数据信息,好比列名,列的类型。还有一些统计信息,min,max,用以提高部分检索的效率。同时Parquet也支持复杂类型的存储,说简单点就是将复杂类型Map,List等转换成schema树,把树的叶子节点当作列数据存储。
简单了解了列式存储,咱们再来看AnalyticDB的行-列混合存储。
注意图中左右两部分分别是两个文件,左边的是元数据文件,存储诸如字段名,一些简单的统计信息帮助过滤,这个文件比较小一般驻存在内存中。这部份内容和前面的Parquet的Footer存储内容相似,这里就很少介绍了。主要仍是介绍下右边部分,即数据存储方式。
图片右边,数据以row group的形式存储,每一个row group中存储固定数量的行。可是在row group中依旧采用列式存储,即同一列的被存储到一块儿,称为Data bolck,全部的Data block按顺序存储(这点和列式存储同样)。而Data block是最小的操做单元(缓存,读取等)。注意这里不像Parquet那样,每个Data block再分多个Page。
看上去,它的存储结构和Parquet是相似的,只是没有再将Data block划分红多个Page,这里论文没和Parquet对比,也没论述很清楚。不过最主要的区别应该就是这里了。为何Data block不须要再划分?由于它没那么多数据呀,在Parquet里,一个row group的数据量是GB级别的,因此一个row group中的列须要再划分。而AnalyticDB中,它的row group明显是小数量级的,可能一个row group仅仅是MB级别的数据量。这一点细微的差异,使AnalyticDB在点查询的时候就能够直接几MB内获取一行所有数据,而Parquet可能须要在1G内才能获取一行数据。这也是为何AnalyticDB的叫作行-列混合存储结构。
对比Parquet和AnalyticDB,它们的设计分歧多是天生的,Hadoop适合存储追加的数据,以及非结构化数据,它的场景更可能是在大数据存储和加载,因此不会考虑单行查询的场景。而AnalyticDB要考虑各类检索,因此设计上就会要差别。固然AnalyticDB这样也不是没有缺点,好比它在全表扫描性能会有所降低。
说完AnalyticDB的存储结构,再来讲说AnalyticDB如何存储复杂类型数据。
复杂类型数据(json,vector)存储有个难点,这种复杂类型的数据一般大小是不定的,并且每每会出乎意料的大。若是按照上面提到row group的方式,可能一个block entry会很是大,因此须要一种其余类型的存储结构来存储复杂类型数据。
具体的作法能够说借鉴了hadoop的存储思路。既然复杂类型数据大小不同,可能大可能小,那就将数据统一用32KB大小的块组织起来,称为FBlock。一个复杂类型数据可能分散在多个FBlock中(超过32KB),多个FBlock按顺序存储。而后使用稀疏索引,方便快速查询。这样的设计无疑能够方便得将复杂数据进行存储,同时经过稀疏索引又能在必定程度上保证检索的速度。
最后再说说如何支持update和delete操做。
通常的列式存储是不怎么支持行级更新和删除操做的,由于数据都是压缩成二进制进行存储,若是支持行级更新,那并你须要先解压缩,整块数据,而后删除数据,再压缩存储。要是并发量一上来那简直是灾难。
那hbase的底层是hadoop,它是怎样实现更新和删除的呢?这是由于hbase使用LSM-tree,我以前也过一篇介绍这个东西,也兴趣能够看看数据的存储结构浅析LSM-Tree和B-tree。粗略说就是将更新和删除操做都按key-value的形式追加到文件末尾,而后整个文件按期去重,只保留最新的key的数据,旧的key数据就被删除了。检索的时候若是也多个key,只会认最新的那个key的数据。固然具体细节要复杂得多。
AnalyticDB也是相似的思想,不过作了一些改变。它使用一个存储在内存中的bit-set结构,记录更新和删除的数据id以及对应版本号。同时使用copy-on-write(写时复制)技术提供多版本支持。更新和删除操做都会改变版本号,而后查询的时候会提供一个版本号去查找对应的更新和删除信息,而后在查询结果中和结果进行合并。这样就实现了更新和删除操做。
稍稍总结下AnalyticDB的存储结构,行-列混合存储的优点确实是也的,它算是牺牲一部分OLAP查询的性能,换取一些灵活性。而这样的换取,使得它拥有快速行级检索,更新删除的能力,对AnalyticDB而言是值得的。
索引能够说是一个数据库系统中极为重要的优化设计。目前主流的索引,包括B+tree,倒排索引,稀疏索引等等,但它们都有各类局限,好比B+tree插入分裂代价太大,倒排索引只支持特定类型,有些索引虽然能提供快速检索的能力但对写入性能有负担。那么AnalyticDB的索引是怎样实现的呢?
AnalyticDB重度使用倒排索引加速检索效率,首先,AnalyticDB对每一列都创建一个倒排索引,索引的key是列的值,索引的值的列的行号。前面的存储结构中能够看到,每一个row group存储的是固定的行数,因此能够快速检索到对应的行。而针对不一样的数据量特色,提供了bitmap和int array两种结构存储倒排索引,达到必定阈值的时候会作相应转化。
而针对复杂类型数据(三种,json,full text,vector),仍是经过倒排提供支持,只是针对不一样类型作了不一样的优化改动。
先说json,以往查询json数据的作法,是要先读取json而后解析再而后查询,这样效率很低。AnalyticDB采用空间换时间的思路,将json数据先解析,而后对每一个列构建倒排索引(和单列倒排索引相似)。在查询的时候就能够直接根据索引快速定位到对应的json。
full-text的索引方式应该是和ElasticSearch相似的,即词到整个文档的倒排索引,查询时还会按TF/IDF评分将结果返回给用户(ES也是这样)。
第三种类型是vector类型数据,主要采用NNS(nearest neighbour search)方法来加速查询(看名字和KNN算法有点像),大意也是用相似计算临近数据的方式加速检索。
对于增量数据,因为数据是落盘到磁盘上才构建全量索引,索引增量数据和已经落盘的数据有检索性能的区别,因此须要对增量数据额外构建索引来弥补这种差距。而AnalyticDB对增量数据构建的是排序索引。所谓排序索引,本质上是一个数组,存储的是数据的id。具体原理比较难解释清楚,能够理解为就是存储排序后的数据的id。经过排序索引能够将全表检索的复杂度从O(n)下降到O(log n),也是一种空间换时间的思路了。
说完AnalyticDB,咱们来对比下其余索引结构。Apache Kylin就没必要多说了,基本就是依托于Hbase的row-key索引机制,算是比较弱的索引机制。
和AnalyticDB比较像是应该算是Druid,也是倒排索引,不过是bitmap结构存储的倒排索引,它的倒排索引是通过优化的,叫Roaring bitmap,能够规避存储小数据时候的存储空间问题。相比于AnalyticDB的大而全的索引,Druid能够说是小而美。只对维度数据存储bitmap索引,而且是和数据一块儿存储在文件中,而非AnalyticDB那样数据和索引分开存。出现这样的缘由一个是场景上,Druid毕竟是面向OLAP查询的,索引它只须要对维度索引构建就行。这样的好处在于实现简单,存储也不会占用太多空间。而针对单一OLAP场景,其实这样也已经足够了。
总而言之言而总之,AnalyticDB由于其是云原生,底层存储,资源调度等都是依托于阿里云的其余服务,因此它开源出来是不现实的(毕竟人家还靠这个赚钱),哪怕真的开源,使用者使用开源存储和资源调度方案估计也难以作到它在阿里云生态上那么好。
不过它的架构和一些特性仍是颇有借鉴意义的,好比读写分离,预先分区,还有行-列混合存储,强大的索引机制,索引机制如何跟底层的存储相互配合等,这些东西目前开源的一些系统可能尚未或没AnalyticDB那么完善。一方面多是由于这些东西实现起来后,配置上会比较复杂,阿里云的东西不怕复杂,由于后端对用户是不可见的。要是开源系统搞得特别复杂,工程师们就不大会想用这些东西。毕竟为了一些可能用不着的性能提高,引入一个后续可能维护复杂的系统,是否值得也是须要权衡的。
整体上看,AnalyticDB仍是走在了业界的前头的,好像也经过了TPC-DS的全流程测试,算是将来可期。将来开源数据库的方向会不会从分化走上AnalyticDB这种全面的道路呢?
以上~