[转载]数据层的多租户浅谈

原文:http://www.ibm.com/developerworks/cn/java/j-lo-dataMultitenant/index.html

 

在上一篇“浅析多租户在 Java 平台和某些 PaaS 上的实现”中我们谈到了应用层面的多租户架构,涉及到 PaaS、JVM、OS 等,与之相应的是数据层也有多租户的支持。

数据层的多租户综述

多租户(Multi Tenancy/Tenant)是一种软件架构,其定义是:

在一台服务器上运行单个应用实例,它为多个租户提供服务。

在SaaS实施过程中,有一个显著的考量点,就是如何对应用数据进行设计,以支持多租户,而这种设计的思路,是要在数据的共享、安全隔离和性能间取得平衡。

传 统的应用,仅仅服务于单个租户,数据库多部署在企业内部网络环境,对于数据拥有者来说,这些数据是自己“私有”的,它符合自己所定义的全部安全标准。而在 云计算时代,随着应用本身被放到云端,导致数据层也经常被公开化,但租户对数据安全性的要求,并不因之下降。同时,多租户应用在租户数量增多的情况下,会 比单租户应用面临更多的性能压力。本文即对这个主题进行探讨:多租户在数据层的框架如何在共享、安全与性能间进行取舍,同时了解一下市面上一些常见的数据 厂商怎样实现这部分内容。

回页首

常见的三种模式

在 MSDN 的这篇文章 Multi-Tenant Data Architecture 中,系统的总结了数据层的三种多租户架构:

  1. 独立数据库
  2. 共享数据库、独立 Schema
  3. 共享数据库、共享 Schema、共享数据表

独立数据库是一个租户独享一个数据库实例,它提供了最强的分离度,租户的数据彼此物理不可见,备份与恢复都很灵活;共享数据库、独立 Schema 将每个租户关联到同一个数据库的不同 Schema,租户间数据彼此逻辑不可见,上层应用程序的实现和独立数据库一样简单,但备份恢复稍显复杂; 最后一种模式则是租户数据在数据表级别实现共享,它提供了最低的成本,但引入了额外的编程复杂性(程序的数据访问需要用 tenantId 来区分不同租户),备份与恢复也更复杂。这三种模式的特点可以用一张图来概括:

图 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 表结构

这是酒店客户信息表,我们仅以此表对这种模式进行说明,使用相同的表结构在 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 图

根据上图在 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

hotel_guest

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
时间: 2024-12-01 20:45:05

[转载]数据层的多租户浅谈的相关文章

Java平台数据层的多租户浅谈

在上一篇"浅析多租户在 Java 平台和某些 http://www.aliyun.com/zixun/aggregation/16038.html">PaaS 上的实现"中我们谈到了应用层面的多租户架构,涉及到 PaaS.JVM.OS 等,与之相应的是数据层也有多租户的支持. 数据层的多租户综述 多租户(Multi Tenancy/Tenant)是一种软件架构,其定义是: 在一台服务器上运行单个应用实例,它为多个租户提供服务. 在SaaS实施过程中,有一个显著的考量点,

浅谈数据中心的选址:混合跨城域网络

像亚马逊,戴尔,谷歌,微软和雅虎这些企业都将他们的大型内容数据中心设在华盛顿州.其中许多设施都位于一个名叫昆西的乡村小镇,因为该地区有水坝优势,能够提供低成本的绿色电力资源.同样,亚马逊在华盛顿的Umtilla建立了一处数据中心,以获得低成本水力发电.距离这些农村地区最近的城市西雅图和波特兰也有300公里,像其他远程数据中心一样,选址在这些地方的数据中心也面临着许多新的挑战,其中包括: 浅谈数据中心的选址:混合跨城域网络 1.增加了跨区域带宽需求 随着数据内容的快速增长,正推动许多大型企业的数据

【转载】运维角度浅谈MySQL数据库优化

 运维角度浅谈MySQL数据库优化 2015-06-02 14:22:02 标签:mysql优化   mysql分库分表分区 mysql读写分离 mysql主从复制 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://lizhenliang.blog.51cto.com/7876557/1657465   一个成熟的数据库架构并不是一开始设计就具备高可用.高伸缩等特性的,它是随着用户量的增加,基础架构才逐渐完善.这篇博文主要谈My

浅谈开源大数据平台的演变

浅谈开源大数据平台的演变 2015-04-16 腾讯大数据一说到开源大数据处理平台,就不得不说此领域的开山鼻祖Hadoop,它是GFS和MapReduce的开源实现.虽然在此之前有很多类似的分布式存储和计算平台,但真正能实现工业级应用.降低使用门槛.带动业界大规模部署的就是Hadoop.得益于MapReduce框架的易用性和容错性,以及同时包含存储系统和计算系统,使得Hadoop成为大数据处理平台的基石之一.Hadoop能够满足大部分的离线存储和离线计算需求,且性能表现不俗:小部分离线存储和计算

浅谈ASP中Request对象获取客户端数据的顺序

request|对象|客户端|数据 浅谈ASP中Request对象获取客户端数据的顺序 /**描述:在使用ASP Request对象时需要注意的小问题作者:慈勤强Email : cqq1978@yeah.net**/ 在ASP中Request对象是获取客户端提交数据的一个很重要的对象,大家对他也是非常熟悉了.虽然如此,还是经常有人问我下面的几种写法有什么不同,到底应该怎么写?strMessage = Request("msg")strMessage = Request.Form(&qu

以数据为中心的云计算——阿里云OS浅谈

文章讲的是以数据为中心的云计算--阿里云OS浅谈,云计算可以被解释为虚拟主机的租赁服务,也可以被解释为企业软件的SaaS化,又或者是一个"云机箱"之上跑许多个虚拟桌面.在阿里云看来,云计算要解决三个实质问题. 第一是大规模.这里提到的大规模不仅是超过单台PC服务器的能力,更指的是能够支撑互联网级别的数据和应用,例如个人邮箱.搜索等. 第二是低成本.低成本的一个标志在于用系统软件来解决廉价硬件在复杂条件下不可靠的问题. 第三是服务运营.这里所指的服务运营是能够通过无差别的存储计算能力来提

《Hadoop与大数据挖掘》——第一篇 基 础 篇 第1章 浅谈大数据 1.1 大数据概述

第一篇 基 础 篇 第1章 浅谈大数据 当你早上起床,拿起牙刷刷牙,你是否会想到从拿起牙刷到刷完牙的整个过程中有多少细胞参与其中?这些细胞在参与的过程中会结合周围环境(可能是宏观的天气.温度.气压等,可能是微观的分子.空气中的微生物等),由你的意识控制而产生不同的反映.如果我说结合这些所有的信息,可以预测你接下来的0.000 000 01秒的动作,那么,你肯定说,这我也可以预测呀.比如正常情况下,你脚抬起来走路,那么抬起来后,肯定是要落下去的,这算哪门子预测呢?那如果我说可以预测你接下来一个小时

浅谈大数据现状:误区严重 人才紧缺

文章讲的是浅谈大数据现状:误区严重 人才紧缺,大数据现在很热,企业.个人都在谈论,每个人对大数据有着自己不同的看法和观点,但是笔者通过日常与朋友们聊天,发现很多朋友对大数据仍然具有一些观念上的误区,比如很多人会认为大数据是一个最新的技术,还有一些朋友认为多有数据只服务自己一个人等等,这些问题我们在本期都会与大家讨论. 大数据不是一项最新技术 大数据现在已经融入我们的生活,人们在日常工作办公的时候都会接触到大数据,这些大量数据总会以不同的形式,以及庞大的数量存在和运用,这也导致了很多朋友认为大数据

JavaScript数据操作_浅谈原始值和引用值的操作本质_javascript技巧

我的一句话总结:原始值不管是变量赋值还是函数传递都不会改变原值,引用值不管是变量赋值还是函数传递,如果新变量重新赋值,则不会影响原引用值,如新变量是直接操作,就会影响原引用值. 首先明确,值和类型是两个不同的概念.例如,null是null类型的唯一值.undefined是undefined类型的唯一值.而true和false是boolean类型仅有的两个值等.在任何语言中,值的操作都可以归纳为以下3个方面. 复制值:即把值赋值给新变量,或者通过变量把值赋值给另一个变量.属性或数组元素. 传递值: