萬維威,谷 鵬
(中國電子科技網絡信息安全有限公司,四川 成都 610041)
隨著互聯網的應用和需求不斷擴大,組成互聯網的網絡設備,正在朝著速度更快、服務質量更好、運行更可靠和高并發性能的方向發展。例如,核心網的路由器從傳統的集中式架構向分布式架構轉變。對于分布式架構的網絡設備,在追求高性能、高轉發效率、高可靠性的同時,其內部模塊的復雜度、耦合性、可靠性、可復用性等隱性指標都比傳統的集中式設備復雜,由此帶來的隱患風險也遠高于集中式設備,而這些隱患主要集中在不同模塊對控制平面的數據在各分布式部件間的分發、存儲、管理、異常等行為上的不一致,對于分布式網絡設備的更新迭代和維護極為不利。
為解決此問題,可以通過分布式數據庫來管理各部件的數據,在市場上,分布式數據庫經過多年發展[1],已經有成熟的項目可供選擇,如傳統關系型數據庫(Traditional Relational Database,TRDB)的Oracle[2]、Access、Foxpro[3]、DBll、Mysql等,不僅僅是結構化查詢語言(Not only Structured Query Language,NoSQL)的Redis[4]、KeyDB、MonetDB[5]、BigTbale 等,NewSQL的PostgreSQL[6]、SequoiaDB、SAPHANA、MariaDB、VoltDB、Clustrix等。但這些數據庫在數據分發上都不是主要關注點,更傾向于數據存儲部分,并且都不能同時兼顧如錯誤恢復功能、可靠性、時序性、效率性等,所以本文從上述痛點出發,提出對分布式數據庫的需求,具體如下文所述。
(1)可以引導各個應用模塊將自身的信息分解成具體的表結構,有利于模塊對自己的信息進行規范化處理,從而減少各應用模塊對數據管理不統一、不規范的情況。
(2)結合集中式應用模塊低復雜度和分布式系統高效的優點,對各模塊容易產生差異的數據分發、存儲、管理等行為進行統一接管,虛擬出一個數據庫,向應用模塊屏蔽分布式特性,使得應用模塊在操作控制平面數據時和在集中式設備上一樣,讓其專心于業務的管理,提高分布式網絡設備的可靠性和可維護性。
(3)提供靈活的存儲擴展,可以根據各應用模塊自身的需求來決定使用何種存儲,或者不使用存儲管理,直接使用分布式部件間數據分發功能。
(4)借鑒一些數據庫公共機制(比如事務、光標、視圖等),以標準的數據庫行為來規范數據的管理,減少應用模塊私自接管數據管理在設計上出現欠考慮的問題。
(5)依托于分布式數據庫的公共接口,可以建立一套靈活的版本移植性解決方案。
基于以上需求,本文設計了一款分布式數據庫(Embedded Distributed database,EDdb)。本文首先從整體架構、軟件層次、接口、實時分發和批量分發、存儲擴展性、異常處理這幾個方面進行論述,其次分析EDdb的不足,最后探討應用于分布式網絡設備上的分布式數據庫的進一步發展方向。
分布式部件是指以插卡的方式為分布式設備提供特定功能的板卡,一般來說,分布式網絡設備默認需要主控卡(Master Main Processing Unit,Master MPU)、備用主控卡(Slave Main Processing Unit,Slave MPU)和業務卡(Line Processing Unit,LPU)。
(1)Master MPU 管理分布式路由器的基礎事務,為系統正常工作提供基礎保障。
(2)Slave MPU 為Master MPU的備份,在主控卡發生故障時,在不間斷系統功能的情況下接替主控卡的所有工作。
(3)LPU 為路由器提供網絡數據包的接收、轉發等功能。
對于EDdb 而言,以上板卡只會劃分為兩類部件,即源部件和目的部件,源部件只有一個,而目的部件可以有多個。在分布式網絡設備中,源部件即是Master MPU,而除此以外的所有板卡都是目的部件。需要注意的是,EDdb 在數據分布上是單向的,即只能源向目的分發,而不能目的向源分發,這保證了分布式網絡設備的Master MPU 具有全部的控制權。
EDdb 作為系統的公共基礎模塊,為系統里的應用模塊提供數據存儲和分發的功能,它不與具體的操作系統平臺和網絡產品緊密相關,因此EDdb為系統軟件的一部分,具體如圖1 所示。

圖1 EDdb 層次
EDdb 與分布式部件之間的通信需要有一個抽象層。對于不同的產品,如果各個分布式部件之間的通信機制有變化,則需要該抽象層進行屏蔽。此外,EDdb 也需要從分布式部件的熱拔插和熱切換的事件中獲得通知,包括分布式部件進入系統和退出系統的事件通知。
EDdb 存在于整個設備的各個分布式部件中,但這些EDdb的角色并不是平等的,分為源EDdb和目的EDdb,主控卡(Master MPU)上的EDdb 是源EDdb(所屬板卡也稱為源部件),除此以外的其他所有部件上的EDdb 都是目的EDdb(所屬板卡也稱為目的部件)。圖2 展示了EDdb 在系統設備上的功能和架構。在分布式網絡設備上,各個板卡部件都是獨立存在的,要想將各個板卡組合在一起實現完整的功能,它們之間的數據通信就顯得尤為重要。為了規范板卡間的數據通信,并且為各板卡上的應用模塊屏蔽分布式特性,即不關心數據的同步及分發,需要在各板卡之間虛擬出EDdb的模塊,對各板卡間所要同步或分發的數據進行集中管理,同時使用數據庫的公共機制(如事務、光標、視圖等),以標準的數據庫行為來為各板卡上的應用模塊提供數據庫操作接口。
圖2 中,通道A、B、C 分別為應用模塊和分布式數據庫的交互接口,為應用模塊提供添、刪、改、查等數據庫操作接口,通過這些接口,應用模塊可以快速地在各個板卡間使用自己需要的數據。通道D 是EDdb 實時分發、批量的通道,管理各應用模塊存儲在數據庫的數據的實時分發和批量,除此以外,EDdb 自身的異常處理、源和目的數據庫切換、目的部件離開或加入的管理都通過通道D 來實現。因此,從整體架構上來看,EDdb 為分布式系統內部各部件間的數據傳輸提供了統一的管理,使得分布在各板卡上的應用模塊可以隨時隨地管理自己的數據,而不用關心數據是怎么分發的。

圖2 EDdb 架構
EDdb的數據組織方式還是參考傳統的數據庫標準,以連接(connection)取代database,即為每個應用模塊的一個數據需求分配一個連接,在該連接下可以創建多張表,對每張表的操作可以采用句柄的方式來進行,這里的句柄是指將要操作的列描述轉換成句柄,再對表進行添、刪、改、查時傳入數據庫即可進行表操作。
對于應用模塊而言,所有的表都在源部件上,而其他目的部件上的表需要由應用模塊來決定是否同步,而同步的表,應用模塊能獲取到和源部件一樣的數據。
對于表項分發的過程,是將數據、表、列句柄等數據信息和操作(即添、刪、改)發往目的部件,在協商過程中會有狀態,其狀態含義如下文所述。
(1)初始化:操作緩存進入分布式數據庫,還未進行分發。
(2)分發未執行:操作緩存已經被發送,但是在發送端還未接收到各個分布式部件確認。
(3)分發執行未確認:發送端已經發出操作緩存執行的信息,但是還未接收到各個分布式部件執行的結果。
(4)錯誤未確認:發送端接收到各個分布式部件執行的結果,其中有錯誤報告,發送端將匯總的錯誤又通知各個分布式部件,還未接收到分布式部件的錯誤確認。
(5)應用未確認:應用模塊對分發操作完結或突發事件所做的回應。
(6)終結:操作已經分發并被各個模塊確認。操作緩存銷毀。
狀態轉換和對應事件如表1 所示。

表1 發送表狀態轉換和對應事件
在接收方接收表項和操作,各個狀態含義如下文所述。
(1)接收未執行:接收到操作緩存,還未接收操作執行報文。
(2)執行返回未確認:接收到操作執行,并且執行完畢返回結果,還未接收到發送端返回的所有分布式部件結果。
(3)錯誤未確認:接收到發送端的所有分布式部件結果,結果中有錯誤報告,已經發出對這個錯誤的確認,還未獲得終結報文。如果結果中沒有錯誤報告,操作緩存的這個狀態會跳過直接到終結狀態。
(4)終結:接收到發送端發出的操作緩存終結報文。

圖3 發送表操作行為狀態機

圖4 接收表操作行為狀態機
狀態轉換和對應事件如表2 所示。

表2 接收表狀態轉換和對應事件
應用模塊的數據是以表的形式存在的,應用模塊對數據的操作也是對表的操作,EDdb 為應用模塊提供了對表進行添、刪、改的操作接口,由于只有這3 個操作能對表數據產生變化,所以在表數據進行數據分發時,其分發的數據報文都是帶有這3種操作指令的。圖5 展示了應用模塊發起的數據流在源部件和目的部件中的流程,其中涉及多個任務協同工作。根據數據分發的場景不同,可以分為實時分發和批量分發兩種方式。

圖5 數據分發
(1)實時分發:適用于已經處于就緒狀態的目的部件,在這種情況下,應用模塊對源部件上表項的添、刪、改操作會實時分發到目的部件上。
(2)批量分發:適用于剛進入系統的目的部件,在這種情況下,表的批量任務會將源部件上所有表項一次性以添加指令的方式分發到目的部件上。
2.3.1 實時分發流程
實時分發操作可以分為5 步,也可以分為兩個階段,第一階段是保證操作的一致性,該階段完成時,操作能夠保證在所有目的部件上都存在數據和操作指令,第二階段是實際的操作。多目的部件實時分發時序如圖6 所示。如果分發時發生了錯誤,則分發操作如圖7 所示。

圖6 多目的部件實時分發時序

圖7 多目的部件實時分發出錯時序
添、刪、改操作本身會在源、目的部件上緩存,這個緩存中包含了操作的上下文,同時每個連接的每個操作都會分配唯一的操作序列號,以便于對于結果的回溯。
在源部件中,該緩存有分發未確認和分發執行未確認兩種狀態。分發未確認對應于源部件發送操作,但是還未接收完所有目的部件響應的狀態。分發執行未確認對應于源部件發送了分發執行的消息,但是還沒有接收到所有目的部件執行結果的狀態。
在目的部件中,該緩存有接收未執行和執行返回未確認兩種狀態。每個操作會帶上操作序列號下發到各個分布式部件,目的部件接收后隨即緩存,并回應該序列號;源部件接收回應后,發送操作執行報文;目的部件接收到操作執行后立即執行操作,并將結果返回。
EDdb 在源部件匯總結果,并且將結果返回給應用模塊,源部件所在的應用模塊在接收到返回的結果以后,按照結果來控制下一步行為,EDdb 提供應用模塊結果確認回調函數,應用模塊調用該回調函數將操作結果返回給各個目的部件,即完成此次下發動作,操作在本地數據庫中的緩存清除。
2.3.2 批量分發流程
在目的部件剛進入系統時,會發送批量數據請求,在源部件收到該請求后,會把本地目的部件關心的表項批量分發給該目的部件,其數據分發指令是添加操作,數據為源部件上需要分發的所有表項,即一行表項對應一個添加指令;其批量分發的具體流程和實時分發的流程一致,這里就不再贅述。
需要注意的是,在批量分發的過程中,應用模塊可能會操作源部件上正在批量的表,此時,實時操作的表項需要與當前正在批量的表項位置進行比較:如果所操作的表項在當前批量表項的后面,則只做本地操作,不做分發動作,因為批量操作會在遍歷到當前實時操作的表項;如果所操作的表項在當前批量表項之前或重合時,本次的實時操作必須分發到目的部件上。
由于嵌入式設備的資源不能和專業的服務器相比,因此在數據存儲功能方面的需求要比數據分發的需求弱很多。嵌入式網絡設備以數據處理效率為主要指標,所以EDdb的存儲功能只提供良好的可擴展存儲插件注冊機制和默認的基本hash 存儲功能。如果應用模塊在沒有效率優先的情況下,可以使用默認的hash 存儲功能;如果應用模塊對于數據的使用有效率要求,則可以自己實現存儲插件,甚至更極端的情況下,自己實現的插件可以不具備存儲功能,在插件中將收到的數據直接處理。所以,EDdb 更適合分布式架構的網絡設備使用。
為了讓EDdb 提高可移植性和可擴展性,應該盡可能少地讓EDdb 依賴于和系統相關的特有基礎模塊,而讓EDdb 依賴于屬于公共特性的基礎模塊。
圖8 展示了EDdb 所相關的外部接口關系,可以看出,和EDdb 相關的內部模塊普遍存在于分布式網絡設備中,包括內部部件通信機制、部件插拔通告機制、系統重啟機制、操作系統公共機制,都是分布式網絡設備所必需的基礎機制。此外,就算沒有和分布式架構相關的機制,如內部部件通信機制、部件插拔通告機制(如集中式網絡設備),EDdb 也可作為普通數據庫來使用,所以EDdb 可以做到適配于幾乎所有的網絡設備。

圖8 數據庫接口關系
源EDdb 通過位圖的方式對目的部件的狀態進行管理,其中位圖有注冊位圖、發送位圖、隔離位圖3 個概念,具體如下文所述。
(1)注冊位圖:在數據分發模塊中,為了能夠使源部件知道將要把數據分發到哪些目的部件上,就需要目的部件在自己初始化完成后主動告知源部件進行注冊,等注冊完畢后,源部件將該目的部件在注冊位圖上置位。該位圖的作用是在與目的部件有交互的流程上提供目的部件的序號。
(2)發送位圖:告知數據分發模塊要將數據分發到哪些目的部件上的位圖。
(3)隔離位圖:告知所有和目的部件有交互的流程該部件是被隔離的。
EDdb的異常處理方法采用注冊的方式,當前只有隔離和重啟兩種方式,但可以根據后續的需求進行擴展。異常處理的整體流程如圖9 所示。

圖9 異常處理的整體流程
隔離操作的目的是讓源EDdb 不再往該隔離部件上發送數據,并對該部件在發送位圖上做標記,同時令該異常部件清空數據庫,后續如果源部件上的表項有變化,在進行實時分發時會根據隔離位圖來避免發送數據到隔離部件上。
解隔離操作的目的是讓異常目的部件解決不嚴重的問題并恢復工作。如在數據分發過程中出現錯誤,一般情況下,對其重新批量一次,其問題即可解決,所以解隔離就是清除該部件的隔離位圖,并對其重新批量分發一次。
對于目的部件上比較嚴重的問題,如內存不足、CPU 繁忙、部件間通信故障等,一般可以通過重啟部件解決,所以源EDdb 會給分發操作設定超時時間、報告基礎故障等機制,一旦收到此類異常,便可對問題部件進行重啟操作。
在對EDdb 做測試時,采用的是某廠商分布式路由器測試環境,由于EDdb 如2.5 節所描述,可以適配幾乎所有的網絡設備,所以可以單獨在設備上運行,不和其他功能相互依賴,這為測試提供了良好的前提。
本次測試創建一條連接testApp 和這條連接下的一張表testMode|testSubMode|testTable,源部件序號為2,目的部件序號為4,在此基礎上進行測試。
創建數據庫分兩個步驟,第一步是調用創建連接接口,創建屬于應用模塊自己獨有的連接;第二步是創建表和相關的列句柄,可以在連接下創建多張表,列句柄的作用是在進行添、刪、改、查時作為索引或輸出時使用。
由于連接是所有表的歸屬,所以連接的一個重要功能是管理所有表項的分發,即2.3 節所述功能。連接的信息可以通過EDdb的可維護性子模塊向外界展現,具體如圖10 和圖11 所示。

圖10 源EDdb 連接信息

圖11 目的EDdb 連接信息
圖10、圖11 即是創建的連接信息,主要分為3個部分:第一部分為連接的基本信息,包括連接名字、連接ID、魔數等;第二部分為數據分發信息,包括發送隊列信息、發送緩存信息、應用確認回調函數等,在發送隊列信息上,noSendSeq、execNoAck、appNoAffirm 信息和2.3 節的分發時序相對應,可以通過隊列信息了解當前表項的分發情況;第三部分為連接的統計信息,基本上是所有錯誤的統計。
需要注意的是,源EDdb 和目的EDdb的連接信息展示有所不同,這和它們各自角色是相關的。在源EDdb 上,主要的功能是分發表項,而目的EDdb 沒有這個功能(1.1 節有說明),所以在目的EDdb 上關于分發的信息為0。
從圖10 可以看出,在源部件上,maxAppAffirm WaitCnt的統計為1,證明應用模塊確認過1 條表項分發的結果,所以這1 條表項是分發成功了,但具體分發到哪張表,需要進一步在表信息里去查看。表信息如圖12、圖13 所示。

圖12 源部件所屬表信息

圖13 目的部件所屬表信息
從圖12 來看,表信息也分為基礎信息、列句柄信息、所要分發的目的部件信息和統計信息,其中統計信息中,insertOp 顯示為1,說明在源部件上做過一次表項的插入,并且在ready dpart list 上顯示dpartId 為4的部件已經就緒,所以結合源部件連接信息可知這條表項成功分發到ID 為4的目的部件上,并且通過bulkWakeUpCnt 為1 得知已經批量分發成功。從圖13 中也可以看到,insertOp 為1,可以得出從源部件分發來的表項已經插入成功,這樣就實現了EDdb的功能。
打開EDdb的debug 信息,在源部件上插入一條表項,會得到相關的實時分發debug 信息,如圖14、圖15 所示。

圖14 源部件上分發debug 信息

圖15 目的部件上分發debug 信息
從圖14 和圖15 可以看出,其分發表項的流程和圖6的時序圖是一致的,分別進行了分發未執行、執行未確認和應用模塊確認的步驟,確保數據的正確分發,其中任何一步的失敗都將導致表項分發失敗,而EDdb 也會通過異常處理流程來處理失敗結果,盡可能地保證數據正確下發。
通過自身制造目的部件異常,使源部件對ID為4的目的部件進行隔離,如圖16 所示。
圖16 是源部件表信息,從中可以看出,對于隔離次數的統計為1,并且在no ready dpart list 下看到ID 為4的目的部件已經處于未就緒范圍內,狀態為隔離狀態(即0x3),在quarantine dpart list下可見隔離部件位圖已經置位(0x10的二進制是0001 0000,第四位置位代表序號4的隔離部件已記錄),再對源部件上的表執行插入表項動作,沒有分發的debug 信息,證明源部件表項的插入已經不會分發到隔離的目的部件上,在對其解隔離后,源部件上的所有表項會進行一次批量動作,這時目的部件上的表項又和源部件同步成功。重啟就是直接調用重啟接口,過程比較簡單,這里就不做贅述了。

圖16 隔離目的部件信息
EDdb 可以很好地滿足分布式架構下的網絡設備對于內部部件間的數據分發、可靠性、可擴展性的需求,方便各應用模塊在不同部件上使用源部件的數據。但在實際使用中仍有不足,未來需要關注和解決的方向主要有以下幾點:
(1)表之間的依賴關系需要考慮。在實際使用過程中,如果應用模塊的某張表必須由另一張表來決定,則在批量分發過程中,先要把依賴的表批量完成后,才能批量這張表。實現依賴關系有利于應用模塊的邏輯時序,這點后續需要考慮如何來實現。
(2)存儲機制需要加強。雖然EDdb 存儲機制不是重點,并且在可擴展性上預留了可能,但現階段的存儲只有hash 方式,在大量表項存儲方面可能遇到瓶頸,后續可以參考標準數據庫存儲方式增強其存儲性能。
(3)對于在設備資源耗盡的情況下,EDdb 如何運行,以及資源恢復正常后該如何恢復業務,這也是需要考慮的問題。