Alluxio及presto的浅知

Alluxio(之前名为Tachyon)是世界上第一个以内存为中心的虚拟的分布式存储系统。它统一了数据访问的方式,为上层计算框架和底层存储系统构建了桥梁。 应用只需要连接Alluxio即可访问存储在底层任意存储系统中的数据。此外,Alluxio的以内存为中心的架构使得数据的访问速度能比现有方案快几个数量级。

在大数据生态系统中,Alluxio介于计算框架(如Apache Spark,Apache MapReduce,Apache HBase,Apache Hive,Apache Flink)和现有的存储系统(如Amazon S3,OpenStack Swift,GlusterFS,HDFS,MaprFS,Ceph,NFS,OSS)之间。 Alluxio为大数据软件栈带来了显著的性能提升。例如,百度采用Alluxio使他们数据分析流水线的吞吐量提升了30倍。 巴克莱银行使用Alluxio将他们的作业分析的耗时从小时级降到秒级。 去哪儿网基于Alluxio进行实时数据分析。 除性能外,Alluxio为新型大数据应用作用于传统存储系统的数据建立了桥梁。 用户可以以 独立集群模式,在例如 Amazon EC2, Google Compute Engine运行Alluxio, 或者用 Apache Mesos或 Apache Yarn安装Alluxio。

Alluxio与Hadoop是兼容的。现有的数据分析应用,如Spark和MapReduce程序,可以不修改代码直接在Alluxio上运行。

现有功能

  • Alluxio存储 Alluxio可以管理内存和本地存储如SSD 和HDD,以加速数据访问。如果需要更细粒度的控制,分层存储功能可以用于自动管理不同层之间的数据,保证热数据在更快的存储层上。 自定义策略可以方便地加入Alluxio,而且pin的概念允许用户直接控制数据的存放位置。

  • 统一命名空间 Alluxio通过挂载功能在不同的存储系统之间实现高效的数据管理。并且,透明命名在持久化这些对象到底层存储系统时可以保留这些对象的文件名和目录层次结构。

  • 世系(Lineage) 通过世系(Lineage),Alluxio可以不受容错的限制实现高吞吐的写入, 丢失的输出可以通过重新执行创建这一输出的任务来恢复。应用将输出写入内存,Alluxio以异步方式定期备份数据到底层 文件系统。写入失败时,Alluxio启动任务重执行恢复丢失的文件。

  • 网页UI & 命令行 用户可以通过网页UI浏览文件 系统。在调试模式下,管理员可以查看每一个文件的详细信息,包括存放位置,检查点路径等等。用户也可以通 过./bin/alluxio fs与Alluxio交互,例如:将数据从文件系统拷入拷出

Alluxio在JD的应用介绍:

https://blog.csdn.net/maobaolong/article/details/79337538

***********************************************************************************************************************************8

presto介绍

Presto是一个开源的分布式SQL查询引擎,适用于交互式分析查询,数据量支持GB到PB字节。查询语言是类ANSI SQL语句。笔者在多个项目中用到Presto做即席查询,总结了一些优化措施。

一、数据存储
合理设置分区
与Hive类似,Presto会根据元信息读取分区数据,合理的分区能减少Presto数据读取量,提升查询性能。
使用列式存储
Presto对ORC文件读取做了特定优化,因此在Hive中创建Presto使用的表时,建议采用ORC格式存储。相对于Parquet,Presto对ORC支持更好。
使用压缩
数据压缩可以减少节点间数据传输对IO带宽压力,对于即席查询需要快速解压,建议采用snappy压缩
预先排序
对于已经排序的数据,在查询的数据过滤阶段,ORC格式支持跳过读取不必要的数据。比如对于经常需要过滤的字段可以预先排序。
INSERT INTO table nation_orc partition(p) SELECT * FROM nation SORT BY n_name;
如果需要过滤n_name字段,则性能将提升。

SELECT count(*) FROM nation_orc WHERE n_name=’AUSTRALIA’;
二、查询SQL优化
只选择使用必要的字段
由于采用列式存储,选择需要的字段可加快字段的读取、减少数据量。避免采用*读取所有字段。
[GOOD]: SELECT time,user,host FROM tbl
[BAD]:  SELECT * FROM tbl
过滤条件必须加上分区字段
对于有分区的表,where语句中优先使用分区字段进行过滤。acct_day是分区字段,visit_time是具体访问时间
[GOOD]: SELECT time,user,host FROM tbl where acct_day=20171101
[BAD]:  SELECT * FROM tbl where visit_time=20171101
Group By语句优化
合理安排Group by语句中字段顺序对性能有一定提升。将Group By语句中字段按照每个字段distinct数据多少进行降序排列。
[GOOD]: SELECT GROUP BY uid, gender
[BAD]:  SELECT GROUP BY gender, uid
Order by时使用Limit
Order by需要扫描数据到单个worker节点进行排序,导致单个worker需要大量内存。如果是查询Top N或者Bottom N,使用limit可减少排序计算和内存压力。
[GOOD]: SELECT * FROM tbl ORDER BY time LIMIT 100
[BAD]:  SELECT * FROM tbl ORDER BY time
使用近似聚合函数
Presto有一些近似聚合函数,对于允许有少量误差的查询场景,使用这些函数对查询性能有大幅提升。比如使用approx_distinct() 函数比Count(distinct x)有大概2.3%的误差。
SELECT approx_distinct(user_id) FROM access
用regexp_like代替多个like语句
Presto查询优化器没有对多个like语句进行优化,使用regexp_like对性能有较大提升
[GOOD]
SELECT
  ...
FROM
  access
WHERE
  regexp_like(method, 'GET|POST|PUT|DELETE')
 
[BAD]
SELECT
  ...
FROM
  access
WHERE
  method LIKE '%GET%' OR
  method LIKE '%POST%' OR
  method LIKE '%PUT%' OR
  method LIKE '%DELETE%'
使用Join语句时将大表放在左边
Presto中join的默认算法是broadcast join,即将join左边的表分割到多个worker,然后将join右边的表数据整个复制一份发送到每个worker进行计算。如果右边的表数据量太大,则可能会报内存溢出错误。
[GOOD] SELECT ... FROM large_table l join small_table s on l.id = s.id
[BAD] SELECT ... FROM small_table s join large_table l on l.id = s.id
使用Rank函数代替row_number函数来获取Top N
在进行一些分组排序场景时,使用rank函数性能更好
[GOOD]
SELECT checksum(rnk)
FROM (
  SELECT rank() OVER (PARTITION BY l_orderkey, l_partkey ORDER BY l_shipdate DESC) AS rnk
  FROM lineitem
) t
WHERE rnk = 1

[BAD]
SELECT checksum(rnk)
FROM (
  SELECT row_number() OVER (PARTITION BY l_orderkey, l_partkey ORDER BY l_shipdate DESC) AS rnk
  FROM lineitem
) t
WHERE rnk = 1
三、无缝替换Hive表
如果之前的hive表没有用到ORC和snappy,那么怎么无缝替换而不影响线上的应用:
比如如下一个hive表:

CREATE TABLE bdc_dm.res_category(
channel_id1 int comment '1级渠道id',
province string COMMENT '省',
city string comment '市',
uv int comment 'uv'
)
comment 'example'
partitioned by (landing_date int COMMENT '日期:yyyymmdd')
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' COLLECTION ITEMS TERMINATED BY ',' MAP KEYS TERMINATED BY ':' LINES TERMINATED BY '\n';
建立对应的orc表

CREATE TABLE bdc_dm.res_category_orc(
channel_id1 int comment '1级渠道id',
province string COMMENT '省',
city string comment '市',
uv int comment 'uv'
)
comment 'example'
partitioned by (landing_date int COMMENT '日期:yyyymmdd')
row format delimited fields terminated by '\t'
stored as orc
TBLPROPERTIES ("orc.compress"="SNAPPY");
先将数据灌入orc表,然后更换表名

insert overwrite table bdc_dm.res_category_orc partition(landing_date)
select * from bdc_dm.res_category where landing_date >= 20171001;

ALTER TABLE bdc_dm.res_category RENAME TO bdc_dm.res_category_tmp;
ALTER TABLE bdc_dm.res_category_orc RENAME TO bdc_dm.res_category;
其中res_category_tmp是一个备份表,若线上运行一段时间后没有出现问题,则可以删除该表。

四、注意事项
ORC和Parquet都支持列式存储,但是ORC对Presto支持更好(Parquet对Impala支持更好)
对于列式存储而言,存储文件为二进制的,对于经常增删字段的表,建议不要使用列式存储(修改文件元数据代价大)。对比数据仓库,dwd层建议不要使用ORC,而dm层则建议使用

presto基本概念

1、1 presto服务进程 
presto集群中一共有两种服务器进程:coordinator服务进程和worker服务进程,其中coordinator服务进程的主要作用是:接收查询请求、解析查询语句、生成查询执行计划、任务调度和worker管理。而worker服务进程则执行被分解后的查询执行任务:task 


coordinator 
coordinator服务进程部署于集群中一个单独的节点上,是整个presto集群的管理节点,coordinator服务进程主要用于接收客户端提交的查询,查询语句解析,生成查询执行计划、stage和task并对生成的task进行调度。除此之外,coordinator还对集群中的所有worker进行管理,coordinator进程是整个presto集群的master进程,该进程既与worker进行通信从而获得最新的worker信息,又与client进行通信,从而接受查询请求,而所有的这些工作都是通过coordinator上的statementResource类提供的RESTful服务来完成的。 
worker 
在一个presto集群中,存在一个coordinator节点和多个worker节点,coordinator节点是管理节点,而worker节点就是工作节点,在每个worker节点上都会存在一个worker服务进程,该服务进程主要进行数据的处理以及task的执行,worker服务进程每隔一定的时间都会向coordinator上的RESTful服务发送心跳,从而告诉coordinator:我还活着,并接受你调度,当客户端提交一个查询的时候,coordinator则会从当前存活的worker列表中选择出适合的worker节点去运行task,而worker在执行每个task的时候又会进一步对当前task读入的每个split进行一系列的操作和处理

1、2 presto查询执行模型 
presto在执行SQL语句时,将这些SQL语句解析成相应的查询,并在分布式集群中执行这些查询 
statement 
statement语句其实就是指我们输入的SQL语句,presto支持符合ANSI标准的SQL语句,这种语句由子句(Clause)、表达式(Expression)和断言(predicate)组成、 
presto为什么将语句(statement)和查询(query)的概念分开呢? 
因为在presto中,语句和查询本身就是不同的概念,语句指的是终端用户输入的用文字表示的SQL的语句,当presto执行输入的SQL语句时,会根据SQL语句生成查询执行计划,进而生成可以执行的查询(Query),而查询代表的是分布到所有的worker之间执行的实际查询操作 
query 
query即查询执行,当presto接收一个SQL语句并执行时,会解析该SQL语句将其转变成一个查询执行和相关的查询执行计划,一个查询执行代表可以在presto集群中运行的查询,是由运行在各个worker上且各自之间相互关联的阶段(stage)组成的 
那么SQL语句与查询执行之间有什么不同? 
其实很简单,你可以认为SQL语句就是提交给presto用文字表示的SQL执行语句,而查询执行则是为了完成SQL语句所表述的查询而实例化的配置信息、组件、查询执行计划和优化信息等。一个查询执行由stage、task、driver、split、operator和DataSource组成。这些组件之间通过内部联系共同组成一个查询执行,从而得到SQL语句表述的查询,并得到相应的结果集。 
stage 
stage即 查询执行阶段,当presto运行query时。presto会将一个query拆分成具有层级关系的多个stage,一个stage就代表查询执行的一部分,例如,当我们执行一个查询,从hive的一张表具有1亿记录的表中查询数据并进行聚合操作时,presto会创建一个Root Stage,该stage聚合其上游stage的输出数据,然后将结果输出给coordinator,并由coordinator将结果输出给终端用户。 
通常情况下,stage之间是树状的层级结构,每个query都有一个Root Stage,该stage用于聚集所有其他stage的输出数据,并将最终的数据反馈给终端用户,需要注意的是,stage并不会再集群中实际执行,它只是coordinator用于对查询执行计划进行管理和建模的逻辑概念,每个stage(除了single stage和source stage)都会有输入和输出,都会从上游stage读取数据,然后将产生结果输出给下游stage,需要注意的是,source stage没有上游stage,它从coordinator获取数据。single没有下游,它的结果直接输出给coordinator,并由coordinator输出给终端用户。

presto中的stage共分为4种,具体介绍如下 
coordinator_Only:这种类型的stage用于执行DDL或者DML语句中最终的表结果创建或者更改。 
single:这种类型的stage用于聚合子stage的输出数据,并将最终数据输出给终端用户 
fixed:这种类型的stage用于接受其子stage产生的数据并在集群中对这些数据进行分布式的聚合或者分组计算。 
source:这种类型的stage用于直接连接数据源,从数据源读取数据,在读取数据的时候,该阶段也会根据presto对查询执行计划的优化完成相关的断言下发和条件过滤等。 
exchange 
exchange的字面意思就是交换,presto的stage是通过exchange来连接另一个stage的,exchange用于完成有上下游关系的stage之间的数据交换,在presto中有两种exchange:output Buffer和exchange client,生产数据的stage通过名为output buffer的exchange将数据传送给其下游的stage。(根据数据的流向,分为上下游,你可以将presto中的查询执行过程中的数据比喻成一条河流,那么产生数据的stage对于消费数据的stage来说,就是上游)消费数据的stage通过名为exchange client的exchange从上游stage读取数据。 
如果当期的stage是source类型的stage。那么该stage则是直接通过相应的connector从数据源读取数据的,而该stage则是通过名为source operator的operator与connector进行交互的,例如,如果一个source stage直接从HDFS获取数据,那么这种操作不是通过exchange client来完成的,而是通过运行于driver中的source operator来完成的。 
task 
从前面的章节我们可以知道,stage并不会在presto集群中实际运行,它仅代表针对于一个SQL语句查询执行计划中的一部分查询的执行过程,只是 用于对查询执行计划进行管理和建模,stage在逻辑上又被分为一系列的task,这些task则是需要实际运行在presto的各个worker节点上的, 
在presto集群中,一个查询执行被分解成具有层级关系的一系列的stage。一个stage又被分为一系列的task,每个task处理一个或者多个split,每个task都有相应的输入和输出,一个stage被分解成多个task,从而可以并行的执行一个stage,task也采用了相同的机制,一个task也可以被分解为一个或者多个driver,从而并行地执行一个task。 
driver 
一个task包含一个或者多个driver,一个driver其实就是作用于一个split的一系列operator的集合,因此一个driver用于处理一个split,并且生成相应的输出,这些输出由task收集并且传送给其下游stage中的一个task,一个driver拥有一个输入和一个输出 
split 
split即分片,一个分片其实就是一个大的数据集中的一个小的子集,而driver则是作用于一个分片上的一系列操作的集合,而每个节点上运行的task,又包含多个driver,从而一个task可以处理多个split,其中每一种操作均由一个operator表示,分布式查询执行计划的源stage通过connector从数据源获得多个分片,source stage对split处理完毕之后,会将输出传递给其下游stage, 
当presto执行一个查询的时候,首先会从coordinator得到一个表对应的所有split,然后presto就会根据查询执行计划,选择合适的节点运行相应的task处理split 
page 
page是presto中处理的最小数据单元,一个page对象包含多个block对象,而每个block对象是一个字节数组,存储一个字段的若干行,多个block横切的一行的真实的一行数据,一个page最大为1MB,最多16*1024行数据,

presto中执行 一个查询一共分为7步:

1、客户端通过HTTP协议发送一个查询语句给presto集群的coordinator 
2、coordinator接到客户端传递过来的查询语句,会对该查询语句进行解析,生成查询执行计划,并根据查询执行计划依次生成SqlQueryExecution、sqlStageExecution、HttpRemoteTask。coordinator会根据数据本地性生成对应的HttpRemoteTask 
3、coordinator将每个task都分发到其所需要处理的数据所在的worker上进行执行,这个过程是通过HttpRemoteTask中的HTTPClient将创建或者更新task的请求发给数据所在节点上TaskResource所提供的RESTful接口,TaskResource接收到请求之后最终会在对应的worker上启动一个sqlTaskExecution对象或者更新对应的sqlTaskExecution对象需要处理的split。 
4、执行处于上游的source stage中的task,这些task通过各种connector从相应的数据源中读取所需要的数据 
5、处于上游stage中的task会读取上游stage产生的输出结果,并在该stage每个task所在worker的内存中进行后续的计算和处理 
6、coordinator从分发task之后,就会一直持续不断地从single stage中的task获取计算结果,并将计算结果缓存到buffer中,直到所有计算结束 
7、client从提交查询语句之后,就会不停地从coordinator中获取本次查询的计算结果,直到获得了所有的计算结果,并不是等到所有的查询结果都产生完毕之后一次全部显示出来,而是每产生一部分,就会显示一部分,直到所有的查询结果都显示完毕。

JD-Presto的介绍https://blog.csdn.net/broadview2006/article/details/80125131

猜你喜欢

转载自blog.csdn.net/u010486679/article/details/81588605