摘要:從發展歷程到通信模型設計,到你了解一下GaussDB通信原理知識。
MPPDB通信庫發展歷程
-
Postgres-XC
方法:采用libpq通信庫實現CN和DN之間的連接,CN負責計算,DN僅進行數據存儲。
缺點:隨着計算任務的不斷增大,CN計算出現瓶頸,且白白浪費DN的計算能力。
-
V1R3
方法:提出分布式執行框架,將計算下推到DN上執行,DN不僅作為存儲同時承擔計算任務。由於每個DN只存儲有一部分數據,因此DN間也需要進行通信,采用stream線程TCP直連,每個連接都需要一個TCP端口。
缺點:隨着集群規模的增加,DN之間的連接數不斷增加,在大並發場景下需要大量的TCP端口,而實際情況是由於單機TCP端口只有65535個,TCP端口數嚴重限制了數據庫的集群規模和並發規模。
-
V1R5-V1R7C00
方法:結合SCTP協議的多流優點,DN間的通信采用SCTP通信庫架構,使用邏輯連接來代替物理連接,用來解決DN之間連接太多的問題。
缺點:SCTP協議本身存在的內核BUG,通信報錯。
-
V1R7C10
方法:TCP代理通信庫。
缺點:CN和DN之間的物理連接數也會暴漲。
-
V1R8C00
方法:CN和DN之間也采用邏輯連接實現,即CN多流。
問題1:DN間是查詢結果的通信還是原始數據的通信?
解:既有查詢結果,也有原始數據;DN之間的數據交流是Hash之后,各個DN根據所需獲取。
通信模型的設計
1. Pooler通信模型
問題2:連接池上的CN、DN是否存在交集,即poolA中的DN在poolB中也存在?
解:不存在。
問題3:CN和CN的連接是做什么的?為什么設計連接池連接CN上的PG線程?CN上為什么有多個PG線程,有什么用途?
解:CN和CN之間連接是為了數據同步等,包括建表之后的信息交匯。CN上的PG線程也是為了用戶查詢建立。
問題4:DN上為什么有多個PG線程,有什么用途?
解:一個查詢操作就是創建一個新的PG線程。
問題5:如何理解gsql客戶端退出時,只退出PG線程和agent代理線程,而取到的slot連接會還回database pool?
解:slot連接退回到pooler,下次連接可以直接獲取,若不存在,則重新創建。
問題6:DN間的TCP連接數:C = (H * D * Q * S)* D,為什么最后乘以D,而不是(D - 1)/ 2 ? 此外,這個公式是否存在高重復,每次查詢都要建立DN間的通信,而不考慮重復性?DN間的通信線程數量是否有限制?
解:數據是雙向通信,但執行算子是單向的,所以不能除以2。數量有限制,但可以調整。
2. DN間stream線程通信模型
執行計划需要使用多個線程進行執行,包括1個CN線程和每個DN上3個線程t1-t3。每個DN節點都有3個線程是因為數據是分布到每個DN上的,在執行查詢過程中,查詢的每個階段每個DN都要參與。自下而上是數據流,自上而下是控制流。具體執行過程如下:
- 每個DN的t3順序掃描table表,將表數據廣播到所有DN的t2線程;
- 每個DN的t2接收t3的數據,建立hash表,然后掃描store_sales數據與customer表進行hashjoin,join結果進行哈希聚集后,重分布發送到對應DN的t1線程;
- 每個DN的t1線程將收到的數據進行第二次哈希聚集, 排序后將結果傳輸到CN;
- CN收集所有DN的返回數據,作為結果集返回。
問題7:DN上是否執行查詢操作?DN廣播的數據是否屬於同一個數據表?每個DN都廣播數據,那最后所有DN的數據是否相同?圖中t2發送給所有DN的t1?、
解:執行,DN上存的數據是表的幾分之幾,不是整個表,也不是一個表的部分,是所有表的一部分,這樣做是為了並發。DN數據不相同,因為各取所需。
SCTP通信庫設計
1. 概要
- SCTP 協議:一種可靠、保序協議,支持message-based模式,單個通道支持65535個流,且多流之間互不阻塞,利用該特性,可以打破設備物理連接數對大規模集群節點間通信的限制,支持更大規模的節點規模。
- 通道共享:每兩個節點之間有一個數據傳輸單向SCTP物理通道,在這條物理通道內部有很多邏輯通道(inner Stream),每個stream流由producer發送到consumer,利用SCTP內部支持多流的特性,不同的producer & consumer對使用通道中不同的流(SCTP流),因此每兩個點之間僅需要兩個數據連接通道。
- 通道復用:查詢完成后,物理通道中的邏輯連接關閉,物理連接不關閉,后面的查詢繼續使用建好的物理連接。
- 流量控制:采用pull模式,因為SCTP通道的所有流共享內核的socket buffer, 為了避免一個連接發的數據量過大,consumer端卻不接收,導致kernel的buffer被填滿,阻塞了其他流的發送,增加了流控,給每個流設置一個quota, 由接收端分配,當有quota時,告知發送端可發送數據,發送端根據發來的quota值,發送quota大小的數據量,實現接收端與發送端同步控制;為了保證控制信息的可靠性,將控制信息和數據通道分離,流控信息走單獨的一條雙向TCP控制通道。
2. 架構
- TCP Channels:TCP控制通道,控制流走此通道;
- SCTP Channels:SCTP數據通道,包含很多stream流,數據流走此通道;
- Send Controller發送端流控線程:gs_senders_flow_controller(),收發控制消息;
- Recv Controller接收端流控線程:gs_receivers_flow_controller(),接收端用於發送和接收控制報文,與代理接收線程不同,代理接收線程接收的是數據,而接收流控線程接收的是報文;
- Receiver代理接收線程:gs_receivers_loop(),用於接收數據的線程,從sctp數據通道中接收數據,將數據放到不同邏輯連接的不同cmailbox報箱中,並通知執行層的consumer工作線程來取,取走后,有空閑的buffer時,接收端流控線程會通過tcp控制通道通知發送端還有多少空閑內存,即還有多少quota可用於繼續接收數據;
- Auxiliary輔助線程:gs_auxiliary(),由top consumer,每個兩秒檢查一下,處理公共事務,如DFX消息,Cancel信號響應等;
- 數據PULL模型:每個邏輯連接有quota大小的buffer,需要數據時將空閑buffer的大小(即quota)發送給發送端,發送端即可以發送quota大小的數據,quota不足時阻塞發送,直到接收端的buffer被應用取走。
TCP多流實現
TCP代理在現有的邏輯連接、代理分發、quota流控等實現的基礎上,將數據通道從SCTP協議替換成TCP協議,TCP代理邏輯連接的實現基於head+data的數據包收發模型,在head中寫入邏輯連接id及后續data的長度。
問題8:單機TCP只有65535個端口,SCTP呢?TCP多流和TCP在端口上的區別?TCP的三次握手是否依舊?
SCTP是基於消息流傳輸,數據收發的最小單位是消息包(chunk),一個SCTP連接(Association)同時可以支持多個流(stream),每個流包含一系列用戶所需的消息數據(chunk)。而TCP協議本身只能支持一個流,因此我們需要在這一個流中區分不同邏輯連接的數據,通過在TCP報文的head中寫入邏輯連接id及后續data的長度來區分,這樣雖然TCP只有一個數據包組成,但每個數據包包含多個塊,實現了TCP的多流。同時發送時需保證整包原子發送。
問題9:如何多流?是多個通道同時發送head+data嗎?
解:一個流發送完整的數據。多流是並發。
TCP代理通信庫的發送端實現,producerA和producerB分別給DN1發送數據,為保證發送端head+data發送的完整性,producerA首先需要對單個物理連加鎖,此時producerB處於等鎖狀態,producerA將數據完整發送到本地協議棧后返回,並釋放鎖,producerB獲得鎖后發送數據。節點數越多沖突越少。
問題10:加鎖等待是否影響效率?SCTP的實現也是如此?
解:不影響,因為有緩存buffer。
問題11:數據丟失,永遠無法達到head怎么辦?一直緩存?
解:不會丟失,TCP協議有保證。
CN多流實現
在V1R8C00版本中,CN和DN之間的鏈接使用libcomm通信庫,即兩個節點間僅存在一條物理通道,在該物理通道上使用邏輯連接通道實現並行通信。
CN端流程:
- 建立連接:CN調用Libcomm的gs_connect與DN建立連接,入參中指明建立雙向邏輯通道,使用相同的nidx,sidx同時初始化發送和接受的mailbox,通過發送流控線程通知接收端;
- 等待DN返回結果:通過判斷發送mailbox的狀態,確認DN端已成功建立的邏輯連接(發送流控線程收到DN端的CTRL_READY報文);
- 發送startuppacket:通過PQbuildPGconn,初始化libpq的pg_conn結構體,生成startuppacket,隨后通過gs_send發送startuppacket給DN端;
- 等待DN PG線程初始化:通過LIBCOMMgetResult,等待DN端返回ready for query報文,之后認為連接建立成功。
DN端流程:
- 初始化發送、接收mailbox:DN端接收流控線程識別到連接請求來自CN后,調用gs_build_reply_conntion,注冊CN的信息,初始化發送mailbox,隨后初始化接收mailbox,最終通過流控線程返回CTRL_READY報文,表示邏輯通道建立成功;
- 創建unix domain sock:DN端接收流控線程,創建一個unix domain sock,將生成的邏輯連接地址通過該通道發給postmaster主線程;
- fork postgres子線程:postmaster主線程的serverloop監聽到unix domain sock后,接收邏輯連接地址,保存到port結構體中,隨后fork postgres子線程(沿用原有邏輯);
- postgres線程初始化完畢:在pg線程完成初始化后,首先給CN回復ready for query報文,隨后進入ReadCommand函數,等待CN發來的下一個Query。
本文分享自華為雲社區《GaussDB通信原理總結》,原文作者:Caesar.D 發。