原文鏈接
- 作爲Greenplum所有ETL操作基本原理,我們有必要稍微多介紹一些gpfdist的細節,以便於理解爲什麼它比其他工具速度更快,以及將來我們應如何對其進行改進。
- 本文將聚焦在gpfdist server和Greenplum 可讀外部表之間的通信細節上,介紹數據流以及gpfdist外部表協議。
- Gpfdist使用HTTP協議和Greenplum segment通信。Gpfdist作爲HTTP Server,負責將靜態文件內容分發到GPDB,或從GPDB接收內容。每個Segment作爲一個HTTP client,通過get或post從gpfdist獲取數據。
- Gpfdist 協議使用特殊的HTTP Header傳遞GPDB和Gpfdist之間必要信息。下表列出了gpfdist 可讀外部表用到的所有特殊HTTP Hader。
Header |
Message type |
Required |
description |
X-GP-XID |
Request |
Y |
transaction id |
X-GP-CID |
Request |
Y |
command id |
X-GP-SN |
Request |
Y |
scan counter |
X-GP-SEGMENT-ID |
Request |
N |
segment id |
X-GP-SEGMENT-COUNT |
Request |
N |
Segment count |
X-GP-LINE-DELIM-LENGTH |
Request |
N |
length of line ending |
X-GP-PROTO |
Request/Response |
Y |
protocol version, 0 or 1 |
X-GP-MASTER_HOST |
Request |
N |
GPDB master host address |
X-GP-MASTER_PORT |
Request |
N |
GPDB master port |
X-GP-CSVOPT |
Request |
Y |
CSV option format |
X-GP_SEG_PG_CONF |
Request |
N |
config path of segment |
X-GP_SEG_DATADIR |
Request |
N |
data directory of segment |
X-GP-DATABASE |
Request |
N |
current database name |
X-GP-USER |
Request |
N |
current login user |
X-GP-SEG-PORT |
Request |
N |
Segment port |
X-GP-SESSION-ID |
Request |
N |
gp_session_id |
X-GPFDIST-VERSION |
Response |
Y |
gpfdist server version |
- 上述表中
Message type
一列表示對應的Header字段應該出現在什麼位置。
Request
表示出現在從Greenplum到gpfdist的HTTP請求頭中
Response
表示出現在gpfdist的響應頭中
- 並非所有字段都是必須的,具體參見
required
列
- 接下來將介紹最重要的幾個字段:
X-GP-SN
- Greenplum外部表目前只支持順序掃描,因此一些self-join的查詢會對一個外部文件進行兩次掃描。
X-GP-SN
用來記錄當前掃描計數。Gpfdist將會使用該參數(連同command id和transaction id)來決定兩個連接是否屬於同一個session。
X-GP-PROTO
- 可讀外部表有兩種不同的協議,Protocol 0 和 Protocol 1 目前都是可用的。我們稍後會介紹他們的區別。該參數是gpfdist識別一個請求是否來自GPDB還是其他工具的重要依據。Gpfdist會拒絕沒有
X-GP-PROTO
的連接。
X-GP-CSVOPT
- GPfdist 支持text和csv格式。
- Text很方便處理。每解析一行就發送數據給GPDB就可以了。
- 對於CSV格式,gpfdist需要知道更多關於轉義(escape)或引號符(quote character)的信息。其格式爲
m.x.q.n.h.
。各小寫字母的含義如下:
Letter |
Meaning |
Example |
m |
Csv or not |
1 or 0 |
x |
Decimal number of escape byte |
9 (TAB), 124 (|), etc |
q |
Decimal number of quote byte |
34(“) |
n |
EOL type |
0-(EOL_UNKNOWN), 1-(EOL_LF), 2-(EOL_CR), 3-(EOL_CRLF) |
h |
With header or not |
1 or 0 |
可讀外部表如何工作
- 當用戶在可讀外部表上執行一個select查詢時,GPDB的每個segment各自發送已填好Header字段的HTTP GET請求到gpfdist,獲取和此次查詢相關的數據。
- 當gpfdist收到來自GPDB segment的請求後,按照每個請求中的<
transaction id
,command id
,scan count
> 參數,分組到各個session。所有屬於同一個session的請求都是服務於同一個查詢的,且由不同的segment並行發送而來。gpfdist會根據URL路徑,讀取本地文件的數據,直到已經讀取足夠多的數據到緩存之後纔會發送。默認緩存大小是32KB,可通過-m
參數修改。如果gpfdist正在讀的文件是一個管道,且管道輸入很慢,它會持續等待,且不會響應其他請求。這是gpfdist當前版本的一個侷限,在後續版本中將會得到改進。
可讀外部表GUC
gp_external_max_segs
- 用於控制每個查詢中,允許連接到單個gpfdist上的最大segment數量。即對於同一個查詢session,最多允許
gp_external_max_segs
個segment連接到單個gpfdist server。默認值是64. Gpfdist在開始發送數據之前不需要等待所有segment都連接上來。每個segment一次只會創建一個到gpfdist的連接。第一個到gpfdist的連接會在gpfdist中創建一個session,之後屬於同一個查詢的連接都會加入這個session。只要session非空,gpfdist就會輪詢地(round robin)通過這些連接向segment發送數據。
readable_external_table_timeout
- 該值控制GPDB在取消鏈接之前的等待時間。如果一個基於gpfdist的查詢執行了很長時間,將會回錯誤
intermittent network connectivity issues
。用戶可自己設置readable_external_table_timeout
的值。
可讀外部表工作流
- 可讀外部表的工作流是很簡單的。GPDB發送HTTP請求到gpfdist,gpfdist返回HTTP響應和請求的數據。
- 有兩個版本的協議可用於讀外部表,通過
X-GP-PROTO
區分。我們接下來將會解釋二者的區別。下面是可讀外部表在單個segment和gpfdist之間的工作流。
Protocol 0
Protocol 0
很簡單:使用HTTP頭部字段來傳遞所有元數據。GPDB Segments 發送 GET請求,gpfdist向segment響應原始數據(raw data)
Protocol 0
最大的缺陷在於當gpfdist內部發生錯誤時,沒有途徑告知GPDB。如果gpfdist被用戶殺掉,或原始文件損壞,gpfdist無法將此類消息告知GPDB。它唯一能做的就是立即關閉socket以結束HTTP連接。然後,這和普通的讀成功時表現一致,因此GPDB無法識別讀成功還是失敗。
Protocol 1
Protocol 1
的目的就是解決Protocol 0
最致命的缺陷。它爲每個數據塊(稱之爲package)定義了一個新的數據格式,封裝後再發送給GPDB。Gpfdist和GPDB不會直接發送原始數據。所有數據通過特殊格式封裝,然後通過一個個package發送。Package 由消息構成。有3中類型的package和4種類型的消息。每種消息有3個字段:message type
,content length
,content
。
Field name |
field length |
Message type |
1 |
Content length |
4 |
Content data |
Value of Content length |
Message Type |
Full name |
content |
F |
filename |
file name that related data belong to |
O |
offset |
approx office(譯註:疑爲筆誤,應該是offset) in file of current data |
D |
data |
the real data |
E |
error |
Error message of gpfdist |
L |
line number |
approx line number in file |
- 由於gpfdist從文件夾中查找文件時支持通配符,
F
消息(filename)總是顯示數據所在的正確文件名。 Offset(O)和line number(L)是對數據在文件中位置的一個估值,在向用戶展示錯誤信息的時候很有意義,比如當Greenplum解析數據內容時發現格式錯誤。可以通過該信息知道文件中發生錯誤的大概位置。
- Package 可對數據或消息進行封裝。
- 對於數據package,消息序列是F,O,L,D。
- 如果沒有更多消息可發送,gpfdist將發送空的package並攜帶長度爲0的D消息,用於關閉連接。
- 如果有錯誤,gpfdist發送包含錯誤詳細信息的E消息
Package type |
Message content |
Data |
FOLD |
End |
zero-length D |
Error |
E |
- 下圖爲Protocol 1的連接的典型示例
l
- 如果對HTTP協議熟悉,你會注意到上述消息和HTTP Chunked-mode很類似,但實際上二者是完全不同的,儘管其目的類似。
- Chunked-mode 非常適合傳輸流式數據
- Gpfdist的Protoco1響應,借鑑了HTTP並添加了其專有的特殊目的元數據。
概述
- 本文介紹了gpfdist可讀外部表的兩種協議。可通過wireshark工具理解這些協議,該工具能捕獲TCP報文並以圖形化方式展示通信過程。建議和gpfdist相同的的主機上運行wireshark,以觀察來自GPDB segment的請求和響應。
- 新寫一個基於上述協議、能夠發送數據到GPDB的簡單版本gpfdist並不難。事實上目前已有一些gpfdist的實現,比如Gemfire GPDB Connector 或者 一個C#版本的工具(https://github.com/pf-qiu/gpfdist.net)
參考
https://gpdb.docs.pivotal.io/43160/ref_guide/config_params/guc-list.html
https://github.com/greenplum-db/gpdb/tree/master/src/bin/gpfdist