天天看點

SaaS|架構與背後的技術思考

引言

作為業務系統技術開發同學,面向當下:

  • 首先應該是快速搭建業務通路,讓線上業務跑起來,快速試錯,解決生存問題;
  • 第二步是在鍊路暢通、業務基本跑起來的基礎上,如何支撐業務跑得更快,就需要解決快速增長問題;
  • 第三步,在完成支撐業務快速增長的基礎上,要進行精細化提升,通過在支撐業務快跑間隙擠時間打磨系統功能和體驗,踏踏實實花時間去抽象能力,沉澱産品,提升效能;

同時我們也必須面向未來,如何在抽象能力以及沉澱了産品的基礎上,把所承載和沉澱的業務能力快速輸出,貢獻給整個行業,或為整個社會商業生态提供基座支撐。面向未來,将平台産品進行 SaaS 化更新,真正将能力進行有價值開放輸出是我們提前要布局的核心方向。

将平台産品進行 SaaS 輸出,需要解決那些問題呢?這裡嘗試把核心問題列舉一下:

1. 如何根據不同使用者需求進行計算能力按需排程配置設定?(IaaS/PaaS)

2. 如何滿足使用者資料安全性要求,嚴格隔離不同使用者的資料,使使用者隻能看到自己的資料?(PaaS)

3. 如何支援不同使用者在标準的資料對象/資料模型上按需添加自定義的資料對象/擴充模型?(PaaS & SaaS)

4. 如何按照不同使用者進行按需功能搭配組合,滿足不同使用者從基礎到專業級不同業務場景需求?(SaaS)

5. 如何統一對平台産品進行更新而不影響使用者已有資料及功能?(IaaS、PaaS、SaaS)

通過以上問題,我們可以看出,産品 SaaS 化輸出的關鍵是如何對不同的使用者通過标準+擴充能力按需進行算力、資料、安全、功能有效定制,支援多使用者共性和個性的問題,即多租戶的問題,同時也涉及到計費和服務水準等相關問題。我們下面來聊下上述問題的解題關鍵和解題思路:

  • 第1個算力問題的核心是排程問題,彈性計算提供在 IaaS 層的統一算力排程能力,而 Serverless 則可以在 PaaS 層提供更高層次的算力排程能力。
  • 第4個問題的核心是業務流程的抽象和業務功能的拆分。領域驅動設計以及服務化(微服務)在平台功能抽象拆分上提供了相對成熟的思路,催化了以縱向業務功能細分作為域劃分的依據的服務化方案以及組織結構,主要訴求是在細分的業務功能服務基礎上,能按需快速靈活的組合,進而支撐不同的業務模式,提供業務靈活性,支撐業務創新求變。

當然反過來,由于縱向功能細分,業務功能域增多,整個業務鍊條上的咬合點越來越多,随之産生越來越多的資料來源備援重複或者缺失,功能或者重合且各自發散,或者缺失,最終給整體業務帶來較多資料和功能的不一緻性風險。這樣一來,不僅橫向端到端的業務串聯成本高,而且關鍵路徑的風險收斂成本比較高,沖突沖突點集中在各縱向域功能和資料咬合處,具體表現為:

資料上:

  • 無主資料,有資料需求無 owner;
  • 大量重複且不一緻資料;

功能上:

  • 部分業務功能缺失;
  • 域之間存在業務功能重複且行為不一緻。

到底是縱向切分域還是橫向分業務模式拉平來做,這個問題沒有标準答案,更沒有最佳答案。隻有根據不同的業務發展階段及時動态調整試錯,換言之,這是一個不斷尋找相對最優解的動态過程。

彈性計算和 Serverless 解決了算力的問題,領域驅動服務化設計解決了功能的拆分和按需搭配組合的問題,那麼剩下的核心問題就是資料了:如何以一套統一的資料架構,既能支撐多租戶的資料安全性需求以及通用的資料存儲,也能支撐使用者擴充的自定義資料對象定義和模型變更,同時也要保證資料定義層面的擴充和變更不會影響自身和其他租戶業務功能的可用性。我們來分析下可能的方案(暫不考慮按服務邊界進行資料庫拆分):

(1)統一的資料庫,标準資料模型和擴充資料模型直接映射到實體表和索引:很顯然,對于不同租戶自定義的資料對象和資料模型要求是無法支撐的,實體資料模型會互相幹擾、互相沖突直到無以為繼。即使是對于所有租戶完全标準的功能和資料存儲,平台自身的标準模型更新的 DDL 也會對使用者的可用性造成較大影響,是以顯然是行不通的。

(2)如果為每個租戶建立各自的資料庫呢?各自租戶擁有各自的資料庫,可以滿足使用者資料安全隔離的需求,也可以滿足各租戶自定義的資料需求,看上去像是一種合理的 SaaS 資料方案。但是仔細分析,會發現有兩個明顯的問題:

  1. 如果使用者需要修改或者擴充現有實體資料模型而進行的 DDL 操作,必然會影響線上業務的整體可用性,也可能會影響到标準資料模型,進而影響到線上功能使用。
  2. 如果使用者可自定義對實體模型進行擴充和定制,當平台進行模型更新的時候,極容易産生實體模型的沖突,導緻新舊功能異常。
  3. 由于使用者在各自資料庫存在各自定義的擴充和定制,則平台資料模型和功能更新需要針對不同的租戶進行分别驗證,存在極大的更新驗證工作量和風險。

以上兩種方案可行性低,我們從其中發現的問題是:平台業務系統的邏輯模型到實體模型的直接映射是造成問題的主要因素。既然實體模型的變更是平台不穩定的動因,那麼我們是否能通過解耦業務邏輯模型和實體模型的映射關系來嘗試解決這個問題呢?

既然問題已經定義清楚了,如何解決這個問題呢?通常我們解決架構問題的一個“萬能”的方法是:增加一個層次,我們也來套用一次,增加一個層次(中繼資料層)來解耦邏輯模型到實體模型強映射的問題。

首先,我們需要對業務進行模組化,對業務進行抽象,定義出業務邏輯模型,然後對模型進行二次抽象,定義出邏輯模型的定義資料,實作業務模型的資料化,即模型的中繼資料(The Metadata of the Logic Model ),将模型結構存儲為資料,而不是直接對應的實體存儲結構。

其次根據定義出的中繼資料進行統一抽象,形成中繼資料邏輯模型。

将中繼資料邏輯模型映射到中繼資料實體模型,對應實際存儲結構。

通過對業務模型的變更,形成對中繼資料層的資料變更,而不是實體結構的變更,進而實作業務邏輯模型同實體模型的解耦。

SaaS|架構與背後的技術思考

很多事情說起來好像挺簡單,實際上是一個非常巨大的系統工程,将其付諸實踐是挑戰非常大的事情,而取得踏踏實實的成功則更難。上述問題的解題思路是 Salesforce 的解題思路,而且 Salesforce 不僅取得了成功,也接近将其做到了極緻,下面我們站在巨人的肩膀上來看看 Salesforce 如何通過中繼資料驅動的架構(核心是基礎資料架構)來支撐多租戶的 SaaS 業務平台。注意:由于 Salesforce 并未有對核心實作邏輯進行完全公開和說明,是以本文所整理的部分核心邏輯包含了作者的邏輯推理和解讀,但是确實進行了邏輯驗證和場景驗證,如有纰漏和不全面的地方,歡迎讨論及指正。

中繼資料驅動的多租戶架構

Salesforce 将 Force.com 定義為 PaaS 平台,Force.com 的基礎就是中繼資料驅動的軟體架構來支撐多租戶應用。首先我來解釋下什麼是以中繼資料驅動的軟體架構為核心。

一、多租戶意味着什麼

多租戶的含義用一句話來描述就是:一個雲平台,無數多個客戶。

一個雲平台的含義是:一個代碼庫,一個資料庫,一整套共享的可擴充服務,包括資料服務、應用服務以及 Web 服務。

無數多個客戶的含義是:每個客戶都被配置設定一個唯一的租戶 OrgID,所有的資料存儲都是按照租戶 OrgID 隔離的,所有的資料通路必須包含 OrgID,所有的操作也都是包含租戶 OrgID 的,也就是所有的客戶資料和行為都是被安全的通過唯一的租戶 Org 進行嚴格隔離的。

每個租戶/組織隻能看到和定義按照自己租戶 OrgID 隔離的自己版本的中繼資料和資料,而且隻能執行自己租戶 OrgID 所授權的行為,這樣每個租戶就擁有各自版本的 SaaS 方案。

二、中繼資料驅動意味着什麼

中繼資料對于平台意味着平台資料的資料,對于租戶意味着是關于租戶資料的資料。

當使用者定義一個新的使用者表的時候,使用者建立的不是資料庫中的實體表,而是在系統态的中繼資料表中添加了一條記錄,這個記錄描述的是使用者表的邏輯定義,是虛拟的,這個表并不在資料庫中實體存在,而這條記錄代表就是使用者态的資料表。

當使用者定義了使用者表的一個新的字段時,使用者并沒有在實體表中建立實體字段,而是在系統态的中繼資料表中添加了一個記錄,這個記錄描述的使用者表的字段組成的邏輯結構,是虛拟的,這個字段也不在資料庫表結構中實體存在,而這條記錄代表的就是使用者态的使用者表字段。

也就是通過存儲在系統态的中繼資料表中的中繼資料記錄作為虛拟使用者的資料庫結構。

三、中繼資料驅動的多租戶整體架構

我們先來大概了解下中繼資料驅動的多租戶的整體架構,整體架構大概分為 5 個邏輯層次:

1. 底層資料架構分為三個層次:

  • 最底層是資料層,存儲了離散的系統和使用者的業務資料,業務日常營運的資料存儲在這裡。
  • 公共中繼資料層,存儲了應用系統标準的對象和标準的字段定義,對底層資料的結構進行定義說明。
  • 租戶特定中繼資料,存儲了租戶自動的對象和自定義的字段定義,用于對底層的資料結構進行定義說明。

2. 通用資料字典 UDD(Universal Data Dictionary) 運作引擎層實作了應用對象到底層資料存儲的映射,包含對象模型操作、SOQL 語言解析、查詢優化,全文搜尋等功能,我們常說的 ORM 功能也是其核心功能,但比其複雜的多。

3. 平台服務層提供 PaaS 層平台服務,提供應用對象模型的建立,權限模型建立,邏輯和工作流程建立以及使用者界面的建立,包括螢幕布局、資料項、報表等

4. 标準應用層提供端到端的标準的業務應用功能。

5. 租戶虛拟應用層,使用者可以在标準應用層或者平台服務層之上定義自己特有的業務應用功能,滿足自己特定的業務場景需要。

SaaS|架構與背後的技術思考

其中,底層資料架構是最為關鍵的平台基石(The Corner Stone),其核心運作引擎也是基于強大的底層資料架構基礎上建構的。本文則以中繼資料驅動的多租戶資料架構為核心來一一展開。

四、中繼資料驅動的多租戶資料架構

下面我們具體來看下系統态的資料模型,基于 Salesforce 加上個人推理的中繼資料驅動的多租戶資料模型。注意:由于 Salesforce 并未有對核心邏輯進行完全公開和說明,是以本文所整理的部分核心模型包含了個人的邏輯推理和解讀,但是确實進行了邏輯驗證和場景驗證,如有纰漏和不全面的地方,歡迎讨論及指正。

Salesforce 雲服務平台遵循的是面向對象的設計理念,所有的實體、實體關系以及實體的 CRUD 均是以對象的視角進行的,是以其中繼資料驅動的多租戶資料模型的存儲基本元素也是按照對象的顆粒度進行存儲,源自于 OO 的對象間引用,同普通關系資料庫主外鍵關系異曲同工,隻是細節處理上不盡相同,請大家注意這一點。

1. 中繼資料驅動的多租戶資料架構概覽

首先,我們先來大概了解下中繼資料驅動的多租戶模型的核心内容,中繼資料驅動的多租戶的資料模型主要分為三個部分:中繼資料表、資料表和功能透視表。

  • 中繼資料表(Metadata Tables)

中繼資料表用于存放系統标準對象以及使用者自定義對象和字段定義的中繼資料,也就是系統和使用者對象的邏輯結構,即對應于關系資料庫中的虛拟表結構。中繼資料表主要包括Objects 表以及 Fields 表,是系統标準對象和使用者對象定義資料的倉庫,即中繼資料倉庫。

  • 資料表(Data Tables)

資料表使用者存放系統以及使用者對象和字段的實際資料,實際的使用者業務資料以及應用系統相關資料存放在這裡。資料表包括 Data 表和存放大文本資料的 Clob 表,資料表存儲了絕大部分使用者的實際資料,是一個巨大的使用者業務資料倉庫。

  • 功能透視表(Specialized Pivot Tables)

功能透視表包含了非常關鍵的關系表、索引表、關系表以及其他特定用途表。例如關系表定義了對象間的關系,索引表解決虛拟結構索引的問題,這部分後續将進行詳盡的介紹。

SaaS|架構與背後的技術思考

2. 中繼資料驅動的多租戶資料架構詳解

上一節粗略地描述了中繼資料驅動的多租戶模型三大部分模型實體和基本作用,大家可能會比較疑惑,這麼簡單一個實體模型,怎麼就起了這麼個牛逼的名字,而且支撐了“一個雲平台,無數個客戶”。我們下面就對此模型的核心邏輯進行詳細展開和推理說明,同時詳細闡述以此模型為中心的服務來說明整個中繼資料層或者說 UDD(Universal Data Dictionary) 層的設計。

土話說:“沒有對比,就沒有傷害”。道理是相通的,用相似的事物進行對比是對了解客觀事物比較好的方法,找出其相同點和共性的地方,找出其不同點和異樣的地方,同時識别出是否有不可對比的方面。從各個方面去對比,則能更全面、更深入的了解客觀事物。

下面我按照普通應用設計思路方式來定義一個簡單直覺的多租戶 SaaS 資料架構方案示例,作為中繼資料驅動多租戶資料架構方案的對比基準方案,用對比來更好的幫大家了解中繼資料驅動多租戶資料模型及架構的設計邏輯。

(1)普通多租戶 SaaS 資料架構方案示例(僅做示例)

  • 多租戶基本思路:每個租戶一個資料庫,提供資料庫級别的租戶資料隔離,平台提供标準應用功能模型,使用者可以在各自資料庫内定義以及修改各自的定義模型,所有模型采用資料庫實體表、索引、主外鍵實作。不同的租戶通過路由到不同的資料庫來實作隔離。
  • 域模型樣例采用大家都熟悉的最小集的訂單模型實作,包含商品、使用者、訂單和訂單詳情表。注意:此簡化模型僅用做示意說明,和意圖無關的大多數字段均省略,非嚴謹定義。
SaaS|架構與背後的技術思考
  • 示例模型資料

資料庫實體表資料:Customer

SaaS|架構與背後的技術思考

資料庫實體表資料:Product

SaaS|架構與背後的技術思考

資料庫實體表資料:Order

SaaS|架構與背後的技術思考

資料庫實體表資料:OrderItem

SaaS|架構與背後的技術思考
  • 實體表關系

Order 表同 OrderItem 為父子表,通過 OrderID 進行主外鍵關聯;Customer 表同 Order 表為父子表,通過 CustomerID 進行主外鍵關聯;Product 表同 OrderItem 表為父子表,通過 ProductID 進行主外鍵關聯。

  • 使用者自定制

使用者有執行 DDL 權限,可以在自己租戶資料庫内在進行擴充模型自定義,建立自定義的實體表,索引,關系等。

  • 問題和風險

使用者具有執行 DDL 權限,可以自定義資料庫實體模型,會帶來各租戶的自定義資料模型大爆炸,會給後續平台模型定義更新沖突,造成模型更新的巨大的障礙

同時,由于系統标準模型和使用者模型均為實體模型,未有做系統标準和自定義資料的有效隔離,如何保證平台應用的每一次更新必然會考慮對現有使用者自定義模型的穩定性和可用性的影響,在自定義實體模型的情況下,不僅挑戰巨大,而且包含巨大的回歸驗證的工作量,很難收斂。

當使用者執行 DDL 時,通常會鎖定資料庫實體資源,當資料庫數量非常巨大時可能會帶來不可控的 downtime,對應用系統的可用性造成巨大的影響。如果資料庫是每個租戶各自獨占,還隻會影響到單個租戶;如果是多租戶共享資料庫,則可能會影響到其他租戶,影響是災難性的。作為雲平台服務商,不管是使用者操作還是系統行為,我們都不期望我們的設計對使用者系統的可用性造成影響,是以使用者執行 DDL 的行為是否允許确實有待商榷,但是如果不允許,使用者可擴充性在這種設計環境中必然受到一定程度的限制。

(2)中繼資料驅動的多租戶資料模型(Metadata Tables)

前面章節描述了中繼資料驅動的多租戶模型簡單模型圖,本小節詳細解說下每個核心實體表的核心結構,同時已知資料部分較為簡略,無法描述模型全貌和核心細節,為了模型完整性,整體資料模型包含了作者思路推理部分,用以來完整清晰地定義模型。當然由于所有模型都是主觀的(subjective),僅代表個人觀點,歡迎大家的不同的觀點,一起讨論改進。

正如前面介紹“一個雲平台”時提到,通過一個統一的資料庫來支撐無數個租戶,是以中繼資料驅動的多租戶模型是基于一個共享資料庫的前提。當然多租戶實作設計多種多樣,大家可以不拘泥此種。

1)中繼資料表之對象定義表:Objects 表

SaaS|架構與背後的技術思考

Object 系統表存儲了每個租戶為它的擴充應用對象定義的中繼資料,包含如下核心字段:

  • ObjID:應用對象唯一辨別,具有固定長度和格式。
  • OrgID:應用對象所歸屬的租戶 ID,用于統一共享資料庫内的多租戶資料隔離,通常和租戶定義的域名對應。
  • ObjName/Name:對象名稱,用于系統配置和開發(developer name)。
  • Label: 對象的顯示名稱。

除了使用者自定義對象,系統的标準對象也是采用相同的方式進行定義的。

2)中繼資料表之字段與關系定義表:Fields 表

SaaS|架構與背後的技術思考

Fields 系統表存儲了每個租戶為他的擴充應用對象字段定義的中繼資料,包含了其所歸屬的應用對象的租戶 OrgID,字段所屬對象的 ObjID,字段定義辨別 FieldID,字段名稱FieldName,字段存儲位置定義 FieldNum,資料類型 DataType。資料類型重要補充關聯字段(DigitLeft,Scale,TextLength,RelatedTo,ChildRelationshipName)以及是否必選、唯一、索引标記,還有部分标準字段。Fields表非常關鍵,其不僅定義了普通的應用對象字段,包括基本資訊和資料類型資訊,而且通過特殊關系字段對不同應用對象之間的關系進行定義,詳細說明如下:

  • FieldID:此對象字段的唯一辨別,具有固定長度和格式
  • OrgID:其所歸屬的應用對象所歸屬的租戶 OrgID
  • ObjID:字段所屬對象的 ObjID
  • FieldName/Name:字段名,用于系統配置和開發(developer name)。
  • Label:字段展示名稱,用以展示給最終使用者。
  • FieldNum:對應到 Data 資料表的資料存儲字段映射,暨 Data 表中 ValueX 字段中的X。
  • DataType:指定此對象字段的資料類型包含普通類型:Number、TEXT、Auto Number、Date/Time、Email、Text Area等,也包含特殊的關系類型如:Look up關系類型、Master-Detail 關系類型等。
  • DigitLeft 和 Scale:用于 Number、Currency、Geolocation 等數字資料類型的關聯設定,例如定義了一個字段的 DateType 為 Number,則需要指定其整數部分的最大位數 DigitLeft 和小數部分的最大位數 Scale,兩部分長度總和不超過 18 位。
  • TextLength:當資料類型為 TEXT 時啟用,用于指定 TEXT 類型的字元的長度限制。
  • RelatedTo 和 ChildRelationshipName:這兩個字段當 DateType 為關系類型(Look up,Master-Detail 等)時會啟用,其中 RelatedTo 儲存關聯的應用對象 ID,ChildRelationshipName 用于儲存父子關系中子方的關系名稱,同一個父對象的子方的關系名稱唯一,用于關系的反向查詢。
  • IsRequired:此字段資料儲存時,是否校驗值的存在。
  • IsUnique:是否允許重複值。
  • IsIndexed:此字段是否需要建索引。
  • 其他字段:此處僅列舉了說明模型所需要的字段,其他字段暫不進行列舉,不列舉原因和其重要性并無直接關聯。

3)資料表(Data Tables)之關系資料表:Data 表

SaaS|架構與背後的技術思考

MTData 系統表存儲了 MTObjects 和 MT_Fields 中繼資料表内定義的資料對象(表)所對應的資料,一一映射到不同的租戶各自定義的表和表中的字段(對象和對象字段)。

  • GUID:資料表的主鍵,用于存放每個應用對象執行個體的辨別 ID。
  • ObjID:其所歸屬的應用對象所歸屬的租戶 OrgID。
  • Name:應用對象執行個體名稱。
  • Value0....Value500:用于存放對象執行個體字段的資料,其 ValueX 中 X 值對應到 Fields 表中 FieldNum 定義,ValueX 存放的資料,不管原始資料類型、存儲格式均為變長字元串格式。

4)資料表(Data Tables)之非結構化資料表:CLobs

MT_Clobs 用于存儲大字元段的存儲 CLOB,同時 CLOB 也存儲在資料庫外的索引結構中,用于快速的 Full-Text 文字檢索。

3. 中繼資料模型核心實體關系圖

我們在應用系統開發中,通常我們定義的資料結構包括資料表、表字段,索引通常都會直接定義在實體資料庫中,建立實體的表和字段以及索引等。

但是在中繼資料驅動平台資料模型中,我們定義的使用者表包括系統表都是邏輯表,其結構是虛拟的,使用者表的定義存儲在 Objects 表,對應的字段定義存儲在 Fields 表中,實際使用者資料存儲在 Data 表中。特别注意的是,對象的引用關系定義也定義在 Fields 表中,以特殊資料類型方式來定義。(另:Relationships 表後面章節進行描述)。

從每個租戶視角來看,每個租戶都在一個共享資料庫内擁有一個基于租戶辨別 OrgID 來隔離的虛拟的租戶資料庫。

中繼資料實體包括 Objects 和 Fileds 實體以及實際資料 Data 實體都包含租戶 OrgID,這樣就可以通過租戶 OrgID 來天然隔離各租戶的資料,當然不止這些實體,包括索引相關等透視表實體也使如此。

SaaS|架構與背後的技術思考

4、标準對象與标準字段

前面整體架構層次裡提到了公共中繼資料層和标準應用層,公共中繼資料層提供了标準對象和标準字段的定義。

其中标準對象為每個租戶提供公共端到端的應用的标準應用功能。

SaaS|架構與背後的技術思考

同時使用者可以在标準的對象基礎上擴充自定義的應用對象,滿足自己的特定業務場景。__c 字尾代表自定義,後續詳解。

SaaS|架構與背後的技術思考

而标準字段則提供給每個對象包括自定義對象的共同的字段,包含部分業務字段和非業務字段。

SaaS|架構與背後的技術思考

使用者也可以在标準對象和自定義對象内自定義不同的字段,以滿足業務需要。__c字尾代表自定義,後續詳解。

SaaS|架構與背後的技術思考

5、對象關系類型

應用對象關系類型主要分為 Look up 和 Master-Detail 兩種關系類型,其中 Look up 為弱的父子關系類型,Master-Detail 為強的父子關系類型,其特性對比如下。

SaaS|架構與背後的技術思考

6、中繼資料驅動的多租戶資料架構示例

同樣采用普通多租戶 SaaS 資料架構方案中相同的域模型和示例資料作為參照進行說明,隻不過在這裡域模型不再對應到資料庫的實體模型,而是對應到中繼資料所定義的虛拟資料庫的邏輯模型。請前後對比兩種模型對使用者業務模型承載的差異和聯系,以便深入了解中繼資料驅動的多租戶資料架構。

SaaS|架構與背後的技術思考

對于 Tenant 租戶 A00001,需要支撐相同的業務邏輯,需要定義相同的域模型,和普通的方案不同的是,這裡采用中繼資料驅動的多租戶資料模型來定義訂單域模型和對應示例資料,其中域模型定義在中繼資料表(Metadata Tables)中,資料存儲在 Data Tables 表中。

1)使用者自定義對象 Product 的定義

Product 對象的基本資訊定義在 Objects 表,作為 Objects 表的一條記錄,通過 OrgID 進行不同租戶資料隔離。Object 中的每一條記錄都代表一個不同的對象。Objects 表的定義非常清晰,這裡不做過多的解釋,請參考 Objects 表介紹。

SaaS|架構與背後的技術思考

Product 對象的字段結構定義在 Fields 表,同時通過 ObjID 同 Order 對象定義進行關聯,通過 OrgID 進行多租戶資料隔離。

FieldID 格式為字段定義的辨別 ID,用于區分每個字段定義,對于标準字段,則采用标準字段 ID,如 Name,則直接采用 Name 作為字段辨別 ID,對于自定義字段,則中繼資料引擎自動生成 15 位的标準格式的 FieldID。其他字段定義請參考前面的 Fields 中繼資料表詳細介紹。

下面較長的描述一下 Product 對象中每個字段定義:

  1. 産品名稱 Name 字段 為标準字段,資料格式為TEXT,長度為80。
  2. 産品編号 ProductNo 為自定義字段,資料格式為 TEXT,長度為 22,FieldNum 為 1 對應 Data 表存儲字段 Value1,存儲格式為變長字元串。
  3. 産品價格 ProductPrice 為自定義字段,資料格式為 Currentcy(此格式類似Number,不同是帶币種),整數最大長度 DigitLeft:16 位,小數位最大精度Scale:2 位,FieldNum 為 2 對應 Data 表存儲字段 Value3,存儲格式為變長字元串。
  4. 狀态 ProductStatus 為自定義字段,資料格式為 TEXT,長度為 20,FieldNum 為 3對應 Data 表存儲字段 Value3,存儲格式為變長字元串。
SaaS|架構與背後的技術思考

2)使用者自定義對象 Customer 的定義

Customer 對象的基本資訊定義在 Objects 表,作為 Objects 表的一條記錄,通過 OrgID 進行不同租戶資料隔離。Object 中的每一條記錄都代表一個不同的對象。Objects表的定義非常清晰,這裡不做過多的解釋,請參考Objects表介紹。

SaaS|架構與背後的技術思考

Customer 對象的字段結構定義在 Fields 表,同時通過 ObjID 同 Order 對象定義進行關聯,通過 OrgID 進行多租戶資料隔離。

下面較長的描述一下 Customer 對象中每個字段定義:

  • 使用者名稱 Name,必選标準字段,不過多解釋。
  • 使用者編号 CustomerNo 為自定義字段,資料類型為 TEXT,長度為 22,FieldNum 為 1 對應 Data 表存儲字段 Value1,存儲格式為變長字元串。
  • FirstName 和 LastName 為自定義字段,資料類型為 TEXT,長度均為 20,FieldNum 為 2,3 對應 Data 表存儲字段 Value2 和 Value3,存儲格式為變長字元串。
  • 使用者昵稱 Nick Name 為自定義字段,資料類型為 TEXT,長度均為 20,FieldNum 為 4 對應 Data 表存儲字段 Value4,存儲格式為變長字元串。
  • 使用者登入名 LoginName 為自定義字段,資料類型為 TEXT,長度均為 20,FieldNum 為 5 對應 Data 表存儲字段 Value5,存儲格式為變長字元串。
  • 使用者狀态 CustomerStatus 為自定義字段,資料類型為 TEXT 或者 PickList,長度為 20,FieldNum 為 6 對應 Data表存儲字段 Value6。為簡化起見,狀态字段暫定義為 TEXT,對應 Data 表存儲字段 Value4,存儲格式為變長字元串。
SaaS|架構與背後的技術思考

3)使用者訂單 Order 邏輯表的定義

Order 對象的基本資訊定義在 Objects 表,作為 Objects 表的一條記錄,通過 OrgID 進行多租戶資料隔離。Objects 表中的每一條記錄都代表一個不同的對象。

SaaS|架構與背後的技術思考

Order 對象的字段結構定義在 Fields 表,同時通過 ObjID 同 Order 對象定義進行關聯,通過 OrgID 進行多租戶資料隔離。

下面較長的描述一下 Order 對象中每個字段定義:

  • 訂單編号 OrderNo 為自定義字段,DataType 資料格式為 TEXT,長度為 22,FieldNum 為 1,對應 Data 表存儲字段 Value1,存儲格式為變長字元串。
  • 關系字段 Customer 為自定義關系字段,DataType 類型為弱類型 Look up 關系,關聯到父對象 Customer,則 RelatedTo 列存儲 Customer 的 ObjID:01I2v000002zTEZ,對應的 FieldNum 為 2,則 Customer 對象執行個體 GUID 存儲在 Data 表的 Value2 列。ChildRelationshipName 列存儲對象父子關系中子關系名稱:orders,用于對象關系中從父對象執行個體資料反查子對象執行個體資料。
  • 訂單狀态 OrderStatus 為自定義字段,DataType 類型為 TEXT,長度為 20,FieldNum 為 3,則狀态存儲在 Data 表的 Value3 列。為簡化起見,狀态字段暫定義為 TEXT。
  • 下單時間 OrderTime 為自定義字段,DataType 類型為 Date/Time,FieldNum 為4,則下單時間存儲在 Data 資料表的 Value4 列。
SaaS|架構與背後的技術思考

4)使用者訂單行 OrderItem 邏輯表定義同樣的,OrderItem 對象的基本資訊也以一條記錄的資訊定義在 Objects 表,通過 OrgID 進行多租戶資料隔離。Objects 表中的每一條記錄都代表一個不同的對象。

SaaS|架構與背後的技術思考

OrderItem 的字段結構也定義在 Fields 表,通過 ObjID 同 OrderItem 對象關聯,通過 OrgID 進行多租戶資料隔離。

下面較長的描述一下 Order 對象中每個字段定義:

  • 關系字段 Order 為自定義關系字段,DataType 類型為強類型的 Master-Detail 關系,關聯到父對象 Order,則 RelatedTo 列存儲 Order 對象的 ObjID:01I2v000002zTEj,對應的 FieldNum 為 1,則 Order 對象執行個體 GUID 存儲在 Data 表的 Value1 列。ChildRelationshipName 列存儲對象父子關系中子關系名稱:OrderItem(s),用于對象關系中從父對象 Order 執行個體資料反查子對象執行個體資料。
  • 關系字段 Product 為自定義關系字段,DataType 類型為弱類型的 Look up 關系,關聯到父對象 Product,則 RelatedTo 列存儲 Product 對象的 ObjID:01I2v000002zTEU,對應的 FieldNum 為 2,則 Product 對象執行個體 GUID 存儲在Data 表的 Value2 列。ChildRelationshipName 列存儲對象父子關系中子關系名稱:OrderItem(s),用于對象關系中從父對象 Product 執行個體資料反查子對象執行個體資料。
  • 商品實際售價 ItemPrice 為自定義字段,DateType 類型為 Currentcy(此格式類似 Number,不同是帶币種),整數最大長度 DigitLeft:16 位,小數位最大精度 Scale:2 位,FieldNum 為 2 對應 Data 表存儲列 Value3,存儲格式為變長字元串。
  • 商品購買數量 Item Quantity 為自定義字段,DataType 類型為 Number,整形長度為 18 位,無小數位數,FieldNum 為 4,對應 Data 資料表存儲列 Value4。
  • 訂單明細狀态 OrderItemStatus 為自定義字段,Datetype 類型為 TEXT,長度為 20,對應 FieldNum 為 5,對應 Data 資料表存儲列 Value5。為簡化起見,狀态字段暫定義為 TEXT。
SaaS|架構與背後的技術思考

5)對象 Schema

定義好的使用者應用對象 Schema 如下圖

SaaS|架構與背後的技術思考

6)資料表 Data 表使用者資料存儲

前面提到了使用者自定義的應用對象以虛拟結構的方式存儲在 Objects 和 Fields 表中,那麼使用者定義的應用對象 Product、Customer、Order 和 OrderItem 裡的資料存儲在哪裡呢?答案是 Data 表,使用者定義的對象的資料均會存儲在 Data 表中,每個使用者定義對象執行個體(或者近似稱為使用者表記錄)資料以 Data 表中一條記錄的形式存在。Product、Customer、Order 表的資料記錄均存儲在 Data 表,OrderItem 也亦是如此。

其中,GUID 作為每條資料記錄暨是每個對象執行個體的全局唯一辨別,OrgID 進行多租戶資料隔離,ObjID 同 Objects 表關聯代表具體哪個對象定義。這裡重點提一下,Fields 中定義的對象字段在 Data 表中的存儲,其中 Fields 表中 FieldNum 非常關鍵,它對應了對象執行個體字段在 Data 表中的具體存儲位置,FieldNum 對應數字決定着資料存儲在 Data 表中的哪個 ValueX 列。前面每個對象結構定義都對 FieldNum 對應 Data 的進行了說明,對象字段 FieldNum 可以不按照順序來,隻要 FieldNum 沒有占用,可以任意對應,當然按照順序是比較好的實踐。

再舉例來說:

  1. Order 對象的 Customer 關系字段定義在 Fields 表中,其 FieldNum 為 1,則其在 Data 表中存儲的位置,就是是 Order 對象執行個體在 Data 對應的記錄中 Value1 這個字段所存儲的值,存儲的值為 Customer 對象執行個體 GUID,也就是:a062v00001YXEKuAAP、a062v00001YXEKzAAP 等。
  2. OrderItem 對象的 Product、ItemQuantity 字段定義在 Fields 表中,其對應的 FieldNum 分别為2、4,則其在 Data 表中存儲的位置,就是 OrderItem 對象在 Data 對應的記錄中 Value2、以及 Value4 所存儲的資料,也就是:a052v00000jbgEQAAY、2以及a052v00000jbgMqAAI、3 等記錄。
SaaS|架構與背後的技術思考
SaaS|架構與背後的技術思考
SaaS|架構與背後的技術思考

7、通用的存儲,按需轉換 —Data 表資料類型與存儲

我們看了中繼資料驅動的多租戶模型的核心關系,明白了使用者自定義表(包括應用系統表)以及表結構是在 Objects 和 Fields 進行虛拟定義的,也清楚的知道了系統以及使用者表的資料是作為一條條記錄存儲在 Data 表中的,那麼我們下面來看下不同的資料類型如何在 Data 中進行存儲的呢?

在 Fields 表中,可以采用任何一種标準的結構化的資料類型,如 text,number,date,以及 date/time 對使用者表字段進行定義,也可以采用特殊結構的資料類型對字段類型進行定義,如下拉框 picklist,系統自增字段 autonumber,公式列(隻讀的公式推導列),布爾多選框,email,URL 以及其他的類型,當然也可以通過系統應用來對 Fields 中的自定義字段進行強制限制包括是否必須非空以及掐校驗規則(如符合特定格式,符合特定值範圍等)。

上述的各種不同字段格式資料都是存儲在 Data 表中的 ValueX 列中的,Data 表中包含 500 個資料列,稱為彈性列,用來存儲使用者資料和系統資料,也就是對應到 Objects 表和 Fields 表對應的虛拟表結構所要承載的資料。

特别的,所有彈性列都用了一個可變長度的字元串類型,以便于他們可以存儲任何結構化類型的應用和使用者資料(字元串,數字,日期等)。

正是因為彈性列把所有不同的資料類型拉平來存儲,是以任一彈性列可以對存儲任何對象的任何類型的屬性來存儲,使用者可以指定不同的對象的不同屬性對應的不同的存儲彈性列,當然同屬于相同對象的執行個體的屬性對應的彈性列是一緻的。一個彈性列可以存儲來不同的格式的資料,前提條件是這些資料屬于不同的對象的不同屬性。例如:上一節示例中,Data 表的 Value2 列可以存儲 Order 表的日期格式的 OrderTime 資料,也可以存儲 OrderItem 表的格式為字元串的 OrderID 資料。

SaaS|架構與背後的技術思考

如上所述,彈性列用通用資料類型暨可變長字元串來存儲所有類型的資料,這樣就可以在不同的使用者表字段間共享相同彈性列,即便它們的資料類型各異。

既然所有的資料全部用通用的可變長字元串來存儲,那麼應用邏輯處理需要不同的資料格式時候怎麼辦呢?具體做法如下:

當應用系統需要從彈性列讀取和寫入資料時候,UDD(Universal Data Dictionary) 層暨中繼資料運作引擎會用底層資料庫系統資料轉換函數(如 Oracle 資料庫的TONUMBER,TODATE,TO_CHAR 函數)按需對資料格式進行轉換,将字元串格式轉換成對應的資料格式(數字,日期等)。

如果存儲非結構化的大文本塊資料怎麼辦呢?模型支援對Clob大字段的定義,對于在 Data 表中具有 CLob 資料的每一行資料,系統将其存儲在 Clobs 透視表中,并按照需要同 Data 表的對應資料對象執行個體記錄進行關聯。

8、多租戶索引透視表 (Pivot Tables)

1)Indexes 透視表

大多數結構化的資料存儲在 Data 表内,如前面提到的,所有這些不同類型資料都是以可變字元串的形式存在 ValueX 列裡面如各種數字以及日期等全部都是以可變字元存儲的,這樣雖然對于對象執行個體各種字段的存儲确實非常靈活,不同的列可以存儲不同類型的資料,即使同一 ValueX 列不同的對象也可以存儲類型的資料,但是這樣帶來一個巨大的問題,由于不同的資料類型以可變字元串的方式存儲在同一列内,你沒辦法利用底層資料庫索引的能力對其進行排序,ValueX 列的資料都是一種按照離散的順序來存儲的。傳統的資料庫依賴原生的資料庫索引來快速在資料表内定位到符合查詢條件的記錄。而按照 Data 表ValueX列的資料存儲情況,在 Data 表建立 ValueX 列的索引來支撐資料快速查詢是不現實的。

是以解決辦法就是建立另外的透視表叫做 Indexes 索引表,并把資料拷貝出資料表并轉換成原始的的資料類型,并存儲到Indexes索引表列内,如原來是整形的資料以可變字元串的格式存儲 在ValueX 列中,拷貝到 Indexes 表之前通過函數将其轉換為原始的資料類型,在存儲到 Indexes 對應的 NumValue 列内,以友善建立索引,Indexes 表包含強類型的索引類,像 StringValue,NumValue,DataValue,用來定位對應資料類型的字段資料。

SaaS|架構與背後的技術思考

Indexes透視表的字段說明如下:

  • OrgID:其所歸屬的應用對象所歸屬的租戶OrgID
  • ObjID:字段所屬應用對象唯一辨別
  • FieldNum:對象字段存儲位置
  • ObjInstanceGUID:對象執行個體唯一辨別
  • StringValue:強類型的字元串列
  • NumValue:強類型的數字列
  • DateValue:強類型的日期列

下面的 Indexes 表示例包含對字元、數字和日期性資料的索引需求支援,資料來源于前面的 Data 表資料。

SaaS|架構與背後的技術思考
SaaS|架構與背後的技術思考
SaaS|架構與背後的技術思考

Indexes 表的底層索引是标準的,采用非唯一性的資料庫索引。當做對象檢索查詢的時候,實際上不是在Data資料表上做查詢,而是在 Indexes 索引表上做的查詢,擷取到OrgID,ObjectID 以及 GUID,然後再傳回資料表擷取資料。也就是當系統查詢條件包含對象執行個體的結構化的字段時,系統查詢優化器采用 MT_Indexes 來幫助優化相關的資料通路操作。

2)Unique Indexes透 視表

由于 Data 資料表的多資料類型的無差别存儲,無法在 Data 資料表建唯一性的索引供使用者來使用對對象字段值進行唯一性校驗。為了支援使用者對象自定義字段的唯一性校驗,解決辦法是采用了 UniqueIndexes 透視表;這個表非常類似于 Indexes 表,不過 Uniqueindexes 采用底層原生的資料庫索引來強制唯一性校驗。當一個使用者嘗試來插入一個重複的值到具有唯一性限制的對象字段時,或者當使用者嘗試去在一個現存的包含唯一性的字段進行強制唯一性時,系統會給出唯一性校驗失敗的提示,阻止使用者的下一步操作。

SaaS|架構與背後的技術思考

Unique Indexes 透視表的核心字段說明如下:

  • UniqueStringValue:唯一的字元串列
  • UniqueNumValue:唯一的數字列
  • UniqueDateValue:唯一的日期列
  • 其他字段定義請參考 Indexes 透視表

3)Relationships 索引透視表

在中繼資料驅動的多租戶模型中,提到了在 Objects 表以及 Fields 表中儲存了使用者對象結構和對象關系的定義,對象關系的定義是通過中繼資料模型 Fields 表字段資料類型提供了一個特殊的資料類型:“關系” (Relationship), 來給使用者用于聲明不同的使用者應用對象之間的關系,也就是我們通常說引用完整性。

對象之間的引用關系定義以及對象執行個體間的引用關系存儲在中繼資料表 Objects、Fields 中和 Data 表中,關聯查詢關系複雜,為了提升對象之間查詢的效率,特别是通過對象互相引用關系對對象執行個體資料進行檢索,系統提供關系索引透視表 Relationship 來優化對象引用關聯查詢。

SaaS|架構與背後的技術思考

Relationships 索引透視表的字段說明如下:

  • OrgID:其所歸屬的應用對象所歸屬的租戶 OrgID
  • ObjID:子對象的對象辨別
  • GUID:子對象執行個體的唯一辨別
  • RelationID:子對象内關系字段定義的辨別
  • TargetObjInstanceID:父對象執行個體的唯一辨別

關系透視表 Relationship 定義了兩個底層資料庫複合索引:

  • 第一個索引字段:OrgID + GUID,用于從子對象到父對象的關聯查詢。
  • 第二個索引字段:OrgID + ObjID + RelationID + TargetObjInstanceID,用于父對象到子對象的關聯查詢。

Relationships 索引透視表會在後面 SOQL 章節進行進一步描述驗證。

4)其他索引透視表

其他索引透視表的邏輯類似,都是為了滿足特定檢索和查詢需要,将資料同步到索引表,供應用系統使用。此處不再贅述,如确實有需要再補充。

五、SOQL 與關系 Relationships

SOQL 是 Salesforce Object Query Language 的簡稱,具有 SQL 類似的文法結構,就像前面提到的一樣,Salesforce 是以應用對象(Salesforce Object,簡稱 SObject)的視角管理業務資料和功能,SOQL 類似對用于對應有對象資料進行查詢的 API。

1、從SQL 到 SOQL

SOQL 也是采用類似表查詢的結構,同 SQL 非常相似,也通過底層資料庫索引來提供查詢優化支撐。不同點如下:

  • 沒有 select *
  • 沒有視圖概念
  • SOQL 是隻讀的
  • 由于底層中繼資料驅動的多租戶資料模型的限制,索引是受限制的,沒有原生資料庫實體結構豐富的索引支援。
  • 對象到關系的映射 (Object-Relational Mapping) 是自動完成的.
  • SObjects 在多租戶環境中并不是對應實際的實體資料表
  • SObjects 包括 SObjects 之間的關系都是以中繼資料的方式存儲在多租戶環境中的。

2、SOQL 示例&文法

下面我用示例來說明一下 SOQL 的用法,同時引出SOQL的特殊文法說明,SOQL 大小寫不敏感。

1)單個對象的查詢及文法說明

select id,productno__c,name,productprice__c,productstatus__c from product__c           

複制

SaaS|架構與背後的技術思考

前面提到過系統提供了标準應用對象和标準字段定義,更大的優勢在于支援使用者自行自定義對象和字段。這裡__c 代表的使使用者自定義的含義, product__c 代表的使用者自定義對象 Product,而非系統标準對象和字段,系統标準對象和字段在 SOQL 無需__c 字尾,如ID,Name,CreatedBy 等字段則為系統提供給每個對象的标準字段,而字段 ProductNo 為使用者自定義字段,則 SOQL 中的文法表示為 productno__c。這樣的好處是講标準和使用者自定義對象和字段很容易區分開,系統可以定義标準 Product 對象,以 product 表示,使用者也可以同樣定義一個 Product 對象,不過 SOQL 用 product__c 表示用于區分。

2)子對象關聯父對象 (Child to Parent) 查詢及文法說明

select id,name,orderno__c,
customer__c,
customer__r.customerno__c,customer__r.name,
orderstatus__c,ordertime__c
from order__c order by orderno__c           

複制

SaaS|架構與背後的技術思考
select id,name,orderno__c,

customer__c,
customer__r.customerno__c,customer__r.name,
orderstatus__c,ordertime__c
from order__c
where customer__r.name='Cheng Yan'
order by orderno__c           

複制

SaaS|架構與背後的技術思考

這裡是從子對象 Order 關聯到父對象 Customer 進行查詢,其中:

  • from 後面的對象 order__c 表示 Order 為用于自定義對象
  • Id,name 為 Order 對象内系統定義的标準字段,
  • Orderno__c,customer__c,orderstatus__c,ordertime__c 為使用者自定義字段,這裡需要說明的是 customer__c 自定義字段存儲的是父對象執行個體 ID
  • customer__r 就特别有意思,其中__r 部分代表父對象關系引用,customer 部分對應關系字段名,customer__r 代表從 Order 對象到 Customer 對象的一個應用關系,并通過 customer__r.customerno__c,customer__r.name 擷取到 Customer 對象的字段值。

3)父對象關聯子對象 (Parent to Child) 查詢及文法說明

select id,orderno__c,customer__r.name,ordertime__c,orderstatus__c,
(
select id,
product__r.productno__c,product__r.name,product__r.productprice__c
from orderitem__r
)
from order__c
order by orderno__c           

複制

SaaS|架構與背後的技術思考

這個語句稍微有些複雜,從 Order 對象關聯到 OrderItem 對象,又從 OrderItem 關聯到 Product,同時還包含了 Order 對象到 Customer 對象的關聯。

這裡着重說一下從父對象到子對象的關聯,父到子的關聯是在父對象的主查詢語句中在查詢字段中用()來封裝到子對象的關聯,其中

  • 子句中 from orderitem__r 的 orderitem__r 代表的是對子對象 OrderItem 的引用,orderitem 對應的為前文關系字段中提到的 ChildRelationshipName,并且同一個父對象的子方的關系名稱唯一(父對象 Name+ChildRelationshipName 必須唯一),用作父對象到子對象的查詢關聯。
  • 子句中 id,product__r.productno__c,product__r.name,product__r.productprice__c 的上下文為 orderitem__r 代表的子對象。

3、Relationships 索引透視表

Relationships 是為了 SOQL 的快速對象關聯查詢所定義的,子對象關聯父對象( Child to Parent) 查詢,複合索引(OrgID+GUID)在 Join 中起到較大作用,而需要從父對象關聯子對象 (Parent to Child) 查詢,則複合索引 (OrgID + ObjID + RelationID + TargetObjInstanceID) 在 Join 中起到較大作用。

六、如何支撐多租戶巨大資料量

前面我們提到 Salesforce 一個共享資料庫的概念,那一個共享資料庫怎麼來支撐如此巨大的多租戶資料庫呢,同時不僅需要支援巨量資料,并且還可以支撐租戶間的資料實體隔離,保證各租戶的資料穩定性、可用性和資料安全?

Salesforce 的做法是:分區。所有的 Force.com 的資料,中繼資料,透視表結構,包含底層資料庫索引,都是通過對 OrgID 進行實體分區的,采用的是原生的資料庫分區機制。所有的資料以及中繼資料通過你的 OrgID(16digits)進行分片 Hash。

資料分區是資料庫系統提供的被驗證過的技術,用以實體劃分較大的邏輯資料結構到較小的可以管理的區塊中。分區也可以幫助提升性能和擴充性,貼别是在多租戶環境下一個巨大的資料系統的擴充性。根據定義,每一個 SOQL 的查詢對應一個特别的租戶資訊,是以查詢優化器,僅僅需要考慮通路包含對應租戶的資料分區通路,而不是整個表或者索引。

七、無感的對象結構變更(No DDL)

當一個應用系統或者服務元件需要對其資料模型進行更新的時候,通常會通過資料庫 DDL 語言對資料庫實體結構進行操作,如果涉及的資料量較大,則可能會造成較長時間的資料庫變更時效,造成對應時間内的系統不可用,如果是多租戶系統還會可能其他租戶的可用性造成影響,抑或造成諸多的底層模型不一緻産生。

在中繼資料驅動的資料架構中,所有的 DDL 語言操作對應的使中繼資料層的中繼資料的記錄的更新,不涉及資料庫實體結構的更新,不會造成變更期間的資料庫實體結構耗時調整造成的不可用,同時系統平台提供了一個高效的機制來減少對平台多租戶應用總體性能影響。

當使用者修改了一個表字段列的資料結構,從一種資料類型改成另外一種不同存儲格式的資料類型時候,系統會重新分派一個新的彈性列給到這個字段列的資料,将資料從原來的存儲彈性列批量拷貝到新的彈性列,然後才會更新此字段列的中繼資料,暨在 Fields 表中更新這個字段列的中繼資料,将資料類型更改為新的資料類型,并将 FieldNum 更新為新的 ValueX 列對應的X值。

同時,在如上對使用者邏輯表結構調整生效過程中,原來的資料結構和對應的資料通路正常進行,直到邏輯表結構變更生效,對應用系統可用性不會造成影響,使用者對此無感覺。

八、多租戶架構對于研發人員意味着什麼

對于研發人員來說,多租戶結構最多意味着兩個版本:目前版本,以及下一個版本。沒有遺留版本需要維護。所有人不用操心舊的技術,舊的版本,所有隻有最新的版本,隻需要關心最新的版本。

這樣就給靈活開發帶來極大的好處,每年做個位的釋出,每次釋出幾百個新的特性新的版本也不會改變使用者的體驗,新的特性可以根據使用者需要開啟,通過特性管理來開關。

新版本釋出前,提供沙箱環境來允許使用者提前試用新版本的系統。如果做 bug 修複,則是在所有租戶層面上進行統一修複的。

對于使用者應用的釋出進行嚴格管理,防止對其他租戶産生影響,通過提供沙箱環境來讓使用者驗證新應用釋出,并通過成千上萬的自動化測試保證使用者的正常功能。

在運作期間,不作任何底層 DDL 操作,不會做表的建立,也不會做表的變更,隻可能在極少數的更新周期時候進行。

作者簡介:程彥,曾就職于阿裡數字供應鍊事業部擔任多年供應鍊計劃域研發,目前在阿裡資料中台負責相關商業化産品開發。