徐梓薦,葉 盛,張 孝
1(教育部數(shù)據(jù)工程與知識(shí)工程重點(diǎn)實(shí)驗(yàn)室(中國(guó)人民大學(xué)),北京 100872)
2(中國(guó)人民大學(xué) 信息學(xué)院,北京 100872)
關(guān)系型數(shù)據(jù)庫的數(shù)據(jù)存儲(chǔ)方式一般有行式存儲(chǔ)和列式存儲(chǔ)兩種.對(duì)于行存儲(chǔ)數(shù)據(jù)庫,支持海量數(shù)據(jù)的高效更新,然而,當(dāng)需要分析數(shù)據(jù)庫中隱含的信息時(shí),就可能會(huì)涉及一些針對(duì)數(shù)據(jù)庫表中某一或某些屬性上的聚合,而這是行式存儲(chǔ)數(shù)據(jù)庫的不足.列式存儲(chǔ)數(shù)據(jù)庫對(duì)于海量數(shù)據(jù)更新沒有很高的處理效率,而對(duì)數(shù)據(jù)分析,特別是在某些屬性上進(jìn)行分析,往往有很好的查詢優(yōu)勢(shì).如果能夠構(gòu)建由行列數(shù)據(jù)庫共同組成的分布式數(shù)據(jù)庫系統(tǒng),用行存儲(chǔ)數(shù)據(jù)庫接受來自業(yè)務(wù)線的數(shù)據(jù),再通過數(shù)據(jù)同步將這些數(shù)據(jù)同步到列存儲(chǔ)數(shù)據(jù)庫中進(jìn)行數(shù)據(jù)分析,就可以彌補(bǔ)數(shù)據(jù)庫的上述不足,為真實(shí)的業(yè)務(wù)場(chǎng)景提供一個(gè)優(yōu)化的解決方案.
本文以 MySQL為對(duì)象,開展基于該數(shù)據(jù)庫的數(shù)據(jù)同步技術(shù)研究工作,主要實(shí)現(xiàn)了一款基于數(shù)據(jù)庫 Binlog的分布式數(shù)據(jù)庫中的數(shù)據(jù)同步工具 Cynomys.鑒于當(dāng)前并沒有一種針對(duì)行存儲(chǔ)數(shù)據(jù)庫到列存儲(chǔ)數(shù)據(jù)庫的實(shí)時(shí)同步工具,本文提出了一種基于SQL還原的實(shí)現(xiàn)方法,利用標(biāo)準(zhǔn)的SQL語句避免底層數(shù)據(jù)庫引擎差異可能導(dǎo)致的無法進(jìn)行數(shù)據(jù)庫同步的問題.
本文的主要貢獻(xiàn)包括:(1) 設(shè)計(jì)并實(shí)現(xiàn)了Binlog解析器BinParser以及還原器BinReducer,解析器解析并獲取Binlog的日志信息,還原器將BinParser解析的日志內(nèi)容還原為可執(zhí)行的SQL語句;(2) 提出了基于BinParser和 BinReducer的異構(gòu)分布式數(shù)據(jù)同步方法 TD-Reduction,并開發(fā)了基于 TD-Reduction的數(shù)據(jù)同步工具Cynomys;(3) 針對(duì)ColumnStore的存儲(chǔ)結(jié)構(gòu)提出了延遲提交(delay commit)的優(yōu)化技術(shù);(4) 本文針對(duì)Cynomys的性能、功能等方面做了大量實(shí)驗(yàn),結(jié)果表明該工具有效且能夠正常運(yùn)行.
對(duì)于分布式架構(gòu)的數(shù)據(jù)庫及其數(shù)據(jù)同步解決方案,研究人員提出了若干種體系架構(gòu)以及基于此的各類問題的解決方案,如:Stonebraker提出了一種廣域的分布式數(shù)據(jù)庫系統(tǒng)[1],Chen等人提出了一種基于分布式數(shù)據(jù)庫的在線數(shù)據(jù)分割方法[2],Google開發(fā)了可擴(kuò)展、多版本、全球分布式和同步復(fù)制的數(shù)據(jù)庫Spanner[3],Wang等人提出了基于中間件的分布式數(shù)據(jù)同步技術(shù)[4].
在商用數(shù)據(jù)庫中,Oracle和SAP HANA都提出了解決異構(gòu)數(shù)據(jù)存儲(chǔ)的方案.其中,Oracle在其12c版本中加入了解決異構(gòu)數(shù)據(jù)存儲(chǔ)問題的新特性In-memory Option[5].在12c版本以前,Oracle所有的數(shù)據(jù)都是以行形式進(jìn)行存儲(chǔ)的.該特性引入后,Oracle允許用戶將指定的表空間內(nèi)所有的表以列形式存儲(chǔ)在單獨(dú)開辟的一塊內(nèi)存空間In-memory Area中[6],從而在查詢分析數(shù)據(jù)時(shí),對(duì)涉及某些屬性上聚合的查詢操作速度加快,并且這種雙格式架構(gòu)多占用的內(nèi)存開銷被控制在單一格式架構(gòu)內(nèi)存開銷的20%以內(nèi)[7].SAP HANA是混合型的內(nèi)存數(shù)據(jù)庫,它同時(shí)支持行存儲(chǔ)以及列存儲(chǔ)[8].與 Oracle不同的是,它需要在創(chuàng)建新表時(shí)指定行式或列式存儲(chǔ),兩種不同的存儲(chǔ)架構(gòu)使用不同的存儲(chǔ)引擎.這兩種商用數(shù)據(jù)庫的方案是針對(duì)行列存儲(chǔ)數(shù)據(jù)庫有不同的優(yōu)勢(shì)所提出的數(shù)據(jù)存儲(chǔ)方案,而Cynomys則是將行存儲(chǔ)數(shù)據(jù)庫中的數(shù)據(jù)實(shí)時(shí)同步或者遷移到列存儲(chǔ)數(shù)據(jù)庫中,與以上兩種異構(gòu)存儲(chǔ)的方案定位不同.
當(dāng)前,常規(guī)意義上的數(shù)據(jù)遷移已經(jīng)發(fā)展得較為成熟,解決方案也已經(jīng)較為明確,例如基于負(fù)載均衡的數(shù)據(jù)遷移方法[9].而數(shù)據(jù)同步可以看做是實(shí)時(shí)的數(shù)據(jù)遷移過程.一般的數(shù)據(jù)同步可以基于數(shù)據(jù)庫日志[10,11]、觸發(fā)器[12]等,其中,以日志法最具代表性和可行性.然而由于不同數(shù)據(jù)庫的日志格式差別很大,可供日志分析的接口也有所不同,所以基于日志法的數(shù)據(jù)同步也局限于同種數(shù)據(jù)庫,甚至要求版本相同或相近,這限制了數(shù)據(jù)同步技術(shù)的發(fā)展.有人曾提出使用SQL還原的方法[13],但該方法是基于SQL Server觸發(fā)器實(shí)現(xiàn)的,人工干預(yù)程度大、維護(hù)困難.Cynomys的基于標(biāo)準(zhǔn)SQL語句進(jìn)行數(shù)據(jù)同步的方法受到該工作的啟發(fā).
在分布式環(huán)境下,許多數(shù)據(jù)庫或分布式系統(tǒng)如Google的Chubby,MegaStore,Spanner,Hadoop中的Zookeeper等,都依賴Paxos算法[14]來保證不同結(jié)點(diǎn)之間事務(wù)的一致性.Cynomys是一款數(shù)據(jù)同步工具,是從主數(shù)據(jù)庫(行存儲(chǔ)數(shù)據(jù)庫)到從數(shù)據(jù)庫(列存儲(chǔ)數(shù)據(jù)庫)的數(shù)據(jù)同步,需要保證的是同步操作前后數(shù)據(jù)的一致性,而非同步過程中事務(wù)同步執(zhí)行的一致性.
MySQL提供了 Group Commit機(jī)制(https://dev.mysql/com/doc/refman/8.0/en/commit.html),在 MySQL的InnoDB引擎中,為了保證數(shù)據(jù)庫的事務(wù)持久性不被破壞,在每個(gè)事務(wù)被提交之前都會(huì)先刷redo日志,而redo日志是要刷到磁盤上的,在多事務(wù)并發(fā)的情況下會(huì)造成性能的瓶頸.總的來說,Group Commit是將多個(gè)并發(fā)的事務(wù)一次性寫入日志來減少 I/O,而 Cynomys的延遲提交機(jī)制是將一段時(shí)間內(nèi)多個(gè)實(shí)現(xiàn)類似功能的事務(wù)合并在一起,以減少列存儲(chǔ)數(shù)據(jù)庫數(shù)據(jù)文件的訪問,從而提升同步性能.
Binlog的全稱是Binary Log(https://dev.mysql.com.doc/internals/en/binary-log-overview),即二進(jìn)制日志文件,是備份的基本要素之一,對(duì)于基于時(shí)間點(diǎn)的恢復(fù)更是必須的.一般而言,日志會(huì)比數(shù)據(jù)小很多,更適合于需要頻繁備份的場(chǎng)景.MySQL的Binlog從記錄格式上可以分成3種類型:Statement,Row,Mixed.其中,Mixed格式默認(rèn)以Statement格式作為記錄格式,針對(duì)Statement格式不能準(zhǔn)確描述的數(shù)據(jù)變化再以Row格式進(jìn)行記錄,綜合了兩者的優(yōu)點(diǎn),適于本文的場(chǎng)景,即:在不依賴Master節(jié)點(diǎn)具體環(huán)境的情況下使用Statement格式,在依賴Master節(jié)點(diǎn)具體環(huán)境的情況下使用Row格式,保證基于Binlog的業(yè)務(wù)可以快速準(zhǔn)確地開展.
Binlog提供的事件(event)類型復(fù)雜多樣,對(duì)各種事件歸納出若干種有重要特征的基本事件,針對(duì)這些事件再進(jìn)行下一步的處理.本文將 28種事件類型歸納為 10種基本事件類型,并為每種基本事件類型設(shè)置相應(yīng)的屬性,使得每種事件類型可以完整準(zhǔn)確地表述自身的信息,10種基本的事件類型及其描述見表1.
Table 1 10 types of basic event表1 10種基本的事件類型
基于上述對(duì)Binlog事件類型的綜合歸類,形成了本文的解析規(guī)則算法.由于事件種類繁多,處理過程中難免會(huì)出現(xiàn)過多的分支.為了避免這種情況,在設(shè)計(jì)算法時(shí)本文采用了數(shù)據(jù)驅(qū)動(dòng)[15]的編程方式.BinParser的算法偽代碼見算法1.
算法1.解析器(BinParser)算法.
輸入:Binlog;
輸出:Stringlog_info.
Cynomys對(duì)Binlog的還原遵循最簡(jiǎn)原則.所謂最簡(jiǎn)原則,即對(duì)于Mixed格式中的Statement日志直接還原為原SQL語句,對(duì)于Row日志還原為能表述日志內(nèi)容的最簡(jiǎn)單的SQL語句(如圖1所示).
Fig.1 Flow chart of Binlog reduce圖1 Binlog還原示意圖
日志還原器BinReducer的算法偽代碼見算法2.
算法2.還原器(BinReducer)算法.
輸入:Stringevent;
輸出:StringSql.
具體還原步驟如下.
1) Cynomys監(jiān)聽Master節(jié)點(diǎn)的數(shù)據(jù)變化,并依據(jù)第2.2節(jié)的解析規(guī)則對(duì)Binlog信息進(jìn)行實(shí)時(shí)解析;
2) 針對(duì)不同的事件類型分別進(jìn)行還原;
3) 對(duì)于QueryEvent,其中涵蓋了具體執(zhí)行的SQL語句,實(shí)際上是以Statement格式記錄的日志信息,對(duì)此可以進(jìn)行直接還原;
4) WriteRowsEvent,UpdateRowsEvent,DeleteRowsEvent等幾類事件,需要經(jīng)過一定的處理,將其還原為SQL語句.
總的來說,根據(jù)最簡(jiǎn)原則來進(jìn)行 SQL還原的時(shí)候,如果事件信息中包含具體的 SQL語句,那么直接提取出SQL語句即可,如下面這個(gè)例子.
日志示例1
該事件中包含SQL語句,將該行insert into test(a) values(2)直接提取出來即可(對(duì)應(yīng)算法2的第1行、第2行).如果沒有具體的 SQL語句,那么根據(jù)事件的類型信息以及包含的具體數(shù)據(jù),將 SQL語句還原成 INSERT,UPDATE或者DELETE等DML語句即可(對(duì)應(yīng)算法2的第4行~第6行),舉例如下.
日志示例2
該事件是WRITE_ROWS_EVENT,但該日志內(nèi)沒有能夠直接提取出來的SQL語句,這種情況則需要通過正則表達(dá)式提取出事件信息中數(shù)據(jù)的變化,還原成原始或等價(jià)的SQL語句.該事件能夠還原出以下SQL語句:
insert into test values(1,‘c’), (2,’c’)
2.4.1 ColumnStore存儲(chǔ)模式
不同的數(shù)據(jù)庫引擎有不同的存儲(chǔ)模式,而根據(jù)存儲(chǔ)模式的不同,對(duì)目標(biāo)數(shù)據(jù)庫的存儲(chǔ)結(jié)構(gòu)可以優(yōu)化數(shù)據(jù)加載的策略.行存儲(chǔ)主要應(yīng)用在一些事務(wù)型數(shù)據(jù)庫,如 Oracle,MySQL,PostgreSQL等;而列存儲(chǔ)主要應(yīng)用在分析型數(shù)據(jù)庫上,如 Mariadb ColumnStore,MonetDB[16],Infobright等.本文要解決的主要問題是從 InnoDB到ColumnStore引擎之間的數(shù)據(jù)同步,ColumnStore的存儲(chǔ)結(jié)構(gòu)如圖2所示(https://mariadb.com/kb/en/library/columnstore-storage-architecture/):
Fig.2 Structure of ColumnStore圖2 ColumnStore存儲(chǔ)結(jié)構(gòu)
2.4.2 Delay思想
由于ColumnStore等列存儲(chǔ)引擎的特殊存儲(chǔ)架構(gòu)設(shè)計(jì),對(duì)于行存儲(chǔ)架構(gòu)支持完善的SQL語句就會(huì)帶來同步速度不匹配造成的性能瓶頸等問題.
假設(shè)存在表Student(見表2),并且需要執(zhí)行以下插入語句:
INSERT INTO Student (StuId, Lastname, Firstname, Score) VALUES (4, Lucy, Alex, 100);
Table 2 Example of Student table表2 示例Student表
在行存儲(chǔ)數(shù)據(jù)庫中可以發(fā)現(xiàn):插入一行新的數(shù)據(jù)十分簡(jiǎn)單,只要再添加一條完整的記錄就可以,對(duì)于之前的記錄毫無影響.Student表信息在行存儲(chǔ)數(shù)據(jù)庫中存儲(chǔ),那么其邏輯結(jié)構(gòu)就如圖3所示的結(jié)構(gòu).而對(duì)于同樣的Student表,如果存在列存儲(chǔ)引擎中,那么其邏輯結(jié)構(gòu)如圖4所示.在執(zhí)行例子中的INSERT語句時(shí),將會(huì)涉及到表中的每一列數(shù)據(jù),首先取出 StuId所在的數(shù)據(jù)塊,插入新數(shù)據(jù)“4”,然后再取出 Lastname所在列,插入新數(shù)據(jù)“Lucy”,以此類推.而列存儲(chǔ)引擎在通常情況下都會(huì)對(duì)數(shù)據(jù)進(jìn)行壓縮,每更新一次數(shù)據(jù)都會(huì)導(dǎo)致數(shù)據(jù)的解壓縮與再壓縮操作;同時(shí),列存儲(chǔ)數(shù)據(jù)庫作為分析型數(shù)據(jù)庫,通常每張表都有較多的字段.可想而知,通過常規(guī)插入手段的性能是極差的.而當(dāng)用戶基數(shù)大,用戶操作零散,在一些峰值情況下會(huì)產(chǎn)生大量用戶同時(shí)訪問一張表的情況,如果以先來先服務(wù)的形式逐一滿足用戶的請(qǐng)求,盡管主庫的數(shù)據(jù)一定會(huì)同步到備庫中,備庫的同步效率將會(huì)由于主庫的零散插入行為而大幅降低.
Fig.3 Example of row-based database圖3 行存儲(chǔ)數(shù)據(jù)庫數(shù)據(jù)組織示例
Fig.4 Example of column-based database圖4 列存儲(chǔ)數(shù)據(jù)庫數(shù)據(jù)組織示例
圖5是一般的同步處理過程,基本是一語句一提交.以這種方式進(jìn)行事務(wù)提交,直接導(dǎo)致區(qū)間(extent)處在不斷的解壓縮與壓縮的狀態(tài),降低了同步效率,在主庫InnoDB達(dá)到查詢峰值的時(shí)候,備庫ColumnStore基本屬于癱瘓狀態(tài).基于行列存儲(chǔ)的差異,必須提出一種新的思路,以解決SQL語句在處理列存儲(chǔ)數(shù)據(jù)性能不足的問題.
Fig.5 Flow chart of query processing圖5 一般查詢處理流程圖
MySQL的Group Commit目的是為了在并發(fā)的情況下通過將待commit的事務(wù)分組刷到redo日志中來減少I/O,而延遲提交則是為了緩解行存引擎和列存引擎在數(shù)據(jù)插入性能上的差異.通過binlog來進(jìn)行行列同步的過程中,如果不引入延遲提交機(jī)制,那么行存作為數(shù)據(jù)源產(chǎn)生 binlog的速度會(huì)遠(yuǎn)遠(yuǎn)高于列存能讀取 binlog的速度.
假如將行存看做生產(chǎn)者,列存看做消費(fèi)者,那么正常情況下,消費(fèi)者是沒有辦法去完成消費(fèi)生產(chǎn)者的內(nèi)容的,這就導(dǎo)致消費(fèi)者始終處于忙的狀態(tài),且與binlog的差距越來越遠(yuǎn).延遲提交機(jī)制的實(shí)質(zhì)是通過將大量的待提交的插入事務(wù)進(jìn)行緩存,待到緩存區(qū)域達(dá)到一定的數(shù)據(jù)規(guī)模的時(shí)候再對(duì)這一批數(shù)據(jù)進(jìn)行統(tǒng)一同步,通過一次解壓操作,將各列待插入的數(shù)據(jù)追加到相應(yīng)的Extent中去.
舉例來說,假如行存有3個(gè)事務(wù),都是往同一個(gè)表中插入數(shù)據(jù).
對(duì)于行存儲(chǔ)數(shù)據(jù)庫來說,進(jìn)行這3個(gè)事務(wù)的提交很快就能完成.但是對(duì)于列存儲(chǔ)數(shù)據(jù)庫來說,提交3個(gè)事務(wù)遠(yuǎn)比提交 1個(gè)事務(wù)的代價(jià)高得多,因?yàn)榱写鎯?chǔ)數(shù)據(jù)庫的存儲(chǔ)結(jié)構(gòu)與行存儲(chǔ)數(shù)據(jù)庫有本質(zhì)區(qū)別.為了避免類似事務(wù)多次提交,延遲提交機(jī)制就是將一段時(shí)間內(nèi)對(duì)同一表進(jìn)行的操作進(jìn)行歸并.比如,例子中的3個(gè)INSERT語句等同于以下語句:
這樣就可以減少對(duì)列存儲(chǔ)數(shù)據(jù)文件的訪問次數(shù)和解縮/壓縮次數(shù),提高列存吞吐量.
如圖6所示,Query Interface負(fù)責(zé)接收外界待插入的數(shù)據(jù),在當(dāng)前事務(wù)提交之前,Column Buffer會(huì)將數(shù)據(jù)進(jìn)行緩存,緩存的大小由用戶根據(jù)實(shí)際需求決定.
Fig.6 Flow chart of delay commit圖6 延遲提交流程圖
相較于圖4所示的單語句提交的方式,延遲提交解決了列存儲(chǔ)數(shù)據(jù)庫由于存儲(chǔ)模式限制導(dǎo)致插入性能較差、在交易高峰可能導(dǎo)致癱瘓的風(fēng)險(xiǎn).以批量的形式進(jìn)行語句提交,減少了存儲(chǔ)引擎與磁盤的 I/O,也降低了ColumnStore本身的壓縮與解壓縮頻率,滿足了現(xiàn)實(shí)的交易場(chǎng)景.
對(duì)于數(shù)據(jù)同步工具來說,保證數(shù)據(jù)同步操作完成后目標(biāo)數(shù)據(jù)庫(列存儲(chǔ)數(shù)據(jù)庫)表數(shù)據(jù)與源數(shù)據(jù)表中數(shù)據(jù)的一致性是最為重要的指標(biāo).下面簡(jiǎn)單分析數(shù)據(jù)同步的正確性.
數(shù)據(jù)同步分為兩種情況.
· 第1種情況是只有一個(gè)用戶(進(jìn)程).從實(shí)現(xiàn)的角度看,Cynomys實(shí)際上是設(shè)置了一個(gè)監(jiān)聽器(listener),實(shí)時(shí)地監(jiān)控日志文件的變化,一旦日志文件新增了事件,監(jiān)聽器就會(huì)將這個(gè)事件捕捉并發(fā)送給解析程序BinParser,然后執(zhí)行后續(xù)的 BinReducer等操作.如果用戶的請(qǐng)求中包含對(duì)同一表的 INSERT操作過多,Cynomys會(huì)采用延遲提交機(jī)制,在不超過Buffer Size的情況下,將這些INSERT語句保存到緩沖區(qū)中一起執(zhí)行.單用戶情況下,數(shù)據(jù)同步的正確性實(shí)際依賴的是BinParser的解析以及BinReducer還原的SQL語句的正確性,只要上述步驟中的各個(gè)環(huán)節(jié)沒有錯(cuò)誤,最后還原的SQL是正確的語句,同步到從數(shù)據(jù)庫中的數(shù)據(jù)就是正確的;
· 第 2種情況是多用戶(進(jìn)程)并發(fā)的情況.多用戶并發(fā)的情況下,不同的進(jìn)程會(huì)同時(shí)向日志寫入事件.Cynomys對(duì)日志監(jiān)聽并還原 SQL是一個(gè)串行的操作,即使多用戶同時(shí)并發(fā)操作,解析事件的順序與Cynomys監(jiān)聽到日志的順序相同.即使并發(fā)的情況下,也不會(huì)影響數(shù)據(jù)同步的準(zhǔn)確性.
算法3.延遲提交算法.
輸入:Buffer_Size,Query;
輸出:null.
系統(tǒng)的架構(gòu)總體分為日志解析、日志序列化、消息傳遞、SQL執(zhí)行等幾個(gè)主要模塊.
Cynomys是一個(gè)典型的“生產(chǎn)者-消費(fèi)者”模型,日志解析模塊就是整個(gè)工具的生產(chǎn)者,消息傳遞模塊充當(dāng)緩沖區(qū)的作用,而最終的SQL執(zhí)行模塊則是最后的消費(fèi)者,如圖7所示.
Fig.7 Whole architecture of Cynomys圖7 Cynomys整體架構(gòu)圖
日志解析模塊的作用是讀取并解析日志,將日志內(nèi)容還原為SQL語句;序列化模塊的作用是為還原出來的SQL語句提供序列化支持;數(shù)據(jù)壓縮加密模塊的目的是應(yīng)對(duì)可能出現(xiàn)的帶寬不足及網(wǎng)絡(luò)攻擊等問題;消息傳遞模塊主要是為了使消息能夠按照既定的順序或者規(guī)則進(jìn)行傳遞,保證下游的應(yīng)用不會(huì)因?yàn)橄鬟f不合理導(dǎo)致的錯(cuò)誤;SQL執(zhí)行模塊的作用是將先前模塊中得到的信息進(jìn)行解壓縮、反序列化等操作,將還原出來的SQL語句在目標(biāo)數(shù)據(jù)庫中執(zhí)行,達(dá)到數(shù)據(jù)同步的效果.
在第2.1節(jié)中已經(jīng)提到MySQL的Binlog屬于二進(jìn)制文件,本身并不具有可讀性,因此對(duì)基于Binglog的異構(gòu)數(shù)據(jù)庫同步,需先將Binlog中的內(nèi)容解析成目標(biāo)數(shù)據(jù)庫可執(zhí)行的相關(guān)內(nèi)容.
首先從日志解析模塊就進(jìn)行事件的分類處理,Binlog解析器的一級(jí)子模塊可以分為 RotateEventParser,QueryEventParser和WriteEventParser這3個(gè)部分,分別解析更換binlog文件事件、解析statement的事件以及數(shù)據(jù)更新相關(guān)的事件.根據(jù)解析出來的事件類型,EventFilter會(huì)將所有的事件分成更詳細(xì)具體的事件類別,EventListener監(jiān)聽器會(huì)實(shí)時(shí)監(jiān)聽MySQL-Master節(jié)點(diǎn)的日志內(nèi)容變化并通知業(yè)務(wù)線.具體解析流程如圖8所示.
Fig.8 Flow chart of Binlog parser圖8 Binlog解析器解析流程圖
數(shù)據(jù)或者對(duì)象狀態(tài)在分布式系統(tǒng)中的傳輸不可能直接以字符串的形式存在,必須根據(jù)一定的模式對(duì)數(shù)據(jù)進(jìn)行序列化[17].
支持序列化的工具或方法有很多,例如Avro,Protocol Buffer,JSON等.Cynomys中選用Apache Avro作為序列化工具,主要是由于 Avro具有支持多樣數(shù)據(jù)結(jié)構(gòu)、可持久化、支持 RPC(遠(yuǎn)程過程調(diào)用)、能夠與動(dòng)態(tài)語言簡(jiǎn)單結(jié)合等特點(diǎn).JSON在Avro中用來定義序列化模式文件,使用Avro的關(guān)鍵在于定義消息的模式,只要定義好消息模式并編譯保存,Avro就會(huì)根據(jù)定義的模式進(jìn)行相應(yīng)的序列化和反序列化.
在Cynomys中,定義一個(gè)JSON格式的模式如下所示.
在JSON定義的模式中,分別定義了一條消息的幾個(gè)域,為簡(jiǎn)潔明了表述消息內(nèi)容,選擇事務(wù) ID(tid)、涉及的數(shù)據(jù)庫名(database_name)、涉及的表名(table_name)以及還原出來的SQL語句(SQL).經(jīng)過序列化模塊產(chǎn)生的序列化文件會(huì)被傳送到下一個(gè)處理模塊進(jìn)行進(jìn)一步處理,序列化具體的流程如圖9所示.
Fig.9 Flow chart of serialization based on Avro圖9 基于Avro的序列化操作流程圖
除前面提到的日志解析模塊和序列化模塊外,Cynomys還包含數(shù)據(jù)壓縮加密、消息傳遞、SQL執(zhí)行等模塊.在數(shù)據(jù)壓縮加密模塊中,本文所采用的GZip數(shù)據(jù)壓縮算法[18],用AES算法對(duì)壓縮包進(jìn)行加密,RSA算法對(duì)AES算法的密鑰進(jìn)行加密,與被加密的壓縮包一起發(fā)送到消息傳遞模塊.數(shù)據(jù)壓縮加密模塊的流程如圖10所示.在消息傳遞模塊中,本文選擇Redis作為消息隊(duì)列,基于Redis實(shí)現(xiàn)消息的發(fā)布/訂閱(簡(jiǎn)稱Pub/Sub)模式[19].利用發(fā)布/訂閱模式,解決了被壓縮加密的消息包可能在網(wǎng)絡(luò)傳輸過程中丟失或者消息發(fā)送方和接收方處理效率不一致帶來的延遲等問題.
Fig.10 Flow chart of compression and encryption圖10 數(shù)據(jù)壓縮加密模塊流程圖
來自消息隊(duì)列中的消息,需由消費(fèi)模塊對(duì)消息進(jìn)行解密、解壓縮并反序列化后使用消息.Cynomys不會(huì)直接執(zhí)行消息中的SQL語句,而是根據(jù)情況決定是否啟用延遲提交機(jī)制.
本節(jié)內(nèi)容旨在檢驗(yàn)Cynomys的可用性、高效性、兼容性和正確性.在可用性實(shí)驗(yàn)中,主要驗(yàn)證Cynomys對(duì)數(shù)據(jù)庫基本操作的支持度以及Cynomys本身的穩(wěn)定性;在高效性實(shí)驗(yàn)中,主要驗(yàn)證了延遲提交機(jī)制下Cynomys的INSERT性能;在兼容性實(shí)驗(yàn)中,主要驗(yàn)證了Cynomys的數(shù)據(jù)庫兼容性和運(yùn)行平臺(tái)的兼容性;在正確性實(shí)驗(yàn)中,主要驗(yàn)證了多個(gè)事務(wù)并發(fā)情況下Cynomys同步數(shù)據(jù)的正確性.
Cynomys的功能實(shí)驗(yàn)、性能實(shí)驗(yàn)在兩臺(tái)服務(wù)器上運(yùn)行,其中,主服務(wù)器運(yùn)行 MySQL等行存儲(chǔ)數(shù)據(jù)庫,備份服務(wù)器運(yùn)行MariaDB ColumnStore等列存儲(chǔ)數(shù)據(jù)庫.所有實(shí)驗(yàn)的測(cè)試數(shù)據(jù)均采用TPC-C或TPC-H基準(zhǔn)測(cè)試數(shù)據(jù)集.具體的實(shí)驗(yàn)環(huán)境參數(shù)見表3.
Table 3 Configurition of experiment表3 實(shí)驗(yàn)環(huán)境配置
4.2.1 數(shù)據(jù)類型支持度
數(shù)據(jù)庫基本數(shù)據(jù)類型是支撐表元素的基礎(chǔ),Cynomys對(duì)數(shù)據(jù)類型的解析是根據(jù)MySQL寫日志的規(guī)則,不同的數(shù)據(jù)類型對(duì)應(yīng)不同的 ID,對(duì)字符串類型和非字符串類型分別解析.本文對(duì) MySQL的各個(gè)基本數(shù)據(jù)類型都進(jìn)行了相應(yīng)的測(cè)試.實(shí)驗(yàn)結(jié)果表明:Cynomys能夠完全支持 MySQL的基本數(shù)據(jù)類型(MySQL的基本數(shù)據(jù)類型:https://dev.mysql.com/doc/refman/8.0/en/data-type-overview.html),MariaDB,MonetDB的基本數(shù)據(jù)類型和MySQL一致.
4.2.2 操作類型支持度
本文用例中涉及的SQL語句大都與具體的數(shù)據(jù)直接相關(guān),所以在實(shí)驗(yàn)論證中只對(duì)DDL語句和DML語句進(jìn)行實(shí)驗(yàn)驗(yàn)證證,而對(duì)DCL語句不做支持.實(shí)驗(yàn)結(jié)果見表4,可以看出,Cynomys能夠支持所有DDL語句和DML語句.
Table 4 SQL statement support in Cynomys表4 Cynomys對(duì)SQL語句類型支持情況
4.2.3 系統(tǒng)穩(wěn)定性
作為一個(gè)實(shí)時(shí)同步工具,對(duì)其穩(wěn)定性要求一般較高,必須保證 7×24小時(shí)無間斷的穩(wěn)定運(yùn)行和快速響應(yīng).本實(shí)驗(yàn)是將Cynomys部署在服務(wù)器上連續(xù)運(yùn)行一周,在每天的4個(gè)時(shí)間點(diǎn)各發(fā)起一次同步請(qǐng)求,觀察Cynomys的表現(xiàn)并進(jìn)行采樣,得出結(jié)論,Cynomys的穩(wěn)定性達(dá)到了7×24小時(shí)及時(shí)響應(yīng),符合MySQL的運(yùn)行狀態(tài)和同步要求,實(shí)驗(yàn)結(jié)果見表5.
Table 5 Test table of operational stability表5 Cynomys運(yùn)行穩(wěn)定性測(cè)試表
性能實(shí)驗(yàn)中,主要測(cè)試從行存儲(chǔ)到列存儲(chǔ)的同步效率,其中,列存儲(chǔ)數(shù)據(jù)庫選擇前面提到的 MariaDB ColumnStore.測(cè)試集選用的是TPC-C產(chǎn)生的模擬數(shù)據(jù).
MariaDB ColumnStore主要用于分析型數(shù)據(jù)庫使用,所以Cynomys的同步只涉及INSERT,對(duì)UPDATE和DELETE性能不作要求,在性能實(shí)驗(yàn)中,主要是要檢驗(yàn)Cynomys執(zhí)行INSERT的性能.
在測(cè)試Cynomys的INSERT性能時(shí)將啟用延遲提交功能,通過調(diào)整延遲提交的緩沖區(qū)大小,得到實(shí)驗(yàn)數(shù)據(jù)如圖11所示.
Fig.11 Figure of DELAY buffer and INSERT effiency圖11 DELAY緩沖區(qū)大小與INSERT效率關(guān)系表
依據(jù)上述實(shí)驗(yàn)結(jié)論,延遲提交確實(shí)可以提高 INSERT操作的同步性能.在內(nèi)存容量充足的情況下,緩存的待插入數(shù)據(jù)越大,相應(yīng)的同步效率越高.
根據(jù) MySQL的現(xiàn)實(shí)場(chǎng)景,一般峰值的INSERT量在1 000條/s左右.由于表的列數(shù)對(duì)插入性能影響較大,所以對(duì)于延遲提交緩沖區(qū)大小的需要可依據(jù)表結(jié)構(gòu)來確定.以一個(gè)擁有 20個(gè)字段的表為例,滿足峰值 INSERT的緩沖區(qū)大小設(shè)置為3 000左右即可.
從數(shù)據(jù)中還能看出另一個(gè)問題:隨著表的列數(shù)增加,INSERT操作效率的提升降低了.這是由于受測(cè)列式數(shù)據(jù)庫的特殊存儲(chǔ)結(jié)構(gòu),列式數(shù)據(jù)庫中每一列都對(duì)應(yīng)一個(gè)文件,這樣的文件組織結(jié)構(gòu)導(dǎo)致當(dāng)表中的列增多時(shí),會(huì)對(duì)性能有較大的影響.
兼容性實(shí)驗(yàn)主要測(cè)試Cynomys在不同操作系統(tǒng)上是否能正常運(yùn)行以及不同行列數(shù)據(jù)庫能否正常同步.
Cynomys采用JAVA語言開發(fā)完成,目的就是為了能兼容各個(gè)操作平臺(tái).本節(jié)選取了當(dāng)前主流的操作系統(tǒng)包括Windows 7,Windows 8,Ubuntu,CentOS等不同版本測(cè)試Cynomys在各個(gè)操作系統(tǒng)平臺(tái)的運(yùn)行情況,實(shí)驗(yàn)結(jié)果見表6.
Table 6 Experimental result of compatibility of Cynomys表6 Cynomys平臺(tái)兼容性實(shí)驗(yàn)結(jié)果
從表6可以看出,Cynomys在當(dāng)前主流操作系統(tǒng)上都能正常運(yùn)行.此外,還對(duì)不同版本的行列數(shù)據(jù)庫進(jìn)行了軟件兼容性實(shí)驗(yàn).Cynomys是基于Binlog實(shí)現(xiàn)的,因此所有基于Binlog且能執(zhí)行標(biāo)準(zhǔn)SQL語句的行數(shù)據(jù)庫理論上都能運(yùn)行,本節(jié)測(cè)試了MySQL與MariaDB(ColumnStore)的不同版本進(jìn)行軟件兼容性實(shí)驗(yàn),實(shí)驗(yàn)結(jié)果見表7.
Table 7 Experimental result of compatibility of database in Cynomys表7 Cynomys數(shù)據(jù)庫兼容性實(shí)驗(yàn)結(jié)果
除表7給出的MySQL和MariaDB外,還對(duì)其他列存儲(chǔ)數(shù)據(jù)庫如MonetDB進(jìn)行了測(cè)試,在這些行存儲(chǔ)數(shù)據(jù)庫之間,Cynomys都能正常同步數(shù)據(jù),不同列存儲(chǔ)數(shù)據(jù)庫對(duì)數(shù)據(jù)查詢時(shí)間的影響如圖12(以 MariaDB和MonetDB進(jìn)行對(duì)比)所示.
Fig.12 Influence of query time for different column-based database圖12 不同列存儲(chǔ)數(shù)據(jù)庫對(duì)查詢時(shí)間的影響
正確性實(shí)驗(yàn)主要測(cè)試在多用戶(進(jìn)程)并發(fā)的情況下,Cynomys同步數(shù)據(jù)的準(zhǔn)確性與源數(shù)據(jù)是否一致.本節(jié)實(shí)驗(yàn)所選取的主數(shù)據(jù)庫為MySQL5.6,列數(shù)據(jù)庫為MonetDB11.27,數(shù)據(jù)集為TPCH-DBGEN生成的TPC-H數(shù)據(jù)中的LINEITEM表的前100萬條數(shù)據(jù).
實(shí)驗(yàn)假設(shè)有4個(gè)用戶同時(shí)對(duì)MySQL寫入共100萬條數(shù)據(jù),每個(gè)用戶寫入25萬條.從MySQL寫入數(shù)據(jù)一共用時(shí)5.68s,由于Cynomys對(duì)列存儲(chǔ)數(shù)據(jù)庫SQL語句提交的優(yōu)化,數(shù)據(jù)寫入MonetDB的速度與MySQL寫入的速度幾乎是同步的,僅用6.12s.實(shí)驗(yàn)為采樣實(shí)驗(yàn),測(cè)試采樣為30%,50%,100%的情況下,數(shù)據(jù)響應(yīng)速度與數(shù)據(jù)同步的準(zhǔn)確率.對(duì)比是否與原數(shù)據(jù)相同采用將所有字段進(jìn)行拼接,比較與原數(shù)據(jù)的字符串是否相同的方法.同步實(shí)驗(yàn)準(zhǔn)確性結(jié)果如圖13所示.
Fig.13 Accuracy of synchronization圖13 同步準(zhǔn)確性實(shí)驗(yàn)
實(shí)驗(yàn)結(jié)果顯示,在不同采樣大小的情況下,Cynomys同步到從數(shù)據(jù)庫的數(shù)據(jù)都能與源數(shù)據(jù)保證完全一致的準(zhǔn)確性,驗(yàn)證了第2.5節(jié)中對(duì)數(shù)據(jù)同步正確性的說明分析.
在當(dāng)前分布式系統(tǒng)得到廣泛應(yīng)用的背景下,本文提出了一種利用數(shù)據(jù)庫所提供的 Binlog機(jī)制,設(shè)計(jì)了日志解析器BinParser和日志還原器BinReducer.為了應(yīng)對(duì)分布式場(chǎng)景,也為工具實(shí)現(xiàn)了包括序列化、壓縮加密、消息分發(fā)等一系列的工作.同時(shí),針對(duì)列存儲(chǔ)數(shù)據(jù)庫本身在INSERT性能上存在的不足,提出了延遲提交的思想.基于以上各項(xiàng)技術(shù)實(shí)現(xiàn)了同步工具 Cynomys,用于同步行存儲(chǔ)數(shù)據(jù)庫和列存儲(chǔ)數(shù)據(jù)庫之間的數(shù)據(jù).該方法彌補(bǔ)了不同存儲(chǔ)模式的異構(gòu)數(shù)據(jù)庫之間無法進(jìn)行實(shí)時(shí)數(shù)據(jù)同步的空白,解決了從分布式行存儲(chǔ)數(shù)據(jù)庫到列存儲(chǔ)數(shù)據(jù)庫的同步問題,且在實(shí)驗(yàn)中表現(xiàn)出了良好的性能.
此外,目前Cynomys只支持部分版本的MySQL數(shù)據(jù)庫,在MySQL5.6之后,由于Binlog引入了CRC校驗(yàn),Cynomys對(duì)于如何適應(yīng)CRC校驗(yàn)并沒有給出解決方案,所以對(duì)于最新版本的MySQL數(shù)據(jù)庫使用場(chǎng)景還不能做到同步,需要人為地對(duì)高版本MySQL進(jìn)行配置才可以.這也是未來需要改進(jìn)的方面.