Presto的學習與介紹

一 、Presto簡介

1.1 Presto概念

Presto是一個開源的分佈式SQL查詢引擎,適用於交互式分析查詢,數據量支持GB到PB字節。
Presto的設計和編寫完全是爲了解決像Facebook這樣規模的商業數據倉庫的交互式分析和處理速度的問題。
注意:雖然Presto可以解析SQL,但它不是一個標準的數據庫。不是MySQL、Oracle的代替品,也不能用來處理在線事務(OLTP)。

1.2 Presto應用場景

Presto支持在線數據查詢,包括Hive,關係數據庫(MySQL、Oracle)以及專有數據存儲。
一條Presto查詢可以將多個數據源的數據進行合併,可以跨越整個組織進行分析。
Presto主要用來處理響應時間小於1秒到幾分鐘的場景。

1.3 Presto架構

Presto是一個運行在多臺服務器上的分佈式系統。完整安裝包括一個Coordinator和多個Worker。由客戶端提交查詢,從Presto命令行CLI提交到Coordinator。Coordinator進行解析,分析並執行查詢計劃,然後分發處理隊列到Worker。
在這裏插入圖片描述
Presto有兩類服務器:Coordinator和Worker。
1)Coordinator
Coordinator服務器是用來解析語句,執行計劃分析和管理Presto的Worker結點。Presto安裝必須有一個Coordinator和多個Worker。如果用於開發環境和測試,則一個Presto實例可以同時擔任這兩個角色。
Coordinator跟蹤每個Work的活動情況並協調查詢語句的執行。Coordinator爲每個查詢建立模型,模型包含多個Stage,每個Stage再轉爲Task分發到不同的Worker上執行。
Coordinator與Worker、Client通信是通過REST API。
2)Worker
Worker是負責執行任務和處理數據。Worker從Connector獲取數據。Worker之間會交換中間數據。Coordinator是負責從Worker獲取結果並返回最終結果給Client。
當Worker啓動時,會廣播自己去發現 Coordinator,並告知 Coordinator它是可用,隨時可以接受Task。
Worker與Coordinator、Worker通信是通過REST API。
3)數據源
貫穿全文,你會看到一些術語:Connector、Catelog、Schema和Table。這些是Presto特定的數據源
① Connector
Connector是適配器,用於Presto和數據源(如Hive、RDBMS)的連接。你可以認爲類似JDBC那樣,但卻是Presto的SPI的實現,使用標準的API來與不同的數據源交互。
Presto有幾個內建Connector:JMX的Connector、System Connector(用於訪問內建的System table)、Hive的Connector、TPCH(用於TPC-H基準數據)。還有很多第三方的Connector,所以Presto可以訪問不同數據源的數據。
每個Catalog都有一個特定的Connector。如果你使用catelog配置文件,你會發現每個文件都必須包含connector.name屬性,用於指定catelog管理器(創建特定的Connector使用)。一個或多個catelog用同樣的connector是訪問同樣的數據庫。例如,你有兩個Hive集羣。你可以在一個Presto集羣上配置兩個catelog,兩個catelog都是用Hive Connector,從而達到可以查詢兩個Hive集羣。
② Catelog
一個Catelog包含Schema和Connector。例如,你配置JMX的catelog,通過JXM Connector訪問JXM信息。當你執行一條SQL語句時,可以同時運行在多個catelog。
Presto處理table時,是通過表的完全限定(fully-qualified)名來找到catelog。例如,一個表的權限定名是hive.test_data.test,則test是表名,test_data是schema,hive是catelog。
Catelog的定義文件是在Presto的配置目錄中。
③ Schema
Schema是用於組織table。把catelog好schema結合在一起來包含一組的表。當通過Presto訪問hive或Mysq時,一個schema會同時轉爲hive和mysql的同等概念。
④ Table
Table跟關係型的表定義一樣,但數據和表的映射是交給Connector。

1.4 Presto數據模型

1)Presto採取三層表結構:
Catalog:對應某一類數據源,例如Hive的數據,或MySql的數據
Schema:對應MySql中的數據庫
Table:對應MySql中的表
在這裏插入圖片描述
2)Presto的存儲單元包括:
Page:多行數據的集合,包含多個列的數據,內部僅提供邏輯行,實際以列式存儲。
Block:一列數據,根據不同類型的數據,通常採取不同的編碼方式,瞭解這些編碼方式,有助於自己的存儲系統對接presto。
3)不同類型的Block:
① Array類型Block,應用於固定寬度的類型,例如int,long,double。block由兩部分組成:
boolean valueIsNull[]表示每一行是否有值。
T values[] 每一行的具體值。
② 可變寬度的Block,應用於String類數據,由三部分信息組成
Slice:所有行的數據拼接起來的字符串。
int offsets[]:每一行數據的起始偏移位置。每一行的長度等於下一行的起始便宜減去當前行的起始偏移。
boolean valueIsNull[] 表示某一行是否有值。如果有某一行無值,那麼這一行的偏移量等於上一行的偏移量。
③ 固定寬度的String類型的block,所有行的數據拼接成一長串Slice,每一行的長度固定。
④ 字典block:對於某些列,distinct值較少,適合使用字典保存。主要有兩部分組成:
字典,可以是任意一種類型的block(甚至可以嵌套一個字典block),block中的每一行按照順序排序編號。
int ids[]表示每一行數據對應的value在字典中的編號。在查找時,首先找到某一行的id,然後到字典中獲取真實的值。

1.5 Presto優缺點

Presto中SQL運行過程:MapReduce vs Presto
在這裏插入圖片描述
使用內存計算,減少與硬盤交互。
1.5.1 優點
1)Presto與Hive對比,都能夠處理PB級別的海量數據分析,但Presto是基於內存運算,減少沒必要的硬盤IO,所以更快。
2)能夠連接多個數據源,跨數據源連表查,如從Hive查詢大量網站訪問記錄,然後從Mysql中匹配出設備信息。
3)部署也比Hive簡單,因爲Hive是基於HDFS的,需要先部署HDFS。

在這裏插入圖片描述
1.5.2 缺點
1)雖然能夠處理PB級別的海量數據分析,但不是代表Presto把PB級別都放在內存中計算的。而是根據場景,如count,avg等聚合運算,是邊讀數據邊計算,再清內存,再讀數據再計算,這種耗的內存並不高**。但是連表查,就可能產生大量的臨時數據,因此速度會變慢,反而Hive此時會更擅長。**
2)爲了達到實時查詢,可能會想到用它直連MySql來操作查詢,這效率並不會提升,瓶頸依然在MySql,此時還引入網絡瓶頸,所以會比原本直接操作數據庫要慢。

1.6 Presto、Impala性能比較

https://blog.csdn.net/u012551524/article/details/79124532

二、 Presto優化

2.1 數據存儲

1)合理設置分區
與Hive類似,Presto會根據元信息讀取分區數據,合理的分區能減少Presto數據讀取量,提升查詢性能。
2)使用列式存儲
Presto對ORC文件讀取做了特定優化,因此在Hive中創建Presto使用的表時,建議採用ORC格式存儲。相對於Parquet,Presto對ORC支持更好。
3)使用壓縮
數據壓縮可以減少節點間數據傳輸對IO帶寬壓力,對於即席查詢需要快速解壓,建議採用Snappy壓縮。
4)預先排序
對於已經排序的數據,在查詢的數據過濾階段,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’; 

2.2 查詢SQL優化

1)只選擇使用必要的字段
由於採用列式存儲,選擇需要的字段可加快字段的讀取、減少數據量。避免採用*讀取所有字段。

[GOOD]: SELECT time,user,host FROM tbl
[BAD]:  SELECT * FROM tbl

2)過濾條件必須加上分區字段
對於有分區的表,where語句中優先使用分區字段進行過濾。acct_day是分區字段,visit_time是具體訪問時間。

[GOOD]: SELECT time,user,host FROM tbl where acct_day=20171101
[BAD]:  SELECT * FROM tbl where visit_time=20171101

3)Group By語句優化
合理安排Group by語句中字段順序對性能有一定提升。將Group By語句中字段按照每個字段distinct數據多少進行降序排列。

[GOOD]: SELECT GROUP BY uid, gender
[BAD]:  SELECT GROUP BY gender, uid

4)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

5)使用近似聚合函數
Presto有一些近似聚合函數,對於允許有少量誤差的查詢場景,使用這些函數對查詢性能有大幅提升。比如使用approx_distinct() 函數比Count(distinct x)有大概2.3%的誤差。
SELECT approx_distinct(user_id) FROM access
6)用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%'

7)★★★使用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

8)使用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

2.3 無縫替換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是一個備份表,若線上運行一段時間後沒有出現問題,則可以刪除該表。

2.4 注意事項

ORC和Parquet都支持列式存儲,但是ORC對Presto支持更好(Parquet對Impala支持更好)
對於列式存儲而言,存儲文件爲二進制的,對於經常增刪字段的表,建議不要使用列式存儲(修改文件元數據代價大)。對比數據倉庫,dwd層建議不要使用ORC,而dm層則建議使用

Tips:
orc介紹:https://www.cnblogs.com/ITtangtang/p/7677912.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章