LAYER:一种在云中支持多租户数据库即服务的高性价比机制
1. 引言
互联网推动并普及了软件即服务(SaaS),在这种模式下,应用程序的所有权和运营被外包给服务提供商。SaaS服务提供商在其基础设施上托管应用程序,并将这些应用程序作为服务提供给不同的企业,使这些企业无需自行部署应用程序。被称为租户的企业订阅托管服务,并通过网页浏览器或客户端程序访问这些服务。国际数据公司(IDC)报告称,与 SaaS相关的全球收入在2011年达到229亿美元,预计到2016年将达到673亿美元,复合年增长率(CAGR)为24%(P. Robert 和 G. Connor, 2012)。
SaaS 模式也已被应用于多租户数据库系统(Hacigumus 等,2002; C. 温斯曼 和 S. 博布罗夫斯基 ,2009;Salesforce,2014),这种模式被称为Multi-Tenant DataBase as a Service(MTDBaaS)。在 MTDBaaS 中,租户摆脱了拥有和维护数据库系统的高昂成本,因为数据库系统的所有权和运营被外包给了 MTDBaaS 服务提供商。租户可以将其数据存储在 MTDBaaS 的服务器端,并通过浏览器或服务客户端程序查询或更新数据。
基本上,存在三种实现MTDBaaS的方案(D. Jacobs 和 S. Aulbach,2007)。第一种方案是Shared-machine,它通过在虚拟机中或直接运行多个独立的数据库管理系统实例来为不同的租户提供服务。每个租户使用一个专用数据库实例来存储数据并响应查询。第二种方案是Shared-process(C. Curino 等人,2011;C. Curino 等人,2011),该方案在同一个数据库实例中为每个租户逻辑表创建一个物理表。第三种方案是Shared-table(S. Aulbach 等人,2008;M. Hui 等人, 2009),该方案让所有租户共享少量公共的基础表和扩展表。元组所有租户的数据都存储在这些共享表中,且所有查询都需要重新构建,以便租户提交的查询仅在其所属的元组上执行,而不会访问其他租户的元组。
MTDBaaS 的主要关注点是系统可扩展性,即在可接受的性能下服务越来越多租户的能力,而 MTDBaaS 技术的总体目标是通过增强租户整合来获得良好的系统可扩展性,从而实现更高的资源利用率和更低的运营成本。Jacobs 和 Aulbach(D. 雅各布斯 和 S. 奥尔巴赫,2007)指出, Shared-machine 方案只能实现有限且较低的整合,因为内存和CPU资源被冗余的数据库进程所消耗,该方案仅在数据库之间的硬隔离比整体性能更为重要时适用。
对于Shared-process方案,尽管每个租户消耗的内存较少,但与 Shared-machine方案相比,随着租户数量的增加,内存消耗增长较快,因为它为每个租户逻辑表创建一个私有表,从而使每个租户获得一个专用模式实例。先前的一项研究(S. Aulbach 等人,2008)观察到,当表数量超过 50,000 时,数据库服务器出现显著的性能下降。实际上,与系统中注册的租户总数相比,MTDBaaS 的同时活跃租户数量通常较小。因此,大量内存被用于维护大量非活跃租户的海量表,这不可避免地限制了可整合到同一数据库管理系统实例中的租户总数。此外,随着时间推移,每个租户可能在系统中创建了多个表,而只有最近创建的表才会被查询。这些 “空闲”表将占用大量系统资源,从而进一步降低系统效率和可扩展性。
尽管共享表方案的内存消耗随着租户数量的增加而保持不变,并且先前的性能测试(Z. Wang 等,2008)表明,对于大量小型租户,该方案比共享进程方案具有更好的缓冲池利用率,但它仅适用于租户共享具有有限可扩展性的相似模式的应用场景,例如客户关系管理(Salesforce, 2014)、电子邮件(Zimbra,2014)和电子商务(GoECart,2014)应用。对于租户需要拥有自己特定模式且较少这些模式中可以提取出公共表结构,Shared-process方案似乎是唯一可行的选择。然而,Shared-process方案在为大量具有不同数据库模式的小型租户提供服务时存在限制。
根据80/20法则,80%的工作负载可能由20%的租户产生,也就是说,我们可以预期大多数工作负载是由少数最活跃的租户产生的。因此,我们提出了一种新颖的机制,即LAYER(Load As You quERy的缩写),以克服现有MTDBaaS解决方案中的上述问题,并以成本效益的方式支持多租户数据库应用来服务海量租户。LAYER将大量具有较低租户活跃度的小型租户整合到一个DBMS中,特别是当某个租户倾向于访问已创建的大量表中的一小部分时。该机制采用共享表来存储所有租户的数据,同时仅维护适量的相关表作为工作表以响应租户查询。如果某个查询涉及的表在 DBMS中不存在,LAYER会首先将其恢复到DBMS中,然后基于恢复的表回答查询。当一个活跃租户变为非活跃状态时,属于该租户的恢复表将从 DBMS中删除,系统资源将重新分配给活跃租户。
LAYER的一个实际应用场景是Access 365,这是微软作为其著名的云服务产品Office 365的一部分提供的。习惯使用Access管理小型结构化数据集的用户可能是Access 365的潜在租户。在Access 365中,租户可以在云中定义各种数据库模式,并通过互联网几乎在任何地方访问其数据。Access 365的云服务器必须处理由不同租户定义的各种模式。由于用户倾向于随机访问Access 365,同时活跃的租户数量应远小于注册租户总数。
LAYER的另一个可能应用场景是数据市场,其中数据以服务形式提供,租户向数据市场提交对感兴趣数据集的查询,并根据数据市场返回的结果付费。数据市场的租户通常会在结果返回后立即进行分析,很少考虑将这些数据用于未来的分析。然而,数据市场的服务提供商可能会考虑保留从数据市场返回的查询结果,以防租户再次访问这些数据。因此,需要大量表来存储查询结果,但其中仅有适量的表会同时被再次查询。我们的机制能够以低成本支持此类应用。
为了展示所提出的LAYER机制的可行性,我们提供了两种实现:一种是基于MySQL(一种流行的基于磁盘的关系型数据库管理系统)的 LAYER-MYSQL;另一种是基于VoltDB(一种内存关系型数据库管理系统)的LAYER-VoltDB。LAYER-MYSQL能够实现高整合度和可接受的性能,而 LAYER-VoltDB则能提供更高的性能。在合成数据上的大量实验验证了所提 出机制的有效性和效率。
在本文的其余部分,我们首先在第2节介绍LAYER的设计,然后在第 3节通过两种不同的方法展示LAYER的两个系统实现。在第4节,我们提 供并分析实验结果,以展示这两种方法的有效性和效率。我们在第5节调 研相关工作,并在第6节对本文进行总结。
2. LAYER的设计
2.1. 机制概述
LAYER 以随查随加载 的方式运行。它不会为租户创建的每个逻辑表在数据库管理系统中维护一个独立的表,而仅维护正在被查询或将要被查询的表。当查询访问的表在数据库管理系统中不存在时,系统会先创建该表,并将数据加载到新创建的表中,然后在该新创建的表上执行查询。当数据库管理系统中现有表的总数超过某个预配置数量时,LAYER 还会删除“空闲”表,以便释放这些“空闲”表占用的资源,并将其重新分配给其他活跃表的查询处理。当一个活跃租户变为非活跃状态时,该租户的表可以被删除,分配给这些表的资源也可以被回收,且所有被选中删除的表中的更新行都将被备份。通过这种整合方式,我们无需在数据库管理系统中为每个逻辑租户表保留一个私有表,而只需在数据库管理系统中维护较少数量的工作表,这与共享进程方案不同,在共享进程方案中,每个逻辑租户表都维护有一个私有表。
2.2. 模式设计
我们设计模式以支持LAYER机制。这些模式由两部分组成:共享表模式和工作表模式。在LAYER系统中,共享表模式下有一张共享表,用于存储从所有租户整合的数据。为简便起见,后续我们将该共享表称为共享表。此外,在工作表下存在多个工作表,这些工作表被创建用于存储为活跃租户的查询提供答案所需的数据。共享表存储所有租户的数据,每个租户逻辑表的每一行对应其中的一行,而每个工作表则对应一个已恢复的租户逻辑表。属于租户逻辑表的每一行的所有列都被拼接后存储在数据列中,位于共享表内。我们通过从数据列(位于共享表中)提取属于该表每一行的所有列来恢复一张表。
每个工作表或已恢复的表都有两个额外的列:Row列和 Update列。Row列用于标识逻辑租户表中每一行的ID,以便在需要将行的更新刷新到共享表时,能够定位到共享表中的对应行。 Update列用于标识自表恢复以来逻辑租户表中哪些行已被更新。当表最初被恢复时,所有 Update列的值都设置为0。如果某行被更新或插入,则其 Update列的值设为1;如果某行被删除,则其 Update列的值设为2。因此,当DBMS中的一个工作表需要被删除时, Update值为1的行将被更新到共享表, Update值为2的行将从共享表中删除,而 Update值为0的行将保持不变。
2.3. 表恢复
一个租户可能创建了数十或数百个表,但通常只会查询其中的部分表,可能是最近创建的那些表。因此,我们仅恢复与租户查询相关的那些表,与最近查询无关的其他表未被恢复。由于我们允许租户在表之间定义表引用约束,因此为每个租户维护一个有向无环图(DAG),以描述该租户定义的引用约束,如图2所示。每个表对应于有向无环图(DAG)中的一个节点,两个节点之间的有向边表示一个表引用约束,其中被指向的节点为被引用的表。
恢复表的整个过程在算法1中概述。在该算法中,从第6行到第11行,我们检查租户的有向无环图中被已恢复表直接或间接引用的所有表,这些表都将被恢复,因为这些表之间的引用约束可能会被违反否则,从第12行到第16行,我们将逐个恢复所有这些相关表。以图2为例,如果要恢复T1 表4 ,则也会恢复T1 表6、T1 表3、T1 表1 和T1 表2 ,因为 T1 表6 和T1 表3 被T1 表4直接引用,而T1 表1 和T1 表2被间接引用自T1 表 4。
算法1 恢复被引用的表。
1: input originalTableNode: n‐ 表示原始待处理的节点 已恢复的表
变量:
2: graph: G ‐ 表示表引用约束的有向无环图 由租户
3: nodeQueue: Q ‐ 一个表示被引用表的节点队列 表尚未计算
4: nodeStack: S ‐ 一个表示被引用表的节点栈 表已被计算
5: nodeList: L ‐ 一个表示已恢复表的节点列表
6:Q.add(n)
7: while Q 不为空
8: n节点 q =Q.remove()
9: S.push(nq)
10: for each出边的终点节点 no of outgoing edges of nq in G
11: Q.add(no)
12: while S 不为空
13:节点 p=S.pop()
14:如果 !L.contains(p)
15:由节点 p 表示的恢复表 T
16: L.add(p)
2.4. 选择要删除的表
在 LAYER 中,我们仅根据可用的系统资源(内存)维护可配置数量的工作表,以避免数据库管理系统因维护过多工作表而过载。因此,当数据库管理系统中已恢复的工作表总数超过预配置数量时,应删除一些“空闲”表。我们提出了一种称为Least-Restoration-Cost-First(LRCF)的策略,用于选择要删除的表。
LRCF策略不仅考虑了表的访问信息,还考虑了恢复表的成本。每个表都关联有一个访问计数器,每当该表被查询时,其计数器值增加1。维护两个最近最少使用(LRU)列表以记录所有工作表的访问信息。其中一个 LRU列表用于记录最近频繁访问的表,称为FAT列表,另一个列表则用于记录其余的工作表,称为RAT列表。RAT列表中的表比FAT列表中的表更早被删除。设置一个可配置的百分比参数 sper,用于确定FAT列表的长度,从而也确定了RAT列表的长度。 sper的值表示存储在FAT列表中的表占所有工作表的百分比。因此,百分比参数 sper越大,FAT列表就越长,而RAT列表就越短。
我们维护一个名为MAC的变量,用于记录在FAT列表中所有表的访问计数器中的最小值。当一张表最初被恢复时,该表会被添加到RAT列表中,并且其访问计数器被设置为0。随着该表被查询,其访问计数器会增加,每次访问该表时,我们将其访问计数器与MAC进行比较。如果其访问计数器大于MAC,则该表将从RAT列表中移除,并加入到FAT列表中。如果FAT列表达到预设长度,则从FAT列表中移除最近最少访问的表,并将其加入到RAT列表中,同时相应地更新MAC的值,以确保MAC仍然记录FAT列表中所有剩余表的最小访问计数器值。通过分别维护FAT和 RAT列表,我们可以保证最近频繁访问的表不会比最近较少访问的表更早被删除。
如果所有工作表的数量超过配置的数值,则应从RAT列表中删除一个表。我们还使用另一个可配置的百分比参数 rper。我们检查RAT列表中后 rper%的表,并选择其中恢复成本最低的表作为待删除的表。目前我们在评估表的恢复成本时仅考虑表大小。表的大小越大,恢复该表的成本就越高。实际上,行长度也应被考虑在内,因为两个大小相似但行长度不同的表,其恢复成本存在显著差异。
2.5. 列的添加和删除
租户可以向其创建的表中添加/删除列,因此我们需要处理列的增删。我们维护一个表来记录每个逻辑租户表的所有列信息,该表称为Columns Table。我们利用 Columns Table 中记录的信息来重构表结构,并在处理租户请求时控制数据访问。
如果租户想要修改的表尚未恢复,则我们只需更新列表以标记哪些列被添加或删除,而共享表保持不变。新列将追加到现有列(包括已删除的列)之后,因此共享表中的数据实际上不受影响。在恢复表时,我们将使用所有列(包括已删除和新添加的列)创建表。列按照其在列表中的ID进行组织。一旦表被创建,属于该表的所有数据都将从共享表中选择并加载到表中,并且每行的各个部分将正确插入到对应的列中,因为在添加或删除列时所有列的顺序并未改变。
如果租户想要修改已恢复的表,我们必须同时更新列表和已恢复的表。新添加的列将追加到已恢复表的末尾,而已删除的列仅被标记为“已删除”,且无法对已删除的列执行任何查询。通过在现有列之后追加新添加的列并对已删除的列进行标记,我们能够保持数据列中的字符串与已恢复表的列之间的正确映射关系。当然,如果从一个表中删除了过多的列,仅将这些列标记为“已删除”可能并不高效,因为在恢复表时仍需要加载已删除列的数据,这将产生不必要的中央处理器和I/O开销。该问题通过在系统空闲或负载较低时定期重组共享表以移除已删除的数据来解决。
3. LAYER的实现
在本节中,我们通过两种不同的方法展示了LAYER的两个实现。第一个实现基于基于磁盘的MySQL数据库管理系统([MySQL, 2014),称为 LAYER-MYSQL;第二个实现基于内存中的VoltDB数据库管理系统( VoltDB, 2014),命名为LAYER-VoltDB。LAYER-MYSQL和 LAYER-VoltDB具有不同的应用场景,这也是我们采用这两种不同方法实现LAYER的原因。由于大多数现有的多租户数据库即服务解决方案都基于传统的关系型数据库管理系统,因此我们在传统关系型数据库管理系统上实现LAYER是自然而然的选择。LAYER-MYSQL可以实现高整合度和可接受的性能,因此适用于数据库服务器内存资源不充足且高吞吐量并非首要需求的场景。而LAYER-VoltDB能够提供高性能,因此适用于偏好高吞吐量且数据库服务器配置有充足内存资源的场景。
3.1. LAYER-MySQL
LAYER-MYSQL 以集中式方式在 MYSQL 上实现,租户数据存储和查询都在同一个 MYSQL 实例内进行。通过服务客户端程序,租户可以登录系统并提交创建/删除表、建立表之间的引用约束以及查询/更新数据等请求。LAYER-MYSQL 的主要组件包括: 1. 一个认证模块,用于在租户登录系统时执行认证。2. 一个查询接收器, 用于接收租户提交的查询并执行简单的有效性检查,主要检查是否查询中包含的表或列是否确实存在于该租户中。如果请求未通过有效性检查,则不会被后续组件处理。只有通过有效性检查的请求才能提交给数据库管理系统进行进一步执行,从而使数据库管理系统能够专注于处理真正需要执行的查询。
-
一个查询重写器 ,用于重写租户发出的查询,使得重写后的查询可以在相应的工作表和适当的列上执行。重新创建的工作表可能包含一些辅助列,而这些辅助列并不存在于租户最初创建的逻辑表中。
-
一个负载请求生成器 ,当查询需要访问的表尚未创建时,该生成器会生成一个数据加载请求,并将其放入 待加载请求队列 中。
-
一个加载调度器 ,用于处理将数据加载到重新存储的表中的请求。它还负责根据系统调度策略在系统资源稀缺时删除“空闲”表。过多的并发数据加载会降低整体数据库性能,从而导致其他普通查询出现高延迟响应。因此,加载调度器必须控制并发数据加载进程的数量,以确保仅适度数量的数据加载进程并发执行。
-
一个查询处理器,调用JDBC驱动程序执行重写的查询并生成查询结果。如果查询中涉及的所有表都存在于数据库管理系统中,则该查询直接从查询重写器发送到查询处理器,因为无需进行表的恢复。否则,如果查询中涉及的某些表在数据库管理系统中不存在,则会发出一个加载请求,以便加载调度器可以加载这些不存在的表。在这些不存在的表已恢复后,查询处理器继续向数据库管理系统提交该查询。
-
一个结果传输器 ,用于将查询结果返回给客户端。
由于LAYER-MYSQL是一个集中式系统,其中数据存储和查询处理都在同一个MySQL数据库管理系统实例中完成。也就是说,租户的数据首先被存储到一个数据库管理系统实例中,随后该租户所查询的表通过重新收集已存储在同一数据库管理系统实例中的数据来恢复。随着越来越多属于不同租户的数据被整合到同一个数据库管理系统实例中,如果并发活跃租户的数量超过了数据库服务器可承载的能力,数据库服务器可能会因需要响应过多的并发查询而过载。因此,我们应该将部分租户从过载的服务器迁移到仍能容纳更多租户的其他服务器上。尽管存在一些在不同数据库管理系统实例之间迁移数据的方法,但数据迁移会给源实例和目标实例带来巨大的中央处理器、网络和磁盘开销,因此使用LAYER-MYSQL实现负载均衡非常困难。
除了负载均衡之外,LAYER-MYSQL 很难保证容错。为了确保数据库服务器的故障不会导致该服务器上所有租户的服务不可用,我们必须在多个服务器之间进行数据复制。然而,通过数据复制,很难同时保证数据一致性和高性能。因此,我们需要一种分布式实现的 LAYER,将数据存储与查询处理分离,这样可以通过从底层分布式存储系统收集数据,将表恢复到任意数据库服务器中,从而轻松实现负载均衡和容错。LAYER-VoltDB 就是以这种分布式方式实现的。
3.2. LAYER-VoltDB
LAYER-VoltDB 将数据存储与查询处理分离。LAYER-VoltDB 不再将所有租户的数据存储在数据库管理系统实例中的共享表中,而是将所有租户的数据存储在 HBase 分布式键值存储中,该存储基于分布式文件系统(即 Hadoop 文件系统(HDFS)(K. Shvachko 等,2010))实现,其中数据块在多个存储节点之间自动复制。因此,HBase 本身具备容错能力,从而能够保证 LAYER-VoltDB 的高可用性。
在LAYER-VoltDB中,每个HBase集群节点都可以作为L AYER-VoltDB的从节点,运行一个VoltDB实例来处理租户查询,同时它也充当HBase节点。因此,我们在HBase之上部署了多个内存中的VoltDB实例,并且所有这些实例相互独立,从而一个实例的故障不会影响其他实例。
我们创建一个共享键值表,用于在HBase中存储所有租户的数据。HBase的另一个重要特性是,HBase表中的键值对按顺序排列,使得具有相似键值的键值对被连续存储。我们通过组合三个部分来构建行记录的键: 租户ID、表名和行ID,从而确保同一租户的同一张表的数据在HBase中连续存储。因此,当表被恢复时,可以高效地将这些数据收集并加载到 VoltDB中新创建的表中。该表可以在分布式系统中的任意服务器上恢复,从而实现简便的负载均衡以及自动容错和高可用性。当越来越多的租户变得活跃,而某些服务器不再为任何租户提供服务以避免性能下降时,这些表可以迁移到分布式系统中的其他服务器。数据可以直接从分布式存储系统加载到新创建的内存表中,这与LAYER-MYSQL不同。在 LAYER-MYSQL中,数据存储在MySQL实例的共享表中,我们需要首先从共享表中收集逻辑表的行,然后将数据加载到新创建的表中,而数据的收集和加载过程均涉及数据库管理系统操作,并会干扰正常的查询处理。而在LAYER-VoltDB中,收集数据的I/O操作分布在多个服务器上,且数据加载仅涉及内存数据库管理系统的操作,因此可以避免对正常查询处理的干扰。
4. 实验评估
4.1. 实验设置
在本节中,我们展示了实验结果,以证明LAYER的有效性。我们部署了 LAYER-MYSQL和LAYER-VoltDB在一个由8台服务器组成的集群上,这些服务器通过1000M以太网交换机连接,每台服务器配备一个四核2.13GHz英特尔至强CPU、4GB内存和一块7200转SCSI硬盘。LAYER-MYSQL作为LAYER的集中式实现,仅部署在一个集群节点上;而LAYER-VoltDB作为LAYER的分布式实现,部署时将一个集群节点设为主节点,其余七个集群节点作为客户端节点。我们为VoltDB数据库管理系统配置了2GB内存,并在每个LAYER-VoltDB客户端节点上让HBase与操作系统共享剩余的2GB内存。我们使用自行开发的工作负载生成程序生成租户,每个租户包含5MB数据,数据由来自 TPC-C(TPCC, 2014)的相同四张表组成,并为活跃租户随机生成三种类型的查询:简单查询(从单个表中选择条目)、分析查询(跨多个表并带有连接的查询)以及更新查询。实验结果基于单个服务器进行测量并报告,因为集群中的所有服务器均相同且相互独立。
LAYER-N表示在LAYER-MYSQL中每分钟有 N个活跃租户变为非活跃租户,同时有 N个非活跃租户变为活跃租户。
4.2. LAYER-MySQL的实验结果
在评估LAYER-MYSQL时,我们将10,000个租户整合到一个MySQL服务器中,并考虑了三种不同配置的租户活跃度:在10,000个租户中,分别有500、1,000和2,000个租户并发地活跃。我们还实现了共享进程方案用于对比。不同租户活跃度下的吞吐量如图6所示。可以看出,当500个租户同时活跃时,LAYER-MYSQL的三个测试用例均获得了比共享进程方法更高的吞吐量。这是因为 LAYER-MYSQL仅有2,000个工作表,所有查询均可通过访问内存中的数据来响应,而共享进程有40,000个工作表,可能占用大量内存,且只有一部分访问的数据可以从内存中提供服务。在LAYER-MYSQL中,当有500 个活跃租户时,动态租户数量对吞吐量影响较小,因为普通查询的所有数据均由内存提供服务时,磁盘I/O压力适中。而对于1,000和2,000个租户同时活跃的情况,由于普通查询的数据仅有一部分可从内存中提供服务,而恢复的表的数据加载进一步加剧了磁盘I/O压力,导致磁盘I/O压力变得更重。因此,每分钟变为活跃状态的租户数量越大,对吞吐量的负面影响也越大。随着每分钟变为活跃状态的租户数量增加,LAYER-MYSQL的吞吐量逐渐低于共享进程方法;而当每分钟仅有5或10个租户变为活跃时, LAYER-MYSQL实现了更高的吞吐量。
我们发现的一个令人惊讶的事实是,当每分钟有10个租户变为活跃时, LAYER-MYSQL的吞吐量并不总是高于或低于共享进程方法的吞吐量。如我们所见,在拥有2,000个活跃租户的情况下,LAYER-MYSQL获得了比共享进程更高的吞吐量;而在拥有1,000个活跃租户时,LAYER-MYSQL的吞吐量却较低。一种可能的解释是,当并非所有查询都能通过内存中的数据得到服务时,活跃租户总数的增加对共享进程吞吐量的影响大于对LAYER-MYSQL的影响。当并发活跃租户数量为1,000时,共享进程获得了较高的吞吐量,而每分钟仅有10个租户变为活跃的 LAYER-MYSQL只能获得相对较低的吞吐量。当并发活跃租户数量增加到 2,000时,共享进程的吞吐量大幅下降,而每分钟有10个租户变为活跃的 LAYER-MYSQL则获得了相对较高的吞吐量。
混合查询类型下LAYER-MYSQL的查询延迟结果如图7和图8所示。我们可以看到,当仅有500个租户时,LAYER-MYSQL与共享进程 的查询延迟非常接近。
当仅有500个租户时,几乎所有普通查询的数据都从内存中提供服务,每分钟变为活跃/非活跃状态的租户数量对查询延迟影响很小。然而,当有 2,000 个租户并发活跃时,LAYER-MYSQL 和 Sharedprocess 的查询延迟表现出明显差异。在 LAYER-MYSQL 中,每分钟变为活跃状态的租户数量对简单查询和更新的延迟影响较小,但对分析查询的延迟有明显影响,这是因为分析查询在执行聚合操作时,需要更多的内存和磁盘带宽。无论如何,由 LAYER-MYSQL回答的查询明显比共享进程具有更低的延迟。
LAYER-MYSQL和共享进程都能在1秒内回答90%的分析查询,而5%的分析查询在共享进程 中的延迟超过6秒,而在LAYER-MYSQL中,5%的分析查询延迟超过3秒。对于简单查询和更新,LAYER-MYSQL能比共享进程更快地回答查询。这是因为维护4万个表消耗了大量的内存,从而留给查询处理的内存更少,而LAYER-MYSQL仅维护由活跃租户查询的表,因此有更多的内存可用于查询处理,导致响应时间更短。
我们还评估了LAYER-MYSQL在处理相同类型查询时的性能,其中 2,000个租户并发活跃,每种类型查询的结果如图9和图10所示。可以看出,即使LAYER-MYSQL每分钟有20个租户变为活跃状态,其仍能为每种类型的查询提供比共享进程更好的延迟性能。85%由共享进程处理的查询表现出与LAYER-MYSQL相似的延迟,而其余15%的查询则由于共享进程经历严重的内存争用(尤其是分析查询)而出现高得多的延迟。
4.3. LAYER-VoltDB的实验结果
关于LAYER-VoltDB,我们主要评估了数据加载过程以及处理租户查询的过程。为了评估数据加载的性能,我们手动将不同数量的TPCC数据集从HBase加载到VoltDB实例中。实验结果如图11所示。我们可以看到,将数据加载到VoltDB非常快,而LAYER-MYSQL的数据加载速度较慢。平均只需1秒即可将50MB数据加载到VoltDB中,而LAYER-MYSQL加载同样数量的数据需要3秒。将数据加载到LAYER-VoltDB的主要开销是从 HDFS获取数据,其中数据可能来自远程节点,而非负责将数据加载到自身VoltDB实例的本地节点。由于我们预计租户一次查询的数据集小于 100MB,因此租户几乎不会察觉其数据实际上存储在HBase中而不是数据库管理系统中,也不会察觉其数据是在运行时动态加载到数据库管理系统中的。
从图12可以看出,LAYER-VoltDB的吞吐量比Sharedprocess高出几十倍。LAYER-VoltDB之所以提供更高的吞吐量,是因为VoltDB采用了内存设计和实现,这与传统的基于磁盘的数据库管理系统有很大不同。当租户数量超过600时,未给出LAYER-VoltDB的结果,原因是每个节点仅有4GB内存,无法在VoltDB实例中容纳更多租户的数据。LAYER-VoltDB的查询延迟结果如图13所示。可以看出,当仅有500个租户处于活跃状态时, LAYER-VoltDB与Sharedprocess提供的查询延迟非常接近,因为普通查询的几乎所有数据都从内存中获取。当每分钟激活的租户不超过10个时, LAYER-VoltDB对于三种类型的查询仍能提供比Shared process更低的查询延迟。然而,当每分钟有20个租户变为活跃状态时,LAYER-VoltDB的查询延迟会变大,因为LAYER-VoltDB需要每分钟分配更多资源来加载这20个租户的数据,导致普通查询经历更大的延迟。
我们还通过测量LAYER和共享进程方案所能支持的租户表数量,比较了二者的可扩展性。共享进程最多可支持约300,000个空租户表,而 LAYER所能支持的空租户表的最大数量实际上没有限制,只要我们有足够的磁盘空间来存储与租户表相关的文件即可。当然,随着越来越多的租户被整合到LAYER中,同时活跃的租户数量将增加。总之,LAYER 可以提供比 Shared process 方案更高的整合能力。
5. 相关工作
大量研究努力致力于提高多租户环境中数据库工作负载整合的效率。王志等(2008)和 S. 阿尔巴赫等(2009)进行了广泛的实验,以比较多租户数据库系统的不同实现。
使用虚拟机进行数据库整合可以提供良好的性能和数据隔离,用于为关键任务业务应用程序保持严格的服务等级目标,或满足其他要求(例如安全方面的要求)。出于安全原因,需要采用基于虚拟机(VM)的方法。G. Soundararajan 等人 (2009) 和 A. Soror 等人 (2010) 探讨了如何划分系统资源,以正确配置虚拟机来支持多个数据库工作负载。Z. Shen 等人 (2011) 和 P. Xiong 等人 (2011) 专注于虚拟化资源的动态和智能管理,以满足云内不同应用程序的资源需求,适用于整合少量工作负载。T. Somasundaram 等人 (2013) 提出了一种云资源代理 (CRB),该代理具备自适应负载均衡 (ALB) 和弹性资源供给与释放 (ERPD) 机制,通过在虚拟实例之间平衡负载,并以弹性方式供给或释放虚拟实例来处理用户应用程序的请求。这些研究与我们的工作相互独立,因为在单个实例无法满足常规操作需求时,需要多个独立的数据库实例。
C. 库里诺等人 (2011) 并未以数据库实例整合为目标,而是提出了一种工作负载感知的数据库监控方案,用于将多个数据库工作负载整合到同一个数据库管理系统中。他们首先测量了不同工作负载的硬件需求,然后建立模型来预测混合工作负载的综合资源利用率,最后将所有工作负载整合到最少数量的服务器上,每台服务器在同一实例中运行多个工作负载。
S. Oliver 等人 (2011) 阐述了关系型数据库管理系统需要具备适当功能以原生支持租户感知的数据管理,并引入了租户上下文的概念,该概念逻辑上汇集了描述租户对数据库视图的所有信息,并清晰地实现了租户之间的隔离。S. 桑卡尔等人 (2012) 和 H. Mahmoud 等人 (2013) 研究了将不同租户的多个数据库工作负载整合到共享的数据库管理系统实例中的问题,旨在最小化服务器数量,同时确保所有租户的吞吐量服务等级协议( SLA)得到满足。
共享表 方案采用模式映射技术,使多个租户共享适量的基础表和扩展表,其中公共列的数据存储在基础表中,租户特定列的数据存储在扩展表中。一种这样的模式映射技术是通用表(S. 奥尔巴赫 等人,2008),该技术除了包含所有租户共享的属性外,还包括预设数量的自定义属性,用于存储租户特定的属性。如果一个租户向其逻辑表模式添加一个属性,则该属性将映射到一个未使用的自定义属性上。因此,预设的自定义属性数量应必须足够大以满足所有租户的需求。然而,如果自定义属性的最大数量过大,例如 20,000,但大多数租户仅需要少量自定义属性,例如 5 个,则处理大量 NULL 值将带来很高的开销。解释型属性存储格式(J. Bechmann 等,2006)在微软 SQL Server 中作为稀疏列(E. Chu 等, 2007)功能实现,以应对过多 NULL 值带来的问题。然而,该方法阻碍了随机属性访问的优化,并且每个表最多只允许 30,000 个稀疏列。此数量可能不足以容纳数万个租户。
M. Hui 等(2009)提出了一种名为 M-Store 的数据库系统,为多租户提供存储和索引服务。M-Store 通过两种技术提高了可扩展性:位图解释元组(BIT)和多分离索引(MSI)。BIT 不在大型共享的通用表中存储未使用属性的 NULL 值,从而避免了在存储或处理通用表中存在的大量 NULL 值时浪费磁盘和中央处理器资源。MSI 支持灵活的索引机制,由于所有租户几乎不需要在同一列上建立索引,因此无需对同一列的所有数据建立索引,而只需对每个租户自身的数据建立索引即可。S. Aulbach 等 (2008)提出了一种称为块折叠的技术,该技术将租户特定的属性存储在一个固定的所谓块表集合中,其中块表是一种扩展表,但存储的是一组列而非单一列,从而减少了 NULL 值的数量。H. Yaish 和 M. Goyal( 2013)提出了一种介于软件应用与数据库管理系统之间的中间数据库层,用于在一种称为弹性扩展表(EET)的多租户数据库模式中存储和访问租户数据,使服务提供商能够轻松创建高度可配置的关系型多租户数据库,以满足不同租户的业务需求和商业需要。
基于共享表方案的方法仅适用于所有租户具有相似模式的应用,这样可以将公共表提取为共享模式的基础表。对于每个租户具有不同模式因而表结构各异的应用,无法提取公共表来形成共享模式,因此共享表不适用。本文提出的方法允许不同租户拥有各自不同的模式。更重要的是,我们引入了一种新颖的机制——按查询加载,以低成本高效地在云中支持多租户数据库应用作为服务。
6. 结论与未来工作
本文提出了一种新颖的机制,可高效低成本地在云环境中为中小企业提供多租户数据库应用服务。我们的目标应用场景是服务大量小型租户,但其中仅有一部分租户同时活跃。这种新型的“按查询加载”机制通过仅为活跃租户维护相对少量的工作表,能够将大量租户整合到同一个数据库管理系统中。我们通过两种方法实现了该机制:传统的关系型数据库管理系统MySQL和内存数据库管理系统VoltDB。实验结果验证了所提机制的可行性,并表明与现有的共享进程方案相比,该机制能够实现更高的整合度和性能。
在提高所提出机制的可扩展性和可用性方面仍有改进空间。一方面,我们计划同时整合基于磁盘的数据库管理系统和内存数据库管理系统,以便根据不同租户的服务级别目标(SLO)提供差异化的服务。例如,具有低性能SLO的租户可以被整合到基于磁盘的数据库管理系统实例中,而具有高性能SLO的租户则可以被整合到内存数据库管理系统实例中。另一方面,我们将尝试利用租户的活动模式(如租户发出查询的频率、读/写请求的比例等)来实现更优的租户工作负载分布与整合。通过从租户的查询日志中挖掘此类信息,我们可以优化决策,选择哪些租户应被整合到同一个数据库管理系统实例中。一个基本的原则是,分布在不同数据库管理系统实例上的工作负载应尽可能均衡和稳定,从而易于实现负载均衡,并减少所需的工作负载重新分配。减少工作负载重新分配意味着MTDBaaS系统在加载数据时消耗的中央处理器和内存资源将更少,从而可以分配更多资源用于响应查询请求。
56

被折叠的 条评论
为什么被折叠?



