天天看點

多租戶架構的動态資料源方案

作者:Esgoon

本文首先介紹了多租戶架構(Multi-Tenant)系統資料隔離的幾種方案,其後介紹了基于SpringBoot、MySQL的一種動态資料源的實作。

多租戶架構資料隔離

多租戶架構資料隔離通常有三種實作方案。

1、獨立資料庫

獨立資料庫即一個租戶(Tenant)一個資料庫(DB),這種方案的資料隔離級别最高,安全性最好,可以滿足不同租戶的獨特需求,發生故障時恢複資料比較簡單且不會影響到其他租戶。但相應的成本也高。

多租戶架構的動态資料源方案

注意:這裡所講的資料庫與我們日常開發溝通中所說的資料庫的概念是有差別的,這裡所指獨立資料庫的意思是一個資料庫服務執行個體,通常對應于一套實體伺服器。而我們開發溝通中說的資料庫通常是指資料庫執行個體中的一個Schema。

2、共享資料庫,獨立Schema

這種實作方案是多個租戶甚至所有租戶共享同一個資料庫,但每個租戶獨立使用Schema。這種方案的優點是一個資料庫執行個體可以支援更多的租戶數量。缺點是發生故障時恢複困難,且容易影響到其他租戶。

多租戶架構的動态資料源方案

3、共享資料庫共享Schema

多租戶架構的動态資料源方案

這種方案是所有租戶共享同一個資料庫、同一個Schema,不同租戶的資料存放到相同的資料庫表中,資料庫表通常會增加tenant_id字段用以區分租戶的資料。這是共享級别最高,隔離級别最低的方式。

多租戶架構的動态資料源方案

以上三種資料隔離方案中,第一種獨立資料庫我們通常稱為實體隔離,其他兩種我們稱為邏輯隔離。

技術實作方面,獨立資料庫和獨立Schema這兩個方式都會涉及到不同的租戶對應不同的資料庫連接配接URL、資料庫通路賬号和密碼。也就是說,多租戶系統使用過程中,不同租戶下的使用者使用的是不同的資料源(DataSource),使用者的每一次資料庫操作,都需要首先根據使用者身份(所屬租戶的tenant_id)動态擷取到相應的資料源。

動态資料源

本節以後端SpringBoot、MySQL技術講解一種動态資料源的實作方案。

在一個多租戶系統中,資料存儲采用獨立資料庫或是獨立Schema的隔離方式無疑是最靈活的方案,友善資料庫層的擴充,且使得各個租戶之間互不影響。系統實作方面,也就需要動态資料源支撐。

所謂動态資料源,是需要後端服務在運作過程中,根據操作使用者的身份,即所屬的租戶,動态擷取到對應于這個租戶的資料庫連接配接,進而達到對資料的操作是發生在正确的目标資料庫的目的。

在獨立資料庫或獨立Schema的資料隔離方案中,因為每一個租戶都有其自己的資料源資訊,如JDBC URL、資料庫使用者名/密碼、甚至是最大連接配接數、最小連接配接數等等,首先用一個表将這些資訊維護起來。在生産環境中,可以是通過SQL腳本或一個背景管理頁面進行維護。

多租戶架構的動态資料源方案

存儲資料源的表應該位于一個獨立的資料庫,作為一種公共資源,與使用者業務資料庫分開。提供一個獨立的後端服務(假設命名為tenant服務),對該資料源表進行維護。其他的業務服務通過tenant服務提供的接口擷取資料源資訊。

資料源的加載

接下來,需要考慮動态資料源的裝載與維護邏輯。需要在SpringBoot服務啟動時加載資料源。

定義一個資料源加載類,實作FactoryBean與InitializingBean接口。實作afterPropertiesSet()方法,在該方法中,通過feign調用tenant服務,擷取到全部租戶的資料源資訊。

周遊這些資料源資訊并建立DataSource對象,将DataSource對象加入ConcurrentHashMap集合,集合元素的鍵為tenant_id,值為這個tenant_id所對應的DataSource對象。看到這裡,或許大家已基本清楚。沒錯,後面在根據tenant_id擷取資料源的時候,就是從這個集合中擷取。

資料源與租戶ID綁定

因為使用者發往後端的每一個HTTP請求,都對應一個使用者線程。是以,可以使用ThreadLocal存儲使用者租戶辨別tenant_id,以便在需要根據tenant_id擷取資料源的時候從ThreadLocal中取出目前操作對應的tenant_id, 即ThreadLocal.get()。

多租戶架構的動态資料源方案

在使用者登入操作中,登入成功後,将使用者的tenant_id資訊通過ThreadLocal傳遞下去:ThreadLocal.set(tenant_id), 在需要使用tenant_id的時候,都可以通過ThreadLocal.get()擷取到。

動态擷取資料源

重點來了,我們知道,對資料庫進行操作,首先要擷取資料庫連接配接。動态資料源就是要在運作時動态擷取相應的資料源。我們要在實作資料庫連接配接的地方實作自己的邏輯。需要自定義一個javax.sql.DataSource類,實作其中的getConnection()接口方法。主要就是從上面的ConcurrentHashMap取出相應的DataSource。

最後,将自定義的javax.sql.DataSource對象傳給DataSourceTransactionManager。動态資料源的主要流程就基本實作完成。概括下實作流程如下。

多租戶架構的動态資料源方案

總結

在多租戶架構的系統中,如果我們采用獨立資料庫或是獨立Schema的資料隔離方式,動态資料源将是一項重要的基礎功能。需要我們熟悉這些知識點,例如FactoryBean、InitializingBean的擴充與加載機制、ThreadLocal特性、DataSourceTransactionManager與javax.sql.DataSource的關系等等,對于了解這個動态資料源實作方案就變得容易了。

繼續閱讀