1. 程式人生 > >Alluxio及presto的淺知

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的應用介紹:

***********************************************************************************************************************************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中獲取本次查詢的計算結果,直到獲得了所有的計算結果,並不是等到所有的查詢結果都產生完畢之後一次全部顯示出來,而是每產生一部分,就會顯示一部分,直到所有的查詢結果都顯示完畢。