天天看點

CockroachDB之本地以及分布式查詢處理

譯者注:本文詳細介紹了CockroachDB的兩種查詢處理方式:本地及分布式,其中較長的描述了設計分布式引擎的目的,為了達到分布式,還存在哪些遺留問題。以下為譯文。

當CockroachDB節點接收到查詢SQL時,大概會發生什麼事情呢:

pgwire子產品負責與用戶端應用通信,從用戶端接收查詢請求。将SQL文本分析并轉換為抽象文法樹(Abstract Syntax Tree,簡稱AST)。然後進一步分析并将其轉換為邏輯查詢計劃,該計劃是關系運算符的樹,如過濾器,渲染(項目),連接配接。 順便說一下,邏輯計劃樹是由EXPLAIN語句報告的資料。

然後将邏輯計劃交給負責執行查詢的back-end層,并生成要傳回給用戶端的結果資料。

CockroachDB裡有兩種back-end:本地執行引擎和分布式執行引擎。

本地查詢處理

本地執行引擎能夠直接在用戶端應用程式連接配接的節點上執行SQL語句。它主要在本地處理查詢,在一個節點上:它需要的任何資料都可以在叢集中的其他節點上讀取,然後複制到處理節點上以建構查詢結果。

CockroachDB的本地引擎的架構大緻遵循Goetz Graefe于1993年描述的火山模型(PDF連結)。從軟體架構師的角度來看,邏輯查詢計劃中的每個節點都像一個有狀态的疊代器(例如Python發生器做什麼):在樹的根上疊代生成所有結果行,并且在計劃樹的每個節點處,一次疊代将在樹中進一步消耗零個或多個節點的疊代。樹的樹葉是表或索引讀取器節點,它向CockroachDB的分布式存儲層發出KV查找操作。

*邏輯計劃執行個體:

SELECT cust_id, address FROM customer WHERE name LIKE ‘Comp%’ AND state = ‘CA’

假設cust_id就是主鍵,并且customer(name)上面建立了索引。*

從代碼的角度來看,每個關系操作符都是作為疊代器的“下一個”方法的實作;當執行查詢時,疊代器的樹是有序的進行處理:每個節點的“下一個”方法都處于等待狀态,直到源節點完成自己的“下一個”方法調用。從外部看,查詢執行邏輯逐行處理資料并作出決定(例如,保留/删除行,計算派生結果)。處理本質上是連續的。

簡單是該引擎的主要特點。

該引擎的代碼可以僅使用本地推理進行審查和驗證,進而保證正确性; 對此我們(CockroachDB開發人員)是比較信任的。

另外,因為處理是在本地執行的,是以說如果本地(在相同的節點上)有需要的所有資料,并且/或者在源表/索引中隻有少數行進行處理時,則可以非常快速地提供結果。

本地并行處理UPDATE

用戶端應用程式中的常見模式是在單個會話/事務的一行中發出多個INSERT或UPDATE(或UPSERT或DELETE)語句。同時,CockroachDB中的資料更新必須比大多數其他SQL引擎持續更長時間,因為為了保持一緻是需要強制性的網絡流量。

我們找到了自己的困惑:我們為什麼不能通過并行地執行它們進而加快資料寫入的處理呢?這樣的話,雖然修改單個資料的語句的延遲更高,但如果有多個的話,那麼是可以降低總體的延遲。

然而,這種調整帶來的改變是微不足道的。

在客戶機應用程式和資料庫伺服器之間被看作是API時,标準SQL語言有一個不友善的屬性:它不允許并行處理多個查詢。

SQL語言的設計者已經規定每條SQL語句執行時的狀态應該“就假設之前的語句已經執行完成一樣”,特别是PostgreSQL和CockroachDB采用的方言。例如,如果在INSERT之後緊跟着執行了一遍SELECT,那麼SELECT時就必須假設已經INSERT執行完了。除此之外,SQL“API”或“協定”是會話式的:每個語句都可能有一個結果,而用戶端應用程式在決定下一步将運作哪個語句之前,是可以觀察結果的。例如UPDATE完成之後也會有一個傳回結果:影響了多少條資料。用戶端可以使用UPDATE去更新一條資料,如果UPDATE的結果顯示影響0條資料(不存在這樣的資料),那麼會決定發出INSERT。

SQL語言的語義特征是非常有用的;它們賦予了用戶端應用很多的控制。當時為了把這些特征給包含到SQL中而做出的選擇,無意當中卻導緻了SQL并行自動化執行成為了不可能。

自動化并行是什麼樣子的?在計算機科學當中這是一個非常經典的問題。當處在足夠高的級别上時,每個解決方案看起來都是一樣的:從應用程式接收指令/操作/查詢的處理引擎必須找出哪些操作在功能上獨立于它們之前和之後。如果用戶端應用或者程式向處理引擎發送指令“處理A,再處理B”,那麼處理引擎會确認B是不需要A提供任何結果的,如果B在A之前完成,則A不會受到影響,可以在A完成之前啟動B(可能在同一時間點),以便A和B并行執行。當然,每一個傳回給應用或者程式的操作結果必須看起來好像是嚴格按照順序執行的。

使用标準SQL,一旦資料修改語句與同一個表上的SELECT進行交錯,就很難确定。

然而,當我們與某些對寫延遲有特别興趣的使用者讨論這個問題時,我們達成了一緻的觀點:我們可以擴充我們的SQL方言,以提供可以并行處理的CockroachDB特定的文法擴充。這是可以被接受的的,因為與延遲更高的交易導緻的持續業務成本相比,更新用戶端代碼以添加必要的注釋的一次性前期成本是可接受的。

可以在我們的庫中找到詳細的設計。為了利用這個新功能,用戶端應用程式可以使用特殊的子句RETURNNING NOTHING來執行INSERT/DELETE/UPSERT/UPDATE。當使用RETURNING NOTHING發出兩個或多個資料修改語句時,本地執行引擎将同時啟動它們,它們可以并行地進行。隻有當事務被送出時,引擎才會等待直到完成所有的資料更新。

CockroachDB的分布式查詢處理

除了本地引擎,CockroachDB還提供了一個分布式執行引擎。這樣做的作用是将單個SQL語句所需的處理部分委托給叢集中的多個節點,這樣處理就可以并行地在多個節點上進行,并有望完成得更快。我們還可以期望它消耗較少的網絡流量,例如可以在源代碼中應用過濾器。

這篇文章詳細介紹了原因和大概的方式。我們将分兩篇單獨的文章,進一步說明它是如何工作的。

颠覆舊觀念

分布式處理引擎常見的原因是因為我們通過觀察發現查詢的資料通常分布在叢集中的多個節點上。相比将資料放在單個處理節點上,直覺告訴我們可以将把對資料的計算進行轉移,這樣做可以節省處理時間(=使查詢更快)。

然而,當你的思路處在更高層級,你會發現這種想法還是太low了:如果僅僅是出于這個目的,那麼除了查詢分發之外,還有許多可能的解決方案。

例如,有人可能會說,已經有很好的非分布式解決方案來提高性能。

可以将過去30年來那些偉大智慧的結晶歸結為:通過曆史觀察可以這樣說,生産代碼中的資料工作負載要麼是由小型的突發事務組成,這些事務需要的資料視圖得是最新的,并且還得是一緻的,但是隻涉及到非常少的資料量(OLTP工作負載),要麼就是由那些長久的、跨度大的并且隻讀的事務組成,這些事務涉及到資料量就比較大了,但是通常對資料視圖不要求是最新的,也不要求是一緻的(分析工作負載,線上或者是線下)。

從這個觀察可以看出,OLTP的工作負載主要是在分布式存儲系統中與少量的節點進行通信(因為主要和輔助索引會将工作縮小到存儲中的幾行),而分析工作負載可以在物化視圖上面運作,這些視圖是在在單獨的系統中異步維護的,以一緻性為代價(因為它們不需要更新任何内容)對增大吐吞量進行優化。無論在哪種情況下,分布式處理都不能展現出明顯的增值。

分布式處理的另外一個常見的動機是為了向前面提到的30年的智慧結晶發起挑戰,承認在網際網路服務中新型工作負載的冉冉升起:現在最常見的是OLTP工作負載在更新少量資料之前,需要讀取大量的資料,而分析工作負載的好處就是在預知需要讀取少量最新的資料即可。在這兩種情況下,分布式處理看似都提供了有效的解決方案,可以使它們變得更快。

然而,與更進階的再相比,它們還是顯得比較弱,因為由于這些工作量已經變得司空見慣,我們已經看到了一些看起來更簡單、更有效的技術和标準的出現,正是為了解決這些用例。

對于在執行UPDATE之前,需要做事務性的相關工作,包括大量的資料讀取,那麼常用的方法是使用合适的緩存。Memcached以及其他相關技術的出現就是為了解決這個問題的。對于需要最新資料的分析處理,額外的複制堆棧可以確定分析輸入既是最新的也可以快速通路,這些堆棧是用來維護物化視圖的一緻性。良好的緩存和事務/事件日志記錄是實作這一目标的有名的也是有效的技術手段,并且相對于通用的分布式處理引擎,供應商更容易提供相關技術。

在新的計算需求的表達與加速它們的專門解決方案的設計之間來來往往是計算機和軟體架構師的主要工作,并且這也是計算曆史上最經常出現的繪圖裝置。畢竟,它“隻是工作”,對吧?

雖然運作了,但它是如此的複雜!

在這裡,“複雜”是使用需要付出昂貴代價的委婉說法。

分布式查詢處理的初衷

在這個故事中,我們揭示了計算機曆史上第二種最常見的繪圖裝置:排除複雜性。它的工作原理介紹如下:

“作為程式員,我真的不想了解所有這些專門的東西。我隻想開發我的應用程式!”

“作為公司老闆,我不想為了達到業績,而需要與10家不同的技術提供商打交道。我的瑞士軍刀在哪?”

如果沒有通用的分布式計算引擎,開發人員或技術人員處理資料必須時刻牢記許多問題的答案:我的應用程式在資料庫中抛出什麼樣的工作量?我需要什麼二級索引來使查詢更快?我需要采用哪個第三方緩存技術?使用哪個棧來跟蹤資料倉庫中的更新事件?應該使用哪些用戶端庫來保持一緻的資料視圖,以避免伺服器上花費長時間的延遲查詢?

設計一個網際網路規模的應用程式或服務所需要的知識,是需要花費很多時間的,相應的操作成本(軟體+人力資源)也超出了一定的範圍。

這就是我們之是以隻要在CockroachDB設計分布式處理的原因:一種多功能工具,可以在資料附近執行任意計算。

我們的長期目标是幫使用者消除必須考慮任意複雜操作的原子性,一緻性,隔離性和耐久性的負擔,也不要考慮來自不同供應商的工具之間的內建。

簡而言之,我們的目标是提高正确性,操作簡單性和更高的開發人員可伸縮性能的效率,尤其是在普通的情況下,作為一種良好的補充。

這是建立CockroachDB以後一種自然而然的延伸。畢竟,CockroachDB在新一季的NoSQL熱潮之後,再次引起了對NewSQL的興趣:軟體社群已經嘗試管理用戶端的交易和模式,但是失敗了,并已經承認在資料庫引擎層面進行處理,就正确性,簡單性和生産力方面來說,既有直接優勢,也有間接的優勢。

我們的分布式處理引擎擴充了這一原則,并在此基礎上提出了一些更複雜的計算需求。

這包括當維護二級索引和物化視圖的良好組合是不切實際或太不利于更新性能時,需要開始啟動支援執行SQL查詢。

它還包括支援一些經常執行的大量聚合的分析工作,其中結果需要最新的

遊戲賬号賣号

輸入資料。

但是,最終我們也希望能夠滿足更大的工作量。 例如,我們非常尊重制作Apache Samza的願景,我們鼓勵您觀看Martin Kleppmann的示範文稿“将資料庫外出”,以了解我們的目标的總體方向。

“内置電池!”

是以,我們将在CockroachDB中實施分布式處理引擎。

它受到Sawzall的啟發,分布式處理的進階之處如下:

來自用戶端應用的請求被轉換為分布式處理計劃,類似于資料流處理網絡的藍圖。

接收到查詢的節點然後将該查詢計劃部署到叢集中的一個或多個其他節點上。此部署包括在遠端每個節點上建立“虛拟處理器”(如少量計算引擎),以及它們之間的資料流(如少量專用網絡連接配接)。

啟動分布式處理器網絡以開始計算。

同時,處理查詢的節點從分布式網絡收集結果并将其轉發給用戶端。當所有處理器停止時,處理被視為完成。

我們再次強調,這是一種通用方法:資料流處理網絡是理論計算機科學的一個強大的模型,現在被公認為可以處理幾乎任何類型的計算。最終,我們希望使用者能夠以任意方式利用這個通用工具。然而,在初始階段,我們将其開發限制在一些常見的SQL模式,以便我們可以專注于穩健性和穩定性。

例如,在CockroachDB 1.0中,分布式引擎可以自動處理SQL排序、過濾、簡單聚合和一些連接配接。在CockroachDB v1.1中,它将自動接管更多的SQL聚合和連接配接。我們将評估社群對第一個方法的反應,以決定在哪裡進一步擴充功能。

CockroachDB的未來計劃

大量的遺留工作

說實話,在推薦分布式處理作為通用工具之前,我們還需要學習相關知識,進而去回答一些複雜的理論問題。我們現在遇到的部分問題如下:

雖然資料提取處理器可以直覺地在資料生命的節點上啟動,但其他處理器(如排序或聚合資料的處理器)可以放置在任何位置。應該啟動多少?哪個叢集節點?

當CockroachDB在節點間自動重新平衡資料時,如何確定計算量保持接近資料? 虛拟處理器應該與他們正在開展的資料範圍一起遷移嗎?

當分布式查詢正在進行時,節點何時出現故障? 處理是否在其他地方恢複,并嘗試恢複? 部分資料丢失在某些查詢中可以接受嗎?

分布式處理如何影響叢集的性能? 當一個節點代表另一個節點運作虛拟處理器時,它仍然可以向自己的用戶端提供多少吞吐量?

如何確定大型查詢不會在許多節點上耗盡網絡或記憶體資源? 如果用戶端在分布式計算中關閉其連接配接,該怎麼辦?

我們承諾在随後的部落格文章中,将與您分享我們在這些方面的進展。

總結:CockroachDB中SQL的處理

現在你知道CockroachDB對于SQL查詢支援兩種模式:本地以及分布式。

在本地執行引擎中,資料從它本來應該在的位置被拉到了負責處理的某個節點處。XXXXXXXX。

在分布式執行引擎中,資料是被運送到靠近資料存儲的地方進行處理的,通常在多個節點上同時運作。在底層,我們正在建構一個通用的分布式計算引擎,使用dataflow網絡作為基本的抽象。我們計劃稍後向所有使用者公開此功能,以滿足各種分布式處理工作負載,但目前我們隻是使用它來加速一些使用過濾,連接配接,排序和聚合的SQL查詢,特别是那些無法通過,或者是不想使用經典技術(例如索引或異步實體化視圖)進行手動優化。

兩個引擎都可以同時處于活動狀态。然而,由于我們正在緻力于分布式執行,是以我們希望使用者做一下這種嘗試:對于那些可以采用分布式的查詢,我們決定預設使用分布式執行。您可以使用SET覆寫此預設值,也可以使用EXPLAIN(DISTSQL)來檢查給定的查詢是否可以分發。随後的部落格文章将進一步詳細說明這一點。