自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

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

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

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

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

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

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

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

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

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

1、垂直分片

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

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

2、水平分片

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

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

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

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

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

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

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

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

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

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

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

3、讀寫分離

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

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

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

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

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

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

4、核心流程

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

協(xié)議適配

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

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

MySQL協(xié)議

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

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

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

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

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

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

協(xié)議包結(jié)構(gòu)

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

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

連接階段

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

MySQL連接階段流程圖

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

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

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

MySQL服務(wù)端接收到握手協(xié)議響應(yīng)包之后,即進(jìn)行授權(quán)校驗(yàn),并將校驗(yàn)結(jié)果返回至客戶端。

命令階段

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

MySQL命令包

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

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

  • COM_QUERY

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

1 [03] COM_QUERY

string[EOF] the query the server shall execute

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

MySQL查詢命令流程圖

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

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

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

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

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

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

  • COM_STMT_PREPARE

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

1 [16] COM_STMT_PREPARE

string[EOF] the query to prepare

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

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

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

  • COM_STMT_EXECUTE

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

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

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

其他協(xié)議

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

SQL解析

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

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

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

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

抽象語法樹

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

請求路由

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

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

SQL改寫

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

結(jié)果歸并

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

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

二、分布式事務(wù)

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

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

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

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

1、XA協(xié)議

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

DTP模型中通過一個(gè)全局事務(wù)管理器與多個(gè)資源管理器進(jìn)行交互。全局事務(wù)管理器負(fù)責(zé)管理全局事務(wù)狀態(tài)和參與事務(wù)的資源,資源管理器則負(fù)責(zé)具體的資源操作,DTP模型與應(yīng)用程序的關(guān)系見下圖:

DTP模型

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

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

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

XA事務(wù)流程

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

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

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

但嚴(yán)格保障事務(wù)ACID特性是一把雙刃劍。

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

2、柔性事務(wù)

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

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

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

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

最大努力送達(dá)

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

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

Saga

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

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

Saga事務(wù)更適合使用長事務(wù)的場景。它由多個(gè)本地事務(wù)所組成,每個(gè)本地事務(wù)有相應(yīng)的執(zhí)行模塊和補(bǔ)償模塊,任何一個(gè)本地事務(wù)出錯(cuò)時(shí),可以通過調(diào)用相關(guān)的補(bǔ)充方法達(dá)到事務(wù)的最終一致性。

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

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

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

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

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

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

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

TCC

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

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

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

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

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

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

匯款服務(wù)

Try

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

Confirm

  • 不做任何操作。

Cancel

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

收款服務(wù)

Try

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

Confirm

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

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

消息驅(qū)動

消息一致性方案是通過消息中間件保證上下游應(yīng)用數(shù)據(jù)操作的一致性。基本思路是將本地操作和發(fā)送消息放在一個(gè)本地事務(wù)中,下游應(yīng)用向消息系統(tǒng)訂閱該消息,收到消息后執(zhí)行相應(yīng)操作。本質(zhì)上是依靠消息的重試機(jī)制,達(dá)到最終一致性。下圖是消息驅(qū)動的事務(wù)模型:

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

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

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

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

1、基礎(chǔ)治理

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

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

2、彈性伸縮

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

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

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

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

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

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

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

作者介紹

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

責(zé)任編輯:龐桂玉 來源: 今日頭條
相關(guān)推薦

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ù)庫架構(gòu)

2024-12-06 08:29:29

2020-10-15 08:34:32

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

2017-07-26 09:41:28

MyCATSQLMongoDB

2022-11-14 18:23:06

亞馬遜

2021-09-06 10:24:12

鴻蒙HarmonyOS應(yīng)用

2022-04-01 10:55:30

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

2017-12-01 05:40:56

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

2017-11-27 06:01:37

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

2017-12-11 13:30:49

Go語言數(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ù)庫中間件

2017-11-30 08:56:14

數(shù)據(jù)庫中間件架構(gòu)師

2022-03-07 10:27:21

云原生云計(jì)算數(shù)據(jù)庫
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號