數據層的多租戶淺談

在單租戶應用向多租戶應用的轉型中,數據如何隔離,既知足數據服務的共享,又保證數據的安全性,同時性能也在合理的考量中,是一個共同的課題。同時,咱們也關注像 Hibernate、EclipseLink 等數據層的解決方案又是如何具體實現多租戶的。html

數據層的多租戶綜述

多租戶(Multi Tenancy/Tenant)是一種軟件架構,其定義是:java

在一臺服務器上運行單個應用實例,它爲多個租戶提供服務。mysql

在SaaS實施過程當中,有一個顯著的考量點,就是如何對應用數據進行設計,以支持多租戶,而這種設計的思路,是要在數據的共享、安全隔離和性能間取得平衡。web

傳統的應用,僅僅服務於單個租戶,數據庫多部署在企業內部網絡環境,對於數據擁有者來講,這些數據是本身「私有」的,它符合本身所定義的所有安全標準。而在雲計算時代,隨着應用自己被放到雲端,致使數據層也常常被公開化,但租戶對數據安全性的要求,並不因之降低。同時,多租戶應用在租戶數量增多的狀況下,會比單租戶應用面臨更多的性能壓力。本文即對這個主題進行探討:多租戶在數據層的框架如何在共享、安全與性能間進行取捨,同時瞭解一下市面上一些常見的數據廠商怎樣實現這部份內容。sql

常見的三種模式

在 MSDN 的這篇文章 Multi-Tenant Data Architecture 中,系統的總結了數據層的三種多租戶架構:數據庫

  1. 獨立數據庫
  2. 共享數據庫、獨立 Schema
  3. 共享數據庫、共享 Schema、共享數據表

獨立數據庫是一個租戶獨享一個數據庫實例,它提供了最強的分離度,租戶的數據彼此物理不可見,備份與恢復都很靈活;共享數據庫、獨立 Schema 將每一個租戶關聯到同一個數據庫的不一樣 Schema,租戶間數據彼此邏輯不可見,上層應用程序的實現和獨立數據庫同樣簡單,但備份恢復稍顯複雜; 最後一種模式則是租戶數據在數據表級別實現共享,它提供了最低的成本,但引入了額外的編程複雜性(程序的數據訪問須要用 tenantId 來區分不一樣租戶),備份與恢復也更復雜。這三種模式的特色能夠用一張圖來歸納:編程

圖 1. 三種部署模式的異同緩存

圖 1. 三種部署模式的異同

上圖所總結的是通常性的結論,而在常規場景下須要綜合考慮才能決定那種方式是合適的。例如,在佔用成本上,認爲獨立數據庫會高,共享模式較低。但若是考慮到大租戶潛在的數據擴展需求,有時也許會有相反的成本耗用結論。安全

而多租戶採用的選擇,主要是成本緣由,對於多數場景而言,共享度越高,軟硬件資源的利用效率更好,成本也更低。但同時也要解決好租戶資源共享和隔離帶來的安全與性能、擴展性等問題。畢竟,也有客戶沒法滿意於將數據與其餘租戶放在共享資源中。服務器

目前市面上各種數據廠商在多租戶的支持上,大抵都是遵循上文所述的這幾類模式,或者混合了幾種策略,這部份內容將在下面介紹。

JPA Provider

JSR 338 定義了 JPA 規範 2.1,但如咱們已經瞭解到的,Oracle 把多租戶的多數特性推遲到了 Java EE 8 中。儘管這些曾經在 JavaOne 大會中有過演示,但不管是在 JPA 2.0(JSR 317)仍是 2.1 規範中,都依然沒有明文說起多租戶。不過這並不妨礙一些 JPA provider 在這部分領域的實現,Hibernate 和 EclipseLink 已提供了所有或部分的多租戶數據層的解決方案。

Hibernate 是當今最爲流行的開源的對象關係映射(ORM)實現,並能很好地和 Spring 等框架集成,目前 Hibernate 支持多租戶的獨立數據庫和獨立 Schema 模式。EclipseLink 也是企業級數據持久層JPA標準的參考實現,對最新 JPA2.1 完整支持,在目前 JPA 標準還沒有引入多租戶概念之際,已對多租戶支持無缺,其前身是誕生已久、功能豐富的對象關係映射工具 Oracle TopLink。所以本文采用 Hibernate 和 EclipseLink 對多租戶數據層進行分析。

Hibernate

Hibernate 是一個開放源代碼的對象/關係映射框架和查詢服務。它對 JDBC 進行了輕量級的對象封裝,負責從 Java 類映射到數據庫表,並從 Java 數據類型映射到 SQL 數據類型。在 4.0 版本 Hibenate 開始支持多租戶架構——對不一樣租戶使用獨立數據庫或獨立 Sechma,並計劃在 5.0 中支持共享數據表模式。

在 Hibernate 4.0 中的多租戶模式有三種,經過 hibernate.multiTenancy 屬性有下面幾種配置:

  • NONE:非多租戶,爲默認值。
  • SCHEMA:一個租戶一個 Schema。
  • DATABASE:一個租戶一個 database。
  • DISCRIMINATOR:租戶共享數據表。計劃在 Hibernate5 中實現。

模式1:獨立數據庫

若是是獨立數據庫,每一個租戶的數據保存在物理上獨立的數據庫實例。JDBC 鏈接將指向具體的每一個數據庫,一個租戶對應一個數據庫實例。在 Hibernate 中,這種模式能夠經過實現 MultiTenantConnectionProvider 接口或繼承 AbstractMultiTenantConnectionProvider 類等方式來實現。三種模式中它的共享性最低,所以本文重點討論如下兩種模式。

模式 2:共享數據庫,獨立 Schema

對於共享數據庫,獨立 Schema。全部的租戶共享一個數據庫實例,可是他們擁有獨立的 Schema 或 Catalog,本文將以多租戶酒店管理系統爲案例說明 Hibernate 對多租戶的支持和用使用方法。

圖 2. guest 表結構

圖 2. guest 表結構

這是酒店客戶信息表,咱們僅以此表對這種模式進行說明,使用相同的表結構在 MySQL 中建立 DATABASE hotel_1 和 hotel_2。基於 Schema 的多租戶模式,須要在 Hibernate 配置文件 Hibernate.cfg.xml 中設置 hibernate.multiTenancy 等相關屬性。

清單 1. 配置文件 Hibernate.cfg.xml

<session-factory>
<property name="connection.url">
    jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&amp;characterEncoding=utf8
</property>
<property name="connection.username">root</property>
<property name="connection.password"></property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
<property name="hibernate.connection.autocommit">false</property>
<property name="hibernate.cache.use_second_level_cache">false</property>
<property name="show_sql">false</property>

<property name="hibernate.multiTenancy">SCHEMA</property>
<property name="hibernate.tenant_identifier_resolver">
     hotel.dao.hibernate.TenantIdResolver
</property>
<property name="hibernate.multi_tenant_connection_provider">
     hotel.dao.hibernate.SchemaBasedMultiTenantConnectionProvider
</property>

<mapping class="hotel.model.Guest" />
</session-factory>

<hibernate.tenant_identifier_resolver> 屬性規定了一個合約,以使 Hibernate 可以解析出應用當前的 tenantId,該類必須實現 CurrentTenantIdentifierResolver 接口,一般咱們能夠從登陸信息中得到 tenatId。

清單 2. 獲取當前 tenantId

public class TenantIdResolver implements CurrentTenantIdentifierResolver {
	public String resolveCurrentTenantIdentifier() {
		return Login.getTenantId();
	}
}

< hibernate.multi_tenant_connection_provider> 屬性指定了 ConnectionProvider,即 Hibernate 須要知道如何以租戶特有的方式獲取數據鏈接,SchemaBasedMultiTenantConnectionProvider 類實現了MultiTenantConnectionProvider 接口,根據 tenantIdentifier 得到相應的鏈接。在實際應用中,可結合使用 JNDI DataSource 技術獲取鏈接以提升性能。

清單 3. 以租戶特有的方式獲取數據庫鏈接

public class SchemaBasedMultiTenantConnectionProvider 
  implements MultiTenantConnectionProvider, Stoppable,
		Configurable, ServiceRegistryAwareService {

	private final DriverManagerConnectionProviderImpl connectionProvider 
         = new DriverManagerConnectionProviderImpl();
	@Override
	public Connection getConnection(String tenantIdentifier) throws SQLException {
		final Connection connection = connectionProvider.getConnection();
		connection.createStatement().execute("USE " + tenantIdentifier);
		
		return connection;
	}

	@Override
	public void releaseConnection(String tenantIdentifier, Connection connection)
 throws SQLException {
		connection.createStatement().execute("USE test");		
		connectionProvider.closeConnection(connection);
	}
	……	
}

與表 guest 對應的 POJO 類 Guest,其中主要是一些 getter 和 setter方法。

清單 4. POJO 類 Guest

@Table(name = "guest")
public class Guest {

	private Integer id;
	private String name;
	private String telephone;
	private String address;
	private String email;

	//getters and setters
		……	
}

咱們使用 ServiceSchemaBasedMain.java 來進行測試,並假設了一些數據以方便演示,如當有不一樣租戶的管理員登陸後分別進行添加客戶的操做。

清單 5. 測試類 ServiceSchemaBasedMain

public class ServiceSchemaBasedMain {

	public static void main(String[] args) {
		Session session = null;
		Guest guest =null;
		List<Guest> list = null;
		Transaction tx = null;

		System.out.println("======== 租戶 hotel_1 ========");
		Login.setTenantId("hotel_1");
		session = sessionFactory.openSession();
		tx = session.beginTransaction();
		guest = new Guest();
		guest.setName("張三");
		guest.setTelephone("56785678");
		guest.setAddress("上海市張揚路88號");
		guest.setEmail("zhangsan@gmail.com");
		session.saveOrUpdate(guest);
		list = session.createCriteria(Guest.class).list();
		for (Guest gue : list) {
			System.out.println(gue.toString());
		}
		tx.commit();
		session.close();
		

		System.out.println("======== 租戶 hotel_2 ========");
		Login.setTenantId("hotel_2");
		session = sessionFactory.openSession();
		tx = session.beginTransaction();
		guest = new Guest();
		guest.setName("李四");
		guest.setTelephone("23452345");
		guest.setAddress("上海市南京路100號");
		guest.setEmail("lisi@gmail.com");
		session.saveOrUpdate(guest);
		list = session.createCriteria(Guest.class).list();
		for (Guest gue : list) {
			System.out.println(gue.toString());
		}
		tx.commit();
		session.close();
	}
}

清單 6. 運行程序 ServiceSchemaBasedMain 的輸出

======== 租戶 hotel_1 ========
Guest [id=1, name=Victor, telephone=56008888, address=上海科苑路399號, email=vic@gmail.com]
Guest [id=2, name=Jacky, telephone=66668822, address=上海金科路28號, email=jacky@sina.com]
Guest [id=3, name=張三, telephone=56785678, address=上海市張揚路88號, email=zhangsan@gmail.com]
======== 租戶 hotel_2 ========
Guest [id=1, name=Anton, telephone=33355566, address=上海南京路8號, email=anton@gmail.com]
Guest [id=2, name=Gus, telephone=33355566, address=北京大道3號, email=gus@yahoo.com]
Guest [id=3, name=李四, telephone=23452345, address=上海市南京路100號, email=lisi@gmail.com]

模式3:共享數據庫、共享 Schema、共享數據表

在這種狀況下,全部租戶共享數據表存放數據,不一樣租戶的數據經過 tenant_id 鑑別器來區分。但目前的 Hibernate 4 還不支持這個多租戶鑑別器策略,要在 5.0 才支持。但咱們是否有可選的替代方案呢?答案是使用 Hibernate Filter.

爲了區分多個租戶,我在 Schema 的每一個數據表須要添加一個字段 tenant_id 以斷定數據是屬於哪一個租戶的。

圖 3. 共享 Schema、共享數據表案例 E-R 圖

圖 3. 共享Schema、共享數據表案例E-R圖

根據上圖在 MySQL 中建立 DATABASE hotel。

咱們在 OR-Mapping 配置文件中使用了 Filter,以便在進行數據查詢時,會根據 tenant_id 自動查詢出該租戶所擁有的數據。

清單 7. 對象關係映射文件 Room.hbm.xml

點擊查看代碼清單

接下來咱們在 HibernateUtil 類中經過 ThreadLocal 存放和獲取 Hibernate Session,並將用戶登陸信息中的 tenantId 設置爲 tenantFilterParam 的參數值。

清單 8. 獲取 Hibernate Session 的工具類 HibernateUtil

點擊查看代碼清單

不過 Filter 只是有助於咱們讀取數據時顯示地忽略掉 tenantId,但在進行數據插入的時候,咱們仍是不得不顯式設置相應 tenantId 才能進行持久化。這種情況只能在 Hibernate5 版本中獲得根本改變。

清單 9. 運行程序 HotelServiceMain 輸出

點擊查看代碼清單

多租戶下的 Hibernate 緩存

基於獨立 Schema 模式的多租戶實現,其數據表無需額外的 tenant_id。經過 ConnectionProvider 來取得所需的 JDBC 鏈接,對其來講一級緩存(Session 級別的緩存)是安全的可用的,一級緩存對事物級別的數據進行緩存,一旦事物結束,緩存也即失效。可是該模式下的二級緩存是不安全的,由於多個 Schema 的數據庫的主鍵可能會是同一個值,這樣就使得 Hibernate 沒法正常使用二級緩存來存放對象。例如:在 hotel_1 的 guest 表中有個 id 爲 1 的數據,同時在 hotel_2 的 guest 表中也有一個 id 爲 1 的數據。一般我會根據 id 來覆蓋類的 hashCode() 方法,這樣若是使用二級緩存,就沒法區別 hotel_1 的 guest 和 hote_2 的 guest。

在共享數據表的模式下的緩存, 能夠同時使用 Hibernate的一級緩存和二級緩存, 由於在共享的數據表中,主鍵是惟一的,數據表中的每條記錄屬於對應的租戶,在二級緩存中的對象也具備惟一性。Hibernate 分別爲 EhCache、OSCache、SwarmCache 和 JBossCache 等緩存插件提供了內置的 CacheProvider 實現,讀者能夠根據須要選擇合理的緩存,修改 Hibernate 配置文件設置並啓用它,以提升多租戶應用的性能。

EclipseLink

EclipseLink 是 Eclipse 基金會管理下的開源持久層服務項目,爲 Java 開發人員與各類數據服務(好比:數據庫、web services、對象XML映射(OXM)、企業信息系統(EIS)等)交互提供了一個可擴展框架,目前支持的持久層標準中包括:

  • Java Persistence API (JPA)
  • Java Architecture for XML Binding (JAXB)
  • Java Connector Architecture (JCA)
  • Service Data Objects (SDO)

EclipseLink 前身是 Oracle TopLink, 2007年 Oracle 將後者絕大部分捐獻給了 Eclipse 基金會,次年 EclipseLink 被 Sun 挑選成爲 JPA 2.0 的參考實現。

注: 目前 EclipseLink2.5 徹底支持 2013 年發佈的 JPA2.1(JSR 338) 。

在完整實現 JPA 標準以外,針對 SaaS 環境,在多租戶的隔離方面 EclipseLink 提供了很好的支持以及靈活地解決方案。

應用程序隔離

  • 隔離的容器/應用服務器
  • 共享容器/應用服務器的應用程序隔離
  • 同一應用程序內的共享緩存但隔離的 entity manager factory
  • 共享的 entity manager factory 但每隔離的 entity manager

數據隔離

  • 隔離的數據庫
  • 隔離的Schema/表空間
  • 隔離的表
  • 共享表但隔離的行
  • 查詢過濾
  • Oracle Virtual Private Database (VPD)

對於多租戶數據源隔離主要有如下方案

  • Single-Table Multi-tenancy,依靠租戶區分列(tenant discriminator columns)來隔離表的行,實現多租戶共享表。
  • Table-Per-Tenant Multi-tenancy,依靠表的租戶區分(table tenant discriminator)來隔離表,實現一租戶一個表,大致相似於上文的共享數據庫獨立Schema模式。
  • Virtual Private Database(VPD ) Multi-tenancy,依靠 Oracle VPD 自身的安全訪問策略(基於動態SQL where子句特性),實現多租戶共享表。

本節重點介紹多租戶在 EclipseLink 中的共享數據表和一租戶一個表的實現方法,並也以酒店多租戶應用的例子展示共享數據表方案的具體實踐。

EclipseLink Annotation @Multitenant

@Entity 或 @MappedSuperclass 一塊兒使用,代表它們在一個應用程序中被多租戶共享, 如清單 10。

清單10. @Multitenant

@Entity
@Table(name="room")
@Multitenant
...
publicclass Room {
}

表 1. Multitenant 包含兩個屬性

Annotation 屬性 描述 缺省值
boolean includeCriteria 是否將租戶限定應用到 select、update、delete 操做上 true
MultitenantType value 多租戶策略,SINGLE_TABLE,
TABLE_PER_TENANT, VPD.
SINGLE_TABLE

共享數據表(SINGLE_TABLE)

  1. Metadata配置

依靠租戶區分列修飾符 @TenantDiscriminatorColumn 實現。

清單11. @TenantDiscriminatorColumn

@Entity
@Table(name="hotel_guest")
@Multitenant(SINGLE_TABLE)
@TenantDiscriminatorColumn(name="tenant_id", contextProperty="tenant.id")
publicclass HotelGuest {
}

或者在EclipseLink描述文件orm.xml定義對象與表映射時進行限制,二者是等價的。

清單12. orm.xml

<entity class="mtsample.hotel.model.HotelGuest">
  <multitenant>
    <tenant-discriminator-column name="tenant_id" context-property="tenant.id"/>
  </multitenant>
  <table name="HotelGuest"/>
  ...
</entity>
  1. 屬性配置

租戶區分列定義好後,在運行時環境須要配置具體屬性值,以肯定當前操做環境所屬的租戶。

三種方式的屬性配置,按優先生效順序排序以下

  1. EntityManager(EM)
  2. EntityManagerFactory(EMF)
  3. Application context (when in a Java EE container)

例如 EntityManagerFactory 能夠間接經過在 persistence.xml 中配置持久化單元(Persistence Unit)或直接傳屬性參數給初始化時 EntityManagerFactory。

清單 13. 配置 persistence.xml

<persistence-unit name="multi-tenant">
  ...
  <properties>
    <property name="tenant_id" value="開發部"/>
    ...
  </properties>
</persistence-unit>

或者

清單 14. 初始化 EntityManagerFactory

點擊查看代碼清單

按共享粒度能夠做以下區分,

  • EntityManagerFactory 級別

用戶須要經過 eclipselink.session-name 提供獨立的會話名,確保每一個租戶佔有獨立的會話和緩存。

清單 15. 爲 EntityManagerFactory 配置會話名

點擊查看代碼清單

  • 共享的 EntityManagerFactory 級別

EntityManagerFactory 的默認模式, 此級別缺省配置爲獨立二級緩存(L2 cache), 即每一個 mutlitenant 實體緩存設置爲 ISOLATED,用戶也可設置 eclipselink.multitenant.tenants-share-cache 屬性爲真以共享,此時多租戶 Entity 緩存設置爲 PROTECTED。

這種級別下,一個活動的 EntityManager 不能更換 tenantId。

  • EntityManager 級別

這種級別下,共享 session,共享 L2 cache, 用戶須要本身設置緩存策略,以設置哪些租戶信息是不能在二級緩存共享的。

清單 16. 設置緩存

點擊查看代碼清單

一樣,一個活動的EntityManager不能更換tenant ID。

幾點說明:

  • 每一個表的區分列能夠有任意多個,使用修飾符 TenantDiscriminatorColumns。

清單 17. 多個分區列

@TenantDiscriminatorColumns({
@TenantDiscriminatorColumn(name="tenant_id", contextProperty="tenant.id"),
@TenantDiscriminatorColumn(name = "guest_id", contextProperty="guest.id")
})
  • 租戶區分列的名字和對應的上下文屬性名能夠取任意值,由應用程序開發者設定。
  • 生成的 Schema 能夠也能夠不包含租戶區分列,如 tenant_id 或 guest_id。
  • 租戶區分列能夠映射到實體對象也能夠不。

注意:當映射的時候,實體對象相應的屬性必須標記爲只讀(insertable=false, updatable=false),這種限制使得區分列不能做爲實體表的 identifier。

  • TenantDiscriminatorColumn被如下 EntityManager 的操做和查詢支持:

persist,find,refresh,named queries,update all,delete all 。

一租戶一表(TABLE_PER_TENANT )

這種多租戶類型使每一個租戶的數據能夠佔據專屬它本身的一個或多個表,多租戶間的這些表能夠共享相同 Schema 也可以使用不一樣的,前者使用前綴(prefix)或後綴(suffix)命名模式的表的租戶區分符,後者使用租戶專屬的 Schema 名來定義表的租戶區分符。

  1. Metadata配置

依靠數據表的租戶區分修飾符 @TenantTableDiscriminator 實現

清單 18.

@Entity
@Table(name=「CAR」)
@Multitenant(TABLE_PER_TENANT)
@TenantTableDiscriminator(type=SCHEMA, contextProperty="eclipselink-tenant.id")
public class Car{
}

清單 19.

<entity class="Car">
  <multitenant type="TABLE_PER_TENANT">
    <tenant-table-discriminator type="SCHEMA" context-property="eclipselink.tenant-id"/>
  </multitenant>
  <table name="CAR">
</entity>

如前所述,TenantTableDiscriminatorType有 3 種類型:SCHEMA、SUFFIX 和 PREFIX。

  1. 屬性配置

與另外兩種多租戶類型同樣,默認狀況下,多租戶共享EMF,如不想共享 EMF,能夠經過配置 PersistenceUnitProperties.MULTITENANT_SHARED_EMF 以及 PersistenceUnitProperties.SESSION_NAME 實現。

清單 20.

點擊查看代碼清單

或在 persistence.xml 配置屬性。

酒店多租戶應用實例(EclipseLink 共享(單)表)

數據庫 Schema 和測試數據與上文 Hibernate 實現相同,關於對象關係映射(OR mapping)的配置均採用 JPA 和 EclipseLink 定義的 Java Annotation 描述。

關於幾個基本操做

  1. 添加一個對象實例, 利用EntityManager.persist()

清單 21. 添加

public <T> void save(T t) {
		em.persist(t);
	}
	public <T> void saveBulk(List<T> bulk) {
		for(T t:bulk){
			em.persist(t);
		}
	}
  1. 更新一個對象實例, 利用EntityManager.merge()

清單 22. 更新

public <T> void update(T t){
		em.merge(t);
	}
  1. 查詢, 利用EntityManager的NamedQuery,

清單 23. 多條件多結果查詢

點擊查看代碼清單

若用 JPQL 實現則示例以下:

清單 24. JPQL NamedQuery 定義

點擊查看代碼清單

部分測試數據以下(MySQL):

hotel_admin

Fig 4. hotel_admin

hotel_guest

Fig 5. hotel_guest

room

Fig 6. room

運行附件 MT_Test_Hotels.zip 中的測試代碼(請參照 readme)來看看多租戶的一些典型場景。

清單 25. 運行測試代碼

點擊查看代碼清單

能獲得輸出片斷以下:

清單 26. 輸出

點擊查看代碼清單

經過共享表的測試數據以及運行結果能夠看到,對於多個不一樣的租戶(hotel_admin),在添加、查找、更新操做沒有顯示聲明租戶標識的狀況下,EntityManager 能夠根據自身的租戶屬性配置

實現租戶分離。在本實例,EntityManager 初始化時利用到 hotel_admin 登陸後的會話上下文進行租戶判斷,這裏再也不贅述。

注:上文中說起的所有源碼均可以在附件中找到。

其它方面的考慮

數據備份

獨立數據庫和獨立Sechma的模式,爲每一個租戶備份數據比較容易,由於他們存放在不一樣的數據表中,只需對整個數據庫或整個Schema進行備份。

在共享數據表的模式下,能夠將全部租戶的數據一塊兒備份,可是若要爲某一個租戶或按租戶分開進行數據備份,就會比較麻煩。一般須要另外寫sql腳本根據tenant_id來取得對應的數據而後再備份,可是要按租戶來導入的話依然比較麻煩,因此必要時仍是須要備份全部併爲之後導入方便。

性能

獨立數據庫:性能高,但價格也高,須要佔用資源多,不能共享,性價比低。

共享數據庫,獨立 Schema:性能中等,但價格合適,部分共享,性價比中等。

共享數據庫,共享 Schema,共享數據表:性能中等(可利用 Cache 能夠提升性能),但價格便宜,徹底共享,性價比高。若是在某些表中有大量的數據,可能會對全部租戶產生性能影響。

對於共享數據庫的狀況下,若是由於太多的最終用戶同時訪問數據庫而致使應用程序性能問題,能夠考慮數據表分區等數據庫端的優化方案。

經濟考慮

爲了支持多租戶應用,共享模式的應用程序每每比使用獨立數據庫模式的應用程序相對複雜,由於開發一個共享的架構,致使在應用設計上得花較大的努力,於是初始成本會較高。然而,共享模式的應用在運營成本上每每要低一些,每一個租戶所花的費用也會比較低。

結束語

多租戶數據層方案的選擇是一個綜合的考量過程,包括成本、數據隔離與保護、維護、容災、性能等。但不管怎樣選擇,OR-Mapping 框架對多租戶的支持將極大的解放開發人員的工做,從而能夠更多專一於應用邏輯。最後咱們以一個 Hibernate 和 EclipseLink 的比較來結束本文。

表 2. Hibernate 與 EclipeLink 對多租戶支持的比較

  Hibernate EclipseLink
獨立數據庫 支持,經過實現 MultiTenantConnectionProvider 接口以鏈接獨立的數據庫 支持,爲每一個租戶配置獨立的 EntityManagerFactory
共享數據庫,獨立 Schema 支持,經過實現 MultiTenantConnectionProvider 接口以切換 Schema 支持,使用 TABLE_PER_TENANT MultitenantType 以及 SCHEMA TenantTableDiscriminatorType
共享數據庫,共享 Schema,共享數據表 多租戶 Discriminator 計劃在 Hibernate 5.0 支持 支持, 使用 SINGLE_TABLE MultitenantType 以及 TenantDiscriminatorColumn

下載

描述 名字 大小
代碼示例 Multi-tenancy-attachment.zip 272k

參考資料

學習

相關文章
相關標籤/搜索