偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

突破關系型數(shù)據(jù)庫桎梏:云原生數(shù)據(jù)庫中間件核心剖析

運維 數(shù)據(jù)庫運維 云原生
NewSQL的三種分類中,新架構和云數(shù)據(jù)庫涉及了太多與數(shù)據(jù)庫相關的底層實現(xiàn),為了保證本文的范圍不至太過發(fā)散,我們重點介紹透明化分片數(shù)據(jù)庫中間件的核心功能與實現(xiàn)原理,另外兩種類型的NewSQL在核心功能上類似,但實現(xiàn)原理會有所差別。

數(shù)據(jù)庫技術的發(fā)展與變革方興未艾,NewSQL的出現(xiàn),只是將各種所需技術組合在一起,而這些技術組合在一起所實現(xiàn)的核心功能,推動著云原生數(shù)據(jù)庫的發(fā)展。在上一篇文章《關系型數(shù)據(jù)庫尚能飯否?NoSQL、NewSQL誰能接棒?》中我們已經(jīng)了解了云原生數(shù)據(jù)庫的發(fā)展背景,所以本文會有針對性地深入解讀云原生數(shù)據(jù)庫的相關內(nèi)容。

NewSQL的三種分類中,新架構和云數(shù)據(jù)庫涉及了太多與數(shù)據(jù)庫相關的底層實現(xiàn),為了保證本文的范圍不至太過發(fā)散,我們重點介紹透明化分片數(shù)據(jù)庫中間件的核心功能與實現(xiàn)原理,另外兩種類型的NewSQL在核心功能上類似,但實現(xiàn)原理會有所差別。

一、數(shù)據(jù)分片

傳統(tǒng)的將數(shù)據(jù)集中存儲至單一數(shù)據(jù)節(jié)點的解決方案,在性能和可用性兩方面已經(jīng)難于滿足互聯(lián)網(wǎng)的海量數(shù)據(jù)場景。由于關系型數(shù)據(jù)庫大多采用B+樹類型的索引,在數(shù)據(jù)量超過閾值的情況下,索引深度的增加也將使得磁盤訪問的IO次數(shù)增加,進而導致查詢性能的大幅下降;同時高并發(fā)訪問請求也使得集中式數(shù)據(jù)庫成為系統(tǒng)的最大瓶頸。

在傳統(tǒng)關系型數(shù)據(jù)庫無法滿足互聯(lián)網(wǎng)場景需要的情況下,將數(shù)據(jù)存儲至原生支持分布式的NoSQL的嘗試越來越多。但NoSQL對SQL的不兼容性以及生態(tài)圈的不完善,使得它們在與關系型數(shù)據(jù)庫的博弈中始終無法完成致命一擊,關系型數(shù)據(jù)庫的地位依然不可撼動。

數(shù)據(jù)分片,指按照某個維度將存放在單一數(shù)據(jù)庫中的數(shù)據(jù)分散地存放至多個數(shù)據(jù)庫或表中,以達到提升性能瓶頸及可用性的效果。數(shù)據(jù)分片的有效手段是對關系型數(shù)據(jù)庫進行分庫或分表。分庫和分表均可以有效避免因為數(shù)據(jù)量超過可承受閾值而產(chǎn)生的查詢瓶頸。

除此之外,分庫還能夠用于有效分散對數(shù)據(jù)庫單點的訪問量;而分表則能夠提供盡量將分布式事務轉(zhuǎn)化為本地事務的可能。使用多主多從的分片方式,可以有效避免數(shù)據(jù)單點,從而提升數(shù)據(jù)架構的可用性。

1、垂直分片

垂直分片又稱為縱向拆分,它的核心理念是專庫專用。在拆分之前,一個數(shù)據(jù)庫由多個數(shù)據(jù)表構成,每個表對應著不同的業(yè)務。而拆分之后,則按照業(yè)務將表進行歸類,分布到不同的數(shù)據(jù)庫中,從而將壓力分擔到不同的數(shù)據(jù)庫之上,如圖:

突破關系型數(shù)據(jù)庫桎梏:云原生數(shù)據(jù)庫中間件核心剖析

2、水平分片

水平分片又稱為橫向拆分。相對于垂直分片,水平分片不是將數(shù)據(jù)根據(jù)業(yè)務邏輯分類,而是按照某個字段的某種規(guī)則將數(shù)據(jù)分散到多個庫或表中,每個分片僅包含其中的一部分數(shù)據(jù)。

例如,根據(jù)ID的最后一位以10取余,尾數(shù)是0的放入0庫(表),尾數(shù)是1的放入1庫(表)。如圖:

突破關系型數(shù)據(jù)庫桎梏:云原生數(shù)據(jù)庫中間件核心剖析

為了解決關系型數(shù)據(jù)庫面對海量數(shù)據(jù)時因數(shù)據(jù)量過大而導致的性能問題,將數(shù)據(jù)進行分片是行之有效的解決方案。

將集中于單一節(jié)點的數(shù)據(jù)拆分并分別存儲到多個數(shù)據(jù)庫或表,稱為分庫分表。分庫可以有效分散由高并發(fā)所帶來的對數(shù)據(jù)庫訪問的壓力。分表雖然無法緩解數(shù)據(jù)庫壓力,但僅跨分表的更新操作,依然能使用數(shù)據(jù)庫原生的ACID事務;而一旦涉及到跨庫的更新操作,分布式事務的問題就會變得無比復雜。

通過分庫和分表拆分數(shù)據(jù)使得各個表的數(shù)據(jù)量保持在閾值以下。垂直分片往往需要對架構和設計進行調(diào)整,通常來講,是來不及應對互聯(lián)網(wǎng)快速變化的業(yè)務需求的,而且它也無法真正解決單點瓶頸。而水平分片從理論上突破了單機數(shù)據(jù)量處理的瓶頸,并且擴展相對自由,是分庫分表的標準解決方案。

分庫和讀寫分離疏導流量是應對高訪問量的常見手段。分表雖然可以解決海量數(shù)據(jù)導致的性能問題,但無法解決過多請求訪問同一數(shù)據(jù)庫導致的響應變慢問題。所以水平分片通常采取分庫的方式,一并解決數(shù)據(jù)量和訪問量巨大的問題。讀寫分離是另一個疏導流量的辦法,但讀寫數(shù)據(jù)間的延遲是架構設計時需要考慮的問題。

雖然分庫可以解決上述問題,但分布式架構在獲得了收益的同時,也帶來了新的問題。面對如此散亂的分庫分表之后的數(shù)據(jù),應用開發(fā)和運維人員對數(shù)據(jù)庫的操作變得異常繁重就是其中的重要挑戰(zhàn)之一。他們需要知道什么樣的數(shù)據(jù)需要從哪個具體的數(shù)據(jù)庫的分表中去獲取。

新架構的NewSQL與數(shù)據(jù)分片中間件在這個功能的處理方式上是不同的:

  • 新架構的NewSQL會重新設計數(shù)據(jù)庫存儲引擎,將同一表中的數(shù)據(jù)存儲在分布式文件系統(tǒng)中。
  • 數(shù)據(jù)分片中間件則是盡量透明化分庫分表所帶來的影響,讓使用方盡量像使用一個數(shù)據(jù)庫一樣使用水平分片之后的數(shù)據(jù)庫。

跨庫事務是分布式數(shù)據(jù)庫要面對的棘手事情。合理采用分表,可以在降低單表數(shù)據(jù)量的情況下,盡量使用本地事務,善于使用同庫不同表可有效避免分布式事務帶來的麻煩。在不能避免跨庫事務的場景,有些業(yè)務仍需保持事務的一致性。而基于XA的分布式事務由于性能低下,無法被互聯(lián)網(wǎng)公司所采納,大多采用最終一致性的柔性事務代替分布式事務。

3、讀寫分離

面對日益增加的系統(tǒng)訪問量,數(shù)據(jù)庫的吞吐量面臨著巨大瓶頸。對于同一時間有大量并發(fā)讀操作和較少寫操作類型的應用系統(tǒng)來說,將單一的數(shù)據(jù)庫拆分為主庫和從庫,主庫負責處理事務性的增刪改操作,從庫負責處理查詢操作,能夠有效的避免由數(shù)據(jù)更新導致的行鎖,使得整個系統(tǒng)的查詢性能得到極大改善。

通過一主多從的配置方式,可以將查詢請求均勻分散到多個數(shù)據(jù)副本,能夠進一步提升系統(tǒng)的處理能力。

使用多主多從的方式,不但能夠提升系統(tǒng)的吞吐量,還能夠提升系統(tǒng)的可用性,可以達到在任何一個數(shù)據(jù)庫宕機,甚至磁盤物理損壞的情況下仍然不影響系統(tǒng)的正常運行。

讀寫分離本質(zhì)上是數(shù)據(jù)分片的一種。與將數(shù)據(jù)根據(jù)分片鍵打散至各個數(shù)據(jù)節(jié)點的水平分片不同,讀寫分離則是根據(jù)SQL語義的分析,將讀和寫請求分別路由至主庫與從庫。讀寫分離的數(shù)據(jù)節(jié)點中的數(shù)據(jù)是一致的,而水平分片每個數(shù)據(jù)節(jié)點的數(shù)據(jù)內(nèi)容卻并不相同。將水平分片和讀寫分離聯(lián)合使用,能夠更加有效的提升系統(tǒng)性能,但同時也讓系統(tǒng)維護更復雜。

雖然讀寫分離可以提升系統(tǒng)的吞吐量和可用性,但同時也帶來了數(shù)據(jù)不一致的問題,這包括多個主庫之間的數(shù)據(jù)一致性及主庫與從庫之間的數(shù)據(jù)一致性問題。并且,讀寫分離也帶來了與數(shù)據(jù)分片同樣的問題,它也會使得應用開發(fā)和運維人員對數(shù)據(jù)庫的操作和運維變得更加復雜。

透明化讀寫分離所帶來的影響,讓使用方盡量像使用一個數(shù)據(jù)庫一樣使用主從數(shù)據(jù)庫,是讀寫分離的主要功能。

4、核心流程

數(shù)據(jù)分片核心是由SQL解析、SQL路由、SQL改寫、SQL執(zhí)行及結(jié)果歸并的流程組成。為了保持原有的應用程序?qū)崿F(xiàn)低接入成本,則需兼容對數(shù)據(jù)庫的訪問,因此需要進行數(shù)據(jù)庫協(xié)議的適配。

協(xié)議適配

NewSQL對傳統(tǒng)關系型數(shù)據(jù)庫的兼容性,除了SQL之外,兼容數(shù)據(jù)庫的協(xié)議可以降低使用方的接入成本。開源的關系型數(shù)據(jù)庫均能通過實現(xiàn)它的協(xié)議標準,將自己的產(chǎn)品裝扮成原生的關系型數(shù)據(jù)庫。

由于MySQL和PostgreSQL流行度較高,很多NewSQL會實現(xiàn)它們的傳輸協(xié)議,讓使用MySQL和PostgreSQL的用戶能夠無需修改業(yè)務代碼就自動接入NewSQL產(chǎn)品。

MySQL協(xié)議

MySQL是當前最為流行的開源數(shù)據(jù)庫。要了解它的協(xié)議,可以通過MySQL的基本數(shù)據(jù)類型、協(xié)議包結(jié)構、連接階段和命令階段這4方面入手。

基本數(shù)據(jù)類型

MySQL協(xié)議包中所有的內(nèi)容均由MySQL所定義的基本數(shù)據(jù)類型組成,具體數(shù)據(jù)類型參見下表:

突破關系型數(shù)據(jù)庫桎梏:云原生數(shù)據(jù)庫中間件核心剖析

MySQL基本數(shù)據(jù)類型

在需要將二進制數(shù)據(jù)轉(zhuǎn)換為MySQL可理解的數(shù)據(jù)時,MySQL協(xié)議包將根據(jù)數(shù)據(jù)類型預先定義的位數(shù)讀取,并轉(zhuǎn)換為相應的數(shù)字或字符串;反之亦然,MySQL會將每個字段按照規(guī)范中規(guī)定的長度寫入?yún)f(xié)議包。

協(xié)議包結(jié)構

MySQL協(xié)議由一個或多個MySQL協(xié)議包(MySQL Packet)組成。無論類型如何,它均由消息長度(Payload Length)、序列主鍵(Sequence ID)和消息體(Payload)這3部分組成:

  • 消息長度為int<3>類型。它表示隨后的消息體所占用的字節(jié)總數(shù)。需要注意的是,消息長度并不包含序列主鍵的占位在內(nèi)。
  • 序列主鍵為int<1>類型。它表示一次請求后返回的多個MySQL協(xié)議包中,每個協(xié)議包的序號。占位為1字節(jié)的序列主鍵最大值為0xff,即十進制的255,但這并非表示每次請求最多只能包含255個MySQL協(xié)議包,超過255的序列主鍵將再次從0開始計數(shù)。例如一次查詢可能返回幾十萬的記錄,那么MySQL協(xié)議包只需保證其序列主鍵連續(xù),將大于255的序列主鍵重置為0,重新開始計數(shù)即可。
  • 消息體的長度為消息長度所聲明的字節(jié)數(shù)。它是MySQL協(xié)議包中真正的業(yè)務數(shù)據(jù),根據(jù)不同的協(xié)議包類型,消息體的內(nèi)容也不同。

連接階段

連接階段用于創(chuàng)建MySQL的客戶端與服務端的通信管道。該階段主要執(zhí)行交換并匹配MySQL客戶端與服務端的版本功能描述(Capability Negotiation)、創(chuàng)建SSL通信管道及驗證授權這3個任務。下圖以MySQL服務端為視角繪制了連接創(chuàng)建流程圖:

MySQL連接階段流程圖

該圖并未包含MySQL服務端與客戶端的交互。實際上,MySQL的連接創(chuàng)建是由客戶端發(fā)起的。

MySQL服務端在接收到客戶端的連接請求后,先進行服務端和客戶端版本間所具有的功能信息的交換和匹配(Capability Negotiation),然后根據(jù)兩端的協(xié)商結(jié)果生成不同格式的初始化握手協(xié)議包,并向客戶端寫入改協(xié)議包。協(xié)議包中包括由MySQL服務端分配的連接主鍵、服務端當前版本功能描述(Capabilities)以及為驗證授權生成的密文。

MySQL客戶端在接收到服務端發(fā)送的握手協(xié)議包后,將發(fā)送握手協(xié)議響應包。該協(xié)議包中主要包含的信息是用于數(shù)據(jù)庫訪問的用戶名及加密后的密碼密文。

MySQL服務端接收到握手協(xié)議響應包之后,即進行授權校驗,并將校驗結(jié)果返回至客戶端。

命令階段

連接階段成功之后,則進入命令執(zhí)行的交互階段。MySQL一共有32個命令協(xié)議包,具體類型參見下圖:

MySQL命令包

MySQL的命令協(xié)議包分為4個大類,分別是:文本協(xié)議、二進制協(xié)議、存儲過程及數(shù)據(jù)復制協(xié)議。

協(xié)議包消息體中的首位用于標識命令類型。協(xié)議包根據(jù)名稱即可望文生義,在這里無需一一解釋它們的具體用途,下文會解析幾個重點的MySQL命令協(xié)議包:

  • COM_QUERY

COM_QUERY是MySQL用于以明文格式查詢的重要命令,它對應JDBC中的java.sql.Statement。COM_QUERY命令本身較為簡單,它由標識符和SQL組成:

1 [03] COM_QUERY

string[EOF] the query the server shall execute

COM_QUERY的響應協(xié)議包則較為復雜,見下圖:

MySQL查詢命令流程圖

COM_QUERY根據(jù)其場景有可能返回4種類型,它們是:查詢結(jié)果、更新結(jié)果、文件執(zhí)行結(jié)果及錯誤結(jié)果。

當執(zhí)行過程中出現(xiàn)如網(wǎng)絡斷開、SQL語法不正確等錯誤時,MySQL協(xié)議要求將協(xié)議包首位設置為0xff,并將錯誤信息封裝至ErrPacket協(xié)議包返回。

通過文件執(zhí)行COM_QUERY的情況并不常見,此處不再過多說明。

對于更新請求,MySQL協(xié)議要求將協(xié)議包首位設置為0x00,并返回OkPacket協(xié)議包。OkPacket協(xié)議包需要包含本次更新操作所影響的行記錄數(shù)及最后插入的主鍵值信息。

查詢請求最為復雜,它需要將讀取int<lenenc>的方式獲得結(jié)果集字段的數(shù)目創(chuàng)建為獨立的FIELD_COUNT協(xié)議包返回。然后再依次將返回字段的每一列詳細信息分別生成獨立的COLUMN_DEFINITION協(xié)議包,查詢字段的元數(shù)據(jù)信息最終以一個EofPacket結(jié)束。之后便可以開始逐行生成數(shù)據(jù)協(xié)議包Text Protocol Resultset Row,它本身并不關注數(shù)據(jù)的具體類型,會統(tǒng)一將其轉(zhuǎn)換為string<lenenc>格式。數(shù)據(jù)協(xié)議包最終依然以一個EofPacket結(jié)束。

對應于JDBC中java.sql.PreparedStatement的操作,則是由MySQL協(xié)議包中的二進制協(xié)議組成,它們由COM_STMT_PREPARE、COM_STMT_EXECUTE、COM_STMT_ CLOSE、COM_STMT_RESET和COM_ STMT_SEND_LONG_DATA這5個協(xié)議包組成。其中最為重要的是COM_STMT_PREPARE和COM_STMT_ EXECUTE,它們分別對應JDBC中的connection.prepareStatement方法以及connection.execute&connection.executeQuery&connection.executeUpdate方法。

  • COM_STMT_PREPARE

COM_STMT_PREPARE協(xié)議包與COM_QUERY協(xié)議包類似,同樣是由命令標識符和SQL組成:

1 [16] COM_STMT_PREPARE

string[EOF] the query to prepare

COM_STMT_PREPARE協(xié)議包的返回值并非查詢結(jié)果,而是由statement_id、列數(shù)目和參數(shù)數(shù)目等信息組成的響應協(xié)議包。statement_id是由MySQL分配給完成預編譯之后的SQL的唯一標識,通過statement_id即可從MySQL中獲取相應的SQL。

由COM_STMT_PREPARE命令注冊過的SQL,只需將statement_id傳給COM_STMT_EXECUTE命令即可,無需將SQL本身再次傳入,節(jié)省了無謂的網(wǎng)絡帶寬消耗。

而且MySQL可以根據(jù)COM_STMT_PREPARE傳入的SQL預編譯為抽象語法樹以供復用,進而提升SQL的執(zhí)行效率。采用COM_QUERY的方式執(zhí)行SQL,則需要將每條SQL重新編譯。這也是PreparedStatement比Statement效率更佳的原因所在。

  • COM_STMT_EXECUTE

COM_STMT_EXECUTE協(xié)議包主要由statement-id和與SQL的配對的參數(shù)組成。它使用了一個名為-bitmap的數(shù)據(jù)結(jié)構,用于標識參數(shù)中的空值。

COM_STMT_EXECUTE命令的響應協(xié)議包與COM_QUERY命令的響應協(xié)議包類似,都是采用字段元數(shù)據(jù)和查詢結(jié)果集的格式返回,中間依然使用EofPacket間隔。

有所不同的是,COM_STMT_EXECUTE命令的響應協(xié)議包使用Binary Protocol Resultset Row來代替Text Protocol Resultset Row,它不會無視數(shù)據(jù)的類型統(tǒng)一轉(zhuǎn)換為字符串,而是根據(jù)返回數(shù)據(jù)的類型,寫入相應的MySQL基本數(shù)據(jù)類型,進一步節(jié)省網(wǎng)絡傳輸?shù)膸挕?/p>

其他協(xié)議

除了MySQL協(xié)議,PostgreSQL協(xié)議和SQLServer協(xié)議也是完全開源的,可以通過同樣的方式實現(xiàn)。而另一個常用的數(shù)據(jù)庫Oracle協(xié)議并不開源,無法通過這種方式實現(xiàn)。

SQL解析

相對于其他編程語言,SQL是比較簡單的。不過,它依然是一門完善的編程語言,因此解析SQL語法與解析其他編程語言(如:Java語言、C語言、Go語言等)并無本質(zhì)區(qū)別。

解析過程分為詞法解析和語法解析。先通過詞法解析將SQL拆分為一個個不可再分的單詞。再使用語法解析器將SQL轉(zhuǎn)換為抽象語法樹。最后通過訪問抽象語法樹,提煉出解析上下文。

解析上下文包括表、選擇項、排序項、分組項、聚合函數(shù)、分頁信息、查詢條件。如果是分片中間件類型的NewSQL還需要記錄可能修改的占位符標記。

將SQL:select username, ismale from userinfo where age > 20 and level > 5 and 1 = 1解析為抽象語法樹:

抽象語法樹

生成抽象語法樹的第三方工具有很多,ANTLR是不錯的選擇。它可以通過開發(fā)者定義的規(guī)則生成抽象語法樹的Java代碼并提供訪問者接口。相比于代碼生成,手寫抽象語法樹在執(zhí)行效率方面會更加高效,但是工作量也比較大。對性能要求高的場景中,可以考慮定制化抽象語法樹。

請求路由

根據(jù)解析上下文匹配數(shù)據(jù)分片策略,并生成路由路徑。對于攜帶分片鍵的SQL路由,根據(jù)分片鍵的不同可以劃分為單片路由(分片操作符是等號)、多片路由(分片操作符是IN)和范圍路由(分片操作符是BETWEEN)。不攜帶分片鍵的SQL則采用廣播路由。

分片策略通??捎蓴?shù)據(jù)庫內(nèi)置或由用戶方配置。數(shù)據(jù)庫內(nèi)置的方案較為簡單,內(nèi)置的分片策略大致可分為尾數(shù)取模、哈希、范圍、標簽、時間等;由用戶方配置的分片策略則更加靈活,可以根據(jù)使用方需求定制復合分片策略。

SQL改寫

新架構的NewSQL無需SQL改寫,這部分主要是針對分片中間件類型的NewSQL。它用于將SQL改寫為在真實數(shù)據(jù)庫中可以正確執(zhí)行的語句。包括將邏輯表名稱替換為真實表名稱,將分頁信息的起始取值和結(jié)束取值改寫,增加為排序、分組和自增主鍵使用的補列,將AVG改寫為SUM/COUNT等。

結(jié)果歸并

將多個執(zhí)行結(jié)果集歸并并統(tǒng)一對應用端輸出。結(jié)果歸并包括流式歸并和內(nèi)存歸并:

  • 流式歸并用于簡單查詢、排序查詢、分組查詢及排序和分組但排序項和分組項完全一致的場景,流式歸并結(jié)果集的遍歷方式是通過每一次調(diào)用next方法取出,無需占用額外的內(nèi)存。
  • 內(nèi)存歸并則需要將結(jié)果集中所有數(shù)據(jù)加載至內(nèi)存處理,如果結(jié)果集數(shù)據(jù)過多,會占用大量內(nèi)存。

二、分布式事務

前文提到過,數(shù)據(jù)庫事務是需要滿足ACID(原子性、一致性、隔離性、持久性)這四個特性的:

  • 原子性(Atomicity)指事務作為整體來執(zhí)行,要么全部執(zhí)行,要么全不執(zhí)行。
  • 一致性(Consistency)指事務應確保數(shù)據(jù)從一個一致的狀態(tài)轉(zhuǎn)變?yōu)榱硪粋€一致的狀態(tài)。
  • 隔離性(Isolation)指多個事務并發(fā)執(zhí)行時,一個事務的執(zhí)行不應影響其他事務的執(zhí)行。
  • 持久性(Durability)指已提交的事務修改數(shù)據(jù)會被持久保存。

在單一數(shù)據(jù)節(jié)點中,事務僅限于對單一數(shù)據(jù)庫資源的訪問控制,稱之為本地事務。但在基于SOA的分布式應用環(huán)境下,越來越多的應用要求對多個數(shù)據(jù)庫資源、多個服務的訪問都能納入到同一個事務當中,分布式事務應運而生。

關系型數(shù)據(jù)庫雖然對本地事務提供了完美的ACID原生支持。但在分布式的場景下,它卻成為系統(tǒng)性能的桎梏。如何讓數(shù)據(jù)庫在分布式場景下滿足ACID的特性或找尋相應的替代方案,是分布式事務的重點工作。

1、XA協(xié)議

最早的分布式事務模型是由X/Open國際聯(lián)盟提出的X/Open Distributed Transaction Processing(DTP)模型,簡稱XA協(xié)議。

DTP模型中通過一個全局事務管理器與多個資源管理器進行交互。全局事務管理器負責管理全局事務狀態(tài)和參與事務的資源,資源管理器則負責具體的資源操作,DTP模型與應用程序的關系見下圖:

DTP模型

XA協(xié)議使用兩階段提交來保證分布式事務原子性。它將提交過程分為準備階段和提交階段。

  • 在準備階段時,全局事務管理器向每個資源管理器發(fā)送準備消息,用于確認本地事務操作的成功與否;
  • 在提交階段時,若全局事務管理器收到了所有資源管理器回復的成功消息,則向每個資源管理器發(fā)送提交消息,否則發(fā)送回滾消息。資源管理器根據(jù)接收到的消息對本地事務進行提交或回滾操作。

下圖展示了XA協(xié)議的事務流程:

XA事務流程

二階段提交是XA協(xié)議的標準實現(xiàn)。它將分布式事務的提交拆分為兩階段:prepare和commit/rollback。

開啟XA全局事務后,所有子事務會按照本地默認的隔離級別鎖定資源,并記錄undo和redo日志,然后由TM發(fā)起prepare投票,詢問所有的子事務是否可以進行提交:當所有子事務反饋的結(jié)果為“yes”時,TM再發(fā)起commit;若其中任何一個子事務反饋的結(jié)果為“no”,TM則發(fā)起rollback;如果在prepare階段的反饋結(jié)果為yes,而commit的過程中出現(xiàn)宕機等異常時,則在節(jié)點服務重啟后,可根據(jù)XA recover再次進行commit補償,以保證數(shù)據(jù)的一致性。

基于XA協(xié)議實現(xiàn)的分布式事務對業(yè)務侵入很小,它最大優(yōu)勢就是對使用方透明,用戶可以像使用本地事務一樣使用基于XA協(xié)議的分布式事務。XA協(xié)議能夠嚴格保障事務ACID特性。

但嚴格保障事務ACID特性是一把雙刃劍。

事務執(zhí)行在過程中需要將所需資源全部鎖定,它更加適用于執(zhí)行時間確定的短事務,對于長事務來說,整個事務進行期間對數(shù)據(jù)的獨占,將導致對熱點數(shù)據(jù)依賴的業(yè)務系統(tǒng)并發(fā)性能衰退明顯。因此,在高并發(fā)的性能至上場景中,基于XA協(xié)議的分布式事務并不是最佳選擇。

2、柔性事務

如果將實現(xiàn)了ACID事務要素的事務稱為剛性事務的話,那么基于BASE事務要素的事務則稱為柔性事務。BASE是基本可用(Basically Available)、柔性狀態(tài)(Soft state)和最終一致性(Eventually consistent)這三個要素的縮寫:

  • 基本可用保證分布式事務參與方不一定同時在線;
  • 柔性狀態(tài)允許系統(tǒng)狀態(tài)更新有一定的延時,這個延時對客戶來說不一定能夠察覺;
  • 最終一致性通常是通過消息可達的方式保證系統(tǒng)的最終一致性。

在ACID事務中對隔離性的要求很高,在事務執(zhí)行過程中,必須將所有的資源鎖定。柔性事務的理念則是通過業(yè)務邏輯將互斥鎖操作從資源層面上移至業(yè)務層面。通過放寬對強一致性要求,來換取系統(tǒng)吞吐量的提升。

由于在分布式系統(tǒng)中,可能會出現(xiàn)超時重試的情況,因此柔性事務中的操作必須是冪等的,需要通過冪等來避免多次請求所帶來的問題。實現(xiàn)柔性事務的方案主要有最大努力送達、Saga和TCC。

最大努力送達

是最簡單的一種柔性事務,它適合對于數(shù)據(jù)庫的操作最終一定能夠成功的場景。由NewSQL自動記錄執(zhí)行失敗的SQL,并反復嘗試,直至執(zhí)行成功。使用最大努力送達型的柔性事務是沒有回滾功能的。

這種類型的柔性事務實現(xiàn)最為簡單,但是對場景的要求十分苛刻。這種策略的優(yōu)點是無鎖定資源時間,性能損耗小。缺點是嘗試多次提交失敗后,無法回滾,它僅適用于事務最終一定能夠成功的業(yè)務場景。因此它是通過事務回滾功能上的妥協(xié),來換取性能的提升。

Saga

Saga源于1987年由Hector Garcaa-Molrna和Kenneth Salem發(fā)表的論文。

論文參考鏈接:www.cs.cornell.edu/andru/cs711/2002fa/reading/sagas.pdf

Saga事務更適合使用長事務的場景。它由多個本地事務所組成,每個本地事務有相應的執(zhí)行模塊和補償模塊,任何一個本地事務出錯時,可以通過調(diào)用相關的補充方法達到事務的最終一致性。

Saga模型將一個分布式事務拆分為多個本地事務,每個本地事務都有相應的執(zhí)行模塊(Transaction)和補償模塊(Compensation)。當Saga事務中的任一本地事務執(zhí)行失敗時,可以通過調(diào)用其相關補償方法恢復之前的事務,以達到事務最終的一致性。

當每個Saga子事務T1,T2,…,Tn都有對應的補償定義C1,C2,…,Cn-1,那么Saga系統(tǒng)可以保證:

  • 子事務序列T1,T2,…,Tn得以完成 。這是事務的最佳情況,即無需回滾的情況。
  • 或者序列T1,T2,…,Tx, Cx,…,C2,C1,(其中x小于n)得以完成。它能夠保證當回滾發(fā)生時,補償操作按照正向操作相反的順序依次執(zhí)行。

Saga模型同時支持正向恢復以及逆向恢復。正向恢復是指重試當前失敗的事務,它的實現(xiàn)前提是每個子事務都能夠最終執(zhí)行成功;向后恢復則是前文提及的,在任一子事務失敗時,補償所有已完成的事務。

顯然,正向恢復沒有必要提供補償事務,如果在業(yè)務中的子事務最終總會成功,那么向前恢復則能夠降低Saga模型的使用復雜度。另外,如果補償事務難以實現(xiàn),則正向恢復也是不錯的選擇。

雖然在理論上來講,補償事務永不失敗。然而,在分布式的世界中,服務器可能會宕機、網(wǎng)絡可能會失敗,甚至數(shù)據(jù)中心也可能會停電。因此,需要提供故障恢復后回退的機制,比如人工干預。

Saga模型沒有XA協(xié)議中的準備階段,因此事務沒有實現(xiàn)隔離性。如果兩個Saga事務同時操作同一資源則會產(chǎn)生更新丟失,臟數(shù)據(jù)讀取等問題。這就需要使用Saga事務的應用程序需要在應用層面加入資源鎖定的邏輯。

TCC

TCC(Try-Confirm-Cancel)分布式事務模型通過對業(yè)務邏輯的分解來實現(xiàn)分布式事務。顧名思義,TCC事務模型需要業(yè)務系統(tǒng)提供以下三段業(yè)務邏輯:

  • Try。完成業(yè)務檢查,預留業(yè)務所需資源。Try操作是整個TCC的精髓所在,可靈活選擇業(yè)務資源鎖的粒度。
  • Confirm。執(zhí)行業(yè)務邏輯,直接使用Try階段預留的業(yè)務資源,無需再次做業(yè)務檢查。
  • Cancel。釋放Try階段預留的業(yè)務資源。

TCC模型僅提供兩階段原子提交協(xié)議,保證分布式事務原子性。事務的隔離交給業(yè)務邏輯來實現(xiàn)。TCC模型的隔離性思想就是通過業(yè)務的改造,從數(shù)據(jù)庫資源層面加鎖上移至業(yè)務層面加鎖,從而釋放底層數(shù)據(jù)庫鎖資源,放寬分布式事務鎖協(xié)議,提高系統(tǒng)的并發(fā)性。

雖然在柔性事務中,TCC事務模型的功能最強,但需要應用方負責提供實現(xiàn)Try、Confirm和Cancel操作的三個接口,供事務管理器調(diào)用。因此業(yè)務方改造的成本較高。

以A賬戶向B賬戶匯款100元為例,下圖展示了TCC對業(yè)務的改造:

匯款服務和收款服務分別需要實現(xiàn),Try-Confirm-Cancel接口,并在業(yè)務初始化階段將其注入到TCC事務管理器中。

匯款服務

Try

  • 檢查A賬戶有效性,即查看A賬戶的狀態(tài)是否為“轉(zhuǎn)帳中”或者“凍結(jié)”;
  • 檢查A賬戶余額是否充足;
  • 從A賬戶中扣減100元,并將狀態(tài)置為“轉(zhuǎn)賬中”;
  • 預留扣減資源,將從A往B賬戶轉(zhuǎn)賬100元這個事件存入消息或者日志中。

Confirm

  • 不做任何操作。

Cancel

  • A賬戶增加100元;
  • 從日志或者消息中,釋放扣減資源。

收款服務

Try

  • 檢查B賬戶賬戶是否有效。

Confirm

  • 讀取日志或者消息,B賬戶增加100元;
  • 從日志或者消息中,釋放扣減資源。

由此可以看出,TCC模型對業(yè)務的侵入較強,改造的難度較大。

消息驅(qū)動

消息一致性方案是通過消息中間件保證上下游應用數(shù)據(jù)操作的一致性?;舅悸肥菍⒈镜夭僮骱桶l(fā)送消息放在一個本地事務中,下游應用向消息系統(tǒng)訂閱該消息,收到消息后執(zhí)行相應操作。本質(zhì)上是依靠消息的重試機制,達到最終一致性。下圖是消息驅(qū)動的事務模型:

消息驅(qū)動的缺點是:耦合度高,需要在業(yè)務系統(tǒng)中引入消息中間件,導致系統(tǒng)復雜度增加。

總的來說,基于ACID的強一致性事務和基于BASE的最終一致性事務都不是銀彈,只有在最適合的場景中才能發(fā)揮它們的最大長處。詳細對比一下它們之前的區(qū)別,以幫助開發(fā)者進行技術選型。由于消息驅(qū)動與業(yè)務系統(tǒng)的耦合度較高,因此不列入對比表格:

一味的追求強一致性未必是最合理的解決方案。對于分布式系統(tǒng)來說,建議使用“外柔內(nèi)剛”的設計方案。外柔指的是在跨數(shù)據(jù)分片的情況下使用柔性事務,保證數(shù)據(jù)最終一致即可,并且換取最佳性能;內(nèi)剛則是指在同一數(shù)據(jù)分片內(nèi)使用本地事務,以達到ACID的效果。

三、數(shù)據(jù)庫治理

1、基礎治理

前文講述的服務治理,在數(shù)據(jù)庫的基礎治理部分大都是通用的。主要包括配置中心、注冊中心、限流、熔斷、失效轉(zhuǎn)移、調(diào)用鏈路追蹤等:

  • 配置中心用于配置集中化以及動態(tài)配置更新及通知下發(fā);
  • 注冊中心用于服務發(fā)現(xiàn),這里的服務是指數(shù)據(jù)庫中間層實例本身,通過它可以實現(xiàn)狀態(tài)監(jiān)測及自動通知,進而使得數(shù)據(jù)庫中間件具備高可用和自我治愈能力;
  • 限流用于流量的過載保護,分為數(shù)據(jù)庫中間件本身的流量過載保護和對數(shù)據(jù)庫的流量過載保護;
  • 熔斷也是流量過載的保護措施之一,它的不同之處在于熔斷整個客戶端對數(shù)據(jù)庫的訪問,以保護數(shù)據(jù)庫能夠為其他流量正常的系統(tǒng)繼續(xù)提供服務,可以通過前文講的熔斷器模式實現(xiàn)自動熔斷機制;
  • 失效轉(zhuǎn)移用于多數(shù)據(jù)副本的情況,在數(shù)據(jù)完全一致的多數(shù)據(jù)節(jié)點中,當某一節(jié)點不可用后,可通過失效轉(zhuǎn)移的機制讓數(shù)據(jù)庫中間件訪問至另外有效的數(shù)據(jù)節(jié)點操作數(shù)據(jù);
  • 調(diào)用鏈路追蹤則是將對數(shù)據(jù)庫訪問的調(diào)用鏈路、性能、拓撲關系等指標以可視化的方式展現(xiàn)出來。

2、彈性伸縮

數(shù)據(jù)庫治理與服務治理不同的關鍵點在于,數(shù)據(jù)庫是有狀態(tài)的,每個數(shù)據(jù)節(jié)點都有自己持久化的數(shù)據(jù),因此很難像服務化一樣做到彈性伸縮。

當系統(tǒng)的訪問量和數(shù)據(jù)量超過之前評估的預期時,往往涉及到對數(shù)據(jù)庫的重新分片。雖然使用日期分片等策略時,可以在無需遷移遺留數(shù)據(jù)的情況下直接擴容,但在大部分場景中,數(shù)據(jù)庫中的遺留數(shù)據(jù)往往無法直接映射到新的分片策略中。分片策略的修改則需要進行數(shù)據(jù)的遷移。

在傳統(tǒng)的系統(tǒng)中,停止服務進行數(shù)據(jù)遷移,遷移結(jié)束之后再重啟服務是行之有效的解決方案。但這種方案使得業(yè)務方的數(shù)據(jù)遷移成本非常高,需要業(yè)務方工程師精準的評估數(shù)據(jù)量。

在互聯(lián)網(wǎng)場景中,系統(tǒng)可用性要求極高,而且業(yè)務爆發(fā)性增長的可能性較傳統(tǒng)行業(yè)也更加常見。在云原生的服務架構模型中,彈性伸縮是常見的需求,并且可以比較輕松的實現(xiàn)。因此與服務對等的數(shù)據(jù)彈性伸縮功能,是云原生數(shù)據(jù)庫的重要能力。

除了系統(tǒng)預分片之外,彈性伸縮的另一個實現(xiàn)方案是在線數(shù)據(jù)遷移。在線數(shù)據(jù)遷移經(jīng)常被比喻為“在飛行過程中給飛機換引擎”,它最大的挑戰(zhàn)是如何保證遷移過程使服務不受影響。在線數(shù)據(jù)遷移可以在修改了數(shù)據(jù)庫的分片策略之后(比如將根據(jù)主鍵%4分為4個庫的分片方式改為根據(jù)主鍵%16的16個庫的分片方式),通過一系列的系統(tǒng)化操作,保證數(shù)據(jù)正確的遷移到新的數(shù)據(jù)節(jié)點的同時,讓依賴數(shù)據(jù)庫的服務完全無需感知。它可以分為以下4個步驟:

  • 同步線上雙寫。即同時將數(shù)據(jù)寫入分片策略修改前的原數(shù)據(jù)節(jié)點及分片策略修改后的新數(shù)據(jù)節(jié)點??梢酝ㄟ^一致性算法來保證雙寫的一致性,如前文介紹過的Paxos或Raft算法;
  • 歷史數(shù)據(jù)遷移。以離線的方式,將需要遷移到新數(shù)據(jù)節(jié)點部分的歷史存量數(shù)據(jù)從原有數(shù)據(jù)節(jié)點遷移過去。可以通過SQL的方式,也可以通過binlog等二進制方式進行處理;
  • 數(shù)據(jù)源切換。將讀寫請求切換至新的數(shù)據(jù)源,并停止對原數(shù)據(jù)節(jié)點的雙寫;
  • 清理冗余數(shù)據(jù)。在舊數(shù)據(jù)節(jié)點中,清理已遷移至新數(shù)據(jù)節(jié)點的相關數(shù)據(jù)。

在線數(shù)據(jù)遷移不僅可以做數(shù)據(jù)擴容,也可以通過同樣的方式在線進行DDL操作。由于數(shù)據(jù)庫原生的DDL操作是不支持事務的,而且在對包含大量數(shù)據(jù)表做DDL時會導致長時間鎖表,因此,通過在線數(shù)據(jù)遷移的方式,是能夠支持在線DDL操作的。在線DDL操作與數(shù)據(jù)遷移步驟是一致的,只需要在遷移之前新建一個DDL修改后的空表,然后根據(jù)上述4步驟進行即可。 

作者介紹

張亮,京東數(shù)科數(shù)據(jù)研發(fā)負責人。熱愛開源,目前主導兩個開源項目Elastic-Job和Sharding-Sphere(Sharding-JDBC)。擅長以Java為主分布式架構以及以Kubernetes和Mesos為主的云平臺方向,推崇優(yōu)雅代碼,對如何寫出具有展現(xiàn)力的代碼有較多研究。2018年初加入京東數(shù)科,現(xiàn)擔任數(shù)據(jù)研發(fā)負責人。目前主要精力投入在將Sharding-Sphere打造為業(yè)界一流的金融級數(shù)據(jù)解決方案之上。

責任編輯:龐桂玉 來源: 今日頭條
相關推薦

2018-02-24 19:37:33

Java8數(shù)據(jù)庫中間件

2017-12-01 05:04:32

數(shù)據(jù)庫中間件Atlas

2017-11-27 05:36:16

數(shù)據(jù)庫中間件TDDL

2017-11-27 05:06:42

數(shù)據(jù)庫中間件cobar

2011-08-10 13:03:58

CJDBC數(shù)據(jù)庫集群

2017-05-23 18:55:05

mysql-proxy數(shù)據(jù)庫架構

2020-10-15 08:34:32

數(shù)據(jù)庫中間件漫談

2024-12-06 08:29:29

2017-07-26 09:41:28

MyCATSQLMongoDB

2022-11-14 18:23:06

亞馬遜

2022-04-01 10:55:30

數(shù)據(jù)庫混合云建設

2021-09-06 10:24:12

鴻蒙HarmonyOS應用

2017-11-27 06:01:37

數(shù)據(jù)庫中間件中間層

2017-12-01 05:40:56

數(shù)據(jù)庫中間件join

2017-12-11 13:30:49

Go語言數(shù)據(jù)庫中間件

2022-03-07 10:27:21

云原生云計算數(shù)據(jù)庫

2023-01-26 00:18:53

云原生數(shù)據(jù)庫云資源

2017-07-18 17:07:40

數(shù)據(jù)庫 MyCATJoin

2017-07-18 17:35:16

數(shù)據(jù)庫MyCATPreparedSta

2017-11-03 11:02:08

數(shù)據(jù)庫中間件
點贊
收藏

51CTO技術棧公眾號