????這幾年,在公司嘗試轉(zhuǎn)型做產(chǎn)品。所以引入了很多的產(chǎn)品的理念。不管是對(duì)產(chǎn)品的定義,還是針對(duì)產(chǎn)品的管理,以及摸索產(chǎn)品的落地等等。我之前更多的是接觸的ToB端,所以想必也猜到了是一個(gè)SaaS模式的產(chǎn)品。其實(shí),現(xiàn)在回想并總結(jié),之前所做的產(chǎn)品并不理想。當(dāng)然,在這里就不多的來介紹了。
????在一個(gè)月前不久,我給了一個(gè)朋友一份代碼,還包含對(duì)產(chǎn)品的技術(shù)框架以及規(guī)劃的文檔。補(bǔ)充說一下,我很慶幸在一個(gè)新的環(huán)境接手在重新設(shè)計(jì)產(chǎn)品。所以基于之前的沉淀,有了一定的提升。那么這個(gè)朋友也是我之前在一次技術(shù)交流會(huì)上分享技術(shù)的時(shí)候所認(rèn)識(shí)的。這次呢,我們在微信上一起討論關(guān)于SaaS模式的產(chǎn)品時(shí),我給他灌輸了我對(duì)部分產(chǎn)品的理解,以及我參與過的產(chǎn)品的一些經(jīng)驗(yàn),并給他一些思路和參考。
????據(jù)悉,他現(xiàn)在也是在參與一個(gè)項(xiàng)目。大篇幅的概念過后,他顯然有點(diǎn)不滿足。加之我說我們現(xiàn)在所用到的一些技術(shù)方案時(shí),他也變現(xiàn)得格外的感興趣,并最后我也將一個(gè)產(chǎn)品的代碼做了裁剪后,也一并分享給了他。
????好了,寫到這里,我覺得應(yīng)該要進(jìn)入主題了。
????本篇文章呢,我想聊一聊關(guān)于SaaS產(chǎn)品里面一個(gè)重要的概念,多租戶。這個(gè)也是他后來不斷讓我給他解釋和講解的內(nèi)容,所以我索性整理一下。
????在以往傳統(tǒng)的項(xiàng)目中,我們一般地是面向特定的客戶實(shí)現(xiàn)一套特定的系統(tǒng),并部署到對(duì)應(yīng)的企業(yè)內(nèi)部中。不同的企業(yè)或組織各自部署一套自己的軟件系統(tǒng)。而現(xiàn)在面向ToB端的SaaS模式,是將一套軟件服務(wù)部署到云端或特定服務(wù)環(huán)境中,面向不同的企業(yè)或組織提供相同的服務(wù)。這也很符合目前“輕資產(chǎn)”的模式。這里有一張圖比較形象:

????在SaaS模式的平臺(tái)中,我們需要考慮和規(guī)劃好如何將一套相同的服務(wù)提供給不同的企業(yè)或組織使用,并且有且只能使用或操作所屬范圍中的數(shù)據(jù)。當(dāng)然,不同企業(yè)或組織對(duì)平臺(tái)功能的使用,通常我們把這種使用的企業(yè)或組織客戶稱之為“租戶”。所以,我們可以總結(jié)出一句話:多租戶技術(shù),是一種架構(gòu)模式,是實(shí)現(xiàn)如何在多用戶環(huán)境下共用相同的系統(tǒng)或程序組件,并且達(dá)到各用戶間數(shù)據(jù)的“獨(dú)立”的技術(shù)。
????在SaaS平臺(tái)里,注重的就是數(shù)據(jù)的“獨(dú)立性”,也是隔離性。如何在共有的一套系統(tǒng)架構(gòu)與服務(wù),仍可以保障客戶的數(shù)據(jù)相對(duì)獨(dú)立的正常使用。這也是SaaS產(chǎn)品必須面對(duì)的問題。由此帶來了一些新的挑戰(zhàn)。
????一般地,以支持多租戶的運(yùn)行技術(shù)總體可分為三種:獨(dú)立數(shù)據(jù)庫、共享數(shù)據(jù)庫但獨(dú)立Schema、共享數(shù)據(jù)庫且共享數(shù)據(jù)表。他們各自又存在各自的優(yōu)劣性。接下來,就具體聊一聊。
1、獨(dú)立數(shù)據(jù)庫
????針對(duì)獨(dú)立數(shù)據(jù)庫的這種方式,首先需要業(yè)務(wù)層能夠支持多數(shù)據(jù)源的配置,并且為每個(gè)租戶創(chuàng)建或初始化一個(gè)數(shù)據(jù)庫。應(yīng)用程序和數(shù)據(jù)庫都是獨(dú)立的實(shí)例,因此它不會(huì)與任何其他獨(dú)立實(shí)例交互。只為一個(gè)租戶提供服務(wù),擁有獨(dú)立的服務(wù)、獨(dú)立的數(shù)據(jù)庫以及獨(dú)立的請求處理。

優(yōu)點(diǎn):為不同的租戶提供獨(dú)立的數(shù)據(jù)庫,有助于簡化數(shù)據(jù)模型的擴(kuò)展設(shè)計(jì),滿足不同租戶的獨(dú)特需求;如果出現(xiàn)故障,數(shù)據(jù)影響小、恢復(fù)數(shù)據(jù)比較簡單。
缺點(diǎn):增多了數(shù)據(jù)庫的安裝數(shù)量,隨之帶來維護(hù)成本和購置成本的增加這種方案與傳統(tǒng)的一個(gè)客戶、一套數(shù)據(jù)、一套部署類似,差別只在于軟件統(tǒng)一部署在運(yùn)營商那里。由此可見此方案用戶數(shù)據(jù)隔離級(jí)別最高,安全性最好,但是成本較高。
2、共享數(shù)據(jù)庫但獨(dú)立Schema
共享數(shù)據(jù)庫、獨(dú)立Schema模式,是將多個(gè)或所有租戶的數(shù)據(jù)放在一個(gè)數(shù)據(jù)庫服務(wù)中,但是為每一個(gè)租戶建立一個(gè)獨(dú)立的schema。租戶間數(shù)據(jù)彼此邏輯不可見,上層應(yīng)用程序的實(shí)現(xiàn)和獨(dú)立數(shù)據(jù)庫一樣簡單。(補(bǔ)充:mysql數(shù)據(jù)中的schema比較特殊,并不是數(shù)據(jù)庫的下一級(jí),而是等同于數(shù)據(jù)庫。)
優(yōu)點(diǎn):對(duì)于安全性要求較高的租戶,是一種選擇。提供了一定程度的邏輯數(shù)據(jù)隔離,但并不是完全隔離;每個(gè)數(shù)據(jù)庫可支持更多的租戶數(shù)量。
缺點(diǎn):如果出現(xiàn)故障,數(shù)據(jù)恢復(fù)比較困難,因?yàn)榛謴?fù)數(shù)據(jù)庫將牽涉到其他租戶的數(shù)據(jù);如果需要跨租戶統(tǒng)計(jì)數(shù)據(jù),存在一定困難。這種方案是方案一的變種。只需要安裝一份數(shù)據(jù)庫服務(wù),通過不同的Schema對(duì)不同租戶的數(shù)據(jù)進(jìn)行隔離。由于數(shù)據(jù)庫服務(wù)是共享的,所以成本相對(duì)低廉。
3、共享數(shù)據(jù)庫、共享數(shù)據(jù)表
????共享數(shù)據(jù)庫、共享數(shù)據(jù)表,指的是多個(gè)或所有租戶共享同一個(gè)數(shù)據(jù)庫(Database)。所有的租戶數(shù)據(jù)都存在同一個(gè)數(shù)據(jù)和同一套表中。通過數(shù)據(jù)庫或表設(shè)計(jì)的租戶ID等租戶標(biāo)志字段,來表明該記錄是屬于哪個(gè)租戶的。

優(yōu)點(diǎn):所有租戶使用同一套數(shù)據(jù)庫,所以成本低廉;能夠簡單進(jìn)行數(shù)據(jù)聚合統(tǒng)計(jì)或分析。
缺點(diǎn):隔離級(jí)別最低,安全性最低,需要在設(shè)計(jì)開發(fā)時(shí)加大對(duì)安全的開發(fā)量,數(shù)據(jù)備份和恢復(fù)最困難。
????接下來,我聊一聊我們在某個(gè)產(chǎn)品上的實(shí)踐。這里僅針對(duì)實(shí)現(xiàn)邏輯做簡單梳理。不深入展開。
????我們在開展某個(gè)ToB的SaaS產(chǎn)品規(guī)劃時(shí),采用的是微服務(wù)的模式。所以我們從路由網(wǎng)關(guān)、認(rèn)證鑒權(quán)、以及對(duì)應(yīng)的業(yè)務(wù)應(yīng)用和數(shù)據(jù)庫設(shè)計(jì)做了整體性的規(guī)劃和實(shí)現(xiàn)。如下圖,這是是我們之前設(shè)計(jì)的一版架構(gòu)圖,后來有所調(diào)整就不展開了。

????在這個(gè)產(chǎn)品中,我們選擇的是共享數(shù)據(jù)庫、共享數(shù)據(jù)表的模式進(jìn)行實(shí)踐的。
????首先,我們?yōu)榱俗鈶裟芊穹奖愕脑L問,以及平臺(tái)能自動(dòng)識(shí)別訪問是哪個(gè)租戶,我們在接入層采用通過url來識(shí)別租戶。即系統(tǒng)在初始化租戶信息時(shí),會(huì)隨機(jī)生成一個(gè)租戶編碼(租戶編碼允許修改一次),用于saas平臺(tái)的三級(jí)域名監(jiān)聽,通過在業(yè)務(wù)系統(tǒng)的處理和綁定,當(dāng)接收到請求時(shí),攔截器會(huì)自動(dòng)識(shí)別對(duì)應(yīng)的租戶編碼,并加載對(duì)應(yīng)的租戶信息。
????其次,在業(yè)務(wù)處理時(shí),租戶標(biāo)識(shí)編號(hào)作為必須條件帶入,進(jìn)行數(shù)據(jù)操作。這里可以拓展下。目前在構(gòu)建時(shí),我們預(yù)留了分庫分表的處理策略。當(dāng)我們的用戶量和業(yè)務(wù)所殘生的數(shù)據(jù)呈現(xiàn)線性增長模式時(shí),采用一般性的單數(shù)據(jù)庫或者讀寫分離已經(jīng)遠(yuǎn)遠(yuǎn)不能滿足訪問性能的要求了。加上我們在很多微應(yīng)用上采用的是一個(gè)數(shù)據(jù)庫的原則。數(shù)據(jù)庫的IO將是一個(gè)直接面臨的問題。所以我們已經(jīng)預(yù)留了按照租戶分庫的策略。
????針對(duì)數(shù)據(jù)庫分庫,我推薦兩個(gè)方案。一個(gè)是代碼編程式的sharding-jdbc、一個(gè)是采用中間件技術(shù)(MyCat、DBLE)等。其中dble也是基于mycat的版本。
????sharding-jdbc是代碼編程的方式,通過定義對(duì)應(yīng)的分庫規(guī)則來引導(dǎo)對(duì)應(yīng)的數(shù)據(jù)持久化到對(duì)應(yīng)的庫中。這種方式需要編碼規(guī)范并且業(yè)務(wù)實(shí)現(xiàn)要關(guān)注這塊邏輯。
????數(shù)據(jù)庫中間件,是將分庫分表的規(guī)則邏輯從應(yīng)用程序提取出來,在中間件上來維護(hù)對(duì)應(yīng)的規(guī)則。好處就是降低了業(yè)務(wù)人員對(duì)數(shù)據(jù)處理的關(guān)注,讓業(yè)務(wù)實(shí)現(xiàn)更關(guān)注業(yè)務(wù)實(shí)現(xiàn)。
????在這里不具體展開對(duì)這些技術(shù)的詳細(xì)講解了。我們采用的是中間件MyCat1.6.5來實(shí)現(xiàn)。
????接著,在MyCat中配置rule規(guī)則,通過tenantId%4的規(guī)則,將數(shù)據(jù)分到4個(gè)物理數(shù)據(jù)庫中。在業(yè)務(wù)代碼層,只需要指向一個(gè)數(shù)據(jù)庫,通過數(shù)據(jù)庫中間件來維持多數(shù)據(jù)源的連接和數(shù)據(jù)處理(如果涉及到分布式事務(wù)的話,請謹(jǐn)慎使用)。
????最后,就是公約和規(guī)范了。其實(shí),不管是采用編碼式還是數(shù)據(jù)庫中間件,我們都需要一個(gè)租戶的唯一標(biāo)識(shí)。然后建立數(shù)據(jù)庫路由,以租戶id為路由標(biāo)識(shí),來數(shù)據(jù)庫語句路由到對(duì)應(yīng)的數(shù)據(jù)庫中去執(zhí)行。在維護(hù)sql語句時(shí),有一些需要注意或遵守的地方。
????????1、如果有全局表,那么在執(zhí)行全局表數(shù)據(jù)時(shí),可以按照對(duì)應(yīng)的業(yè)務(wù)需求進(jìn)行編寫。
????????2、如果需要執(zhí)行某個(gè)租戶的信息,那么Sql必須要加上租戶的唯一標(biāo)識(shí)條件,且放在第一位條件。
????????3、如需要進(jìn)行全局表或夸庫數(shù)據(jù)聯(lián)表查詢,分開查詢。具體的要結(jié)合中間件支持程度。
????????4、數(shù)據(jù)庫聯(lián)表執(zhí)行,盡量保持在3張表以內(nèi)。超過3張表聯(lián)表,可以拆分執(zhí)行。