Data Partitioning Guidance

在很多大规模的解决方案中,数据都是分成单独的分区,可以分别进行管理和访问的。而分割数据的策略必须仔细的斟酌才能够最大限度的提高效益,同时最大限度的减少不利影响。数据的分区可以极大的提升可扩展性,降低争用以及优化性能。

为何进行数据分区

绝大多数的云应用和云服务都会将存储和检索数据作为其业务的一部分。而应用所使用的数据仓库的设计对于系统的性能,吞吐,扩展性都有着非常重要的影响。其中在大规模系统中常用的一种技术,就是对数据进行分区。

本文中所提到的数据分区,指的是物理上将数据分割成不同的数据仓库。这并不同于SQL Server 表的拆分,这两者完全是两种不同的概念。

对数据进行分区包含了很多的好处,如下:

设计分区

数据分区可以采用不同的方式:横向,竖向,根据功能这三种。开发者可以根据其使用场景和需求选择合适的策略来进行数据分区。

本文中描述的数据分区的体系和数据仓库使用的一些技术是彼此独立的。数据分区完全可以使用不同的数据仓库,包括关系型数据库和NoSQL类型的数据库。

分区策略

常见的三种分区策略如下:

需要注意的是,以上描述的三种不同的策略是可以联合使用的。他们并不是互斥的,开发者可以在考虑数据分区的时候同时考虑。举例来说,开发者可以将数据区分成不同的shard然后使用纵向分区策略来将每个shard中的内容分区。类似地,可以通过功能性分区来讲shard中的数据继续分区等。

然后,每个策略不同的需求也会引起一系列的冲突问题,开发者必须在这设计分区的时候进行权衡,才能满足系统对数据处理的性能要求。下面将针对每个策略的细节进行具体的描述。

横向分区(Sharding)

图1展示了横向分区(sharding)的概览图。在这个例子当中,产品库存数据基于产品的key被区分成不同的shard(每个shard包含了连续返回的shard key,以字母顺序管理)

图1.横向分区(sharding),基于key来分割数据

Sharding可以让应用承载更多的计算机,减少争用,以及提升性能。开发者可以轻易的通过增加shard和服务器来扩展系统。

实现横向分区策略最关键的一个点就是sharding的key。在系统运行后,想要更改key就很困难了。Key必须保证数据在正确的分区,并且足够的平均,这样才能令工作负载平摊。同时,确保单个shard的访问没有超过其数据仓库能承受的性能上限也非常重要。

同时,shard的结构也避免创建hotspot(过度访问的分区),因为那样可能影响性能以及可用性。举个例子,使用基于客户Id的哈希而非客户名字的首字母进行分片,可以有效防止因为字母频率不同所引起的不平衡。这是一种典型的用来帮助数据进行平均分区的技术。

开发者选择的sharding key应该尽量减小将来继续分片,将一些分片合并成大分区或者修改数据仓库的描述等需求。这些操作将非常耗时,并且可能会需要很多分片临时下线才能完成这些操作。如果shard复制了,当其他分片在执行分片,合并或者重新配置的时候,尽量保证一些拷贝是在线的,当进行这些处理的时候,系统可能需要限制对这些被操作的分片的访问。例如,处于拷贝中的数据可能需要被标记为只读,以限制写操作所带来的不一致性。

想了解更多的关于实现Sharding和横向分区的实践技术和所需要考虑的问题,可以参考Sharding模式来了解更多信息。

纵向分区

使用纵向分区最常见的场景就是为了减少由应用最频繁访问数据的条目数。图2展示了纵向分区的一个概览图,数据的不同的属性处于不同的分区之中。

图2.纵向分区根据其场景来组织数据

在上面的例子中,应用最频繁请求的是产品的名字以及描述和价格信息,并将这些信息展示给客户。而库存级别以及产品上一次从厂家预定的日期就被放在了不同的分区,因为通常情况下,这两种数据是一起使用的。这种情况下进行的数据分区会有一个额外的优势,就是相对来说,变化较少的数据(比如产品名字,描述,以及价格等)数据和很容易和一些动态数据(库存,预定日期)等分离开来。应用可以选择性的去缓存那些很少改变的数据到内存中来最大化性能。

另一种典型的应用场景就是为了分离敏感数据,来增加对敏感数据的保护。举个例子,可以将信用卡的卡号和其对应的安全验证码分离到不同的分区中。

垂直分区还可以减少数据所需的并发访问量。

垂直分区在数据存储区内的实体级别上运行,部分地将包含多个字段的实体规范化为多个实体,并用更少的字段。

功能分区

有些系统可以为每个不同的业务领域或服务的应用程序识别有界的上下文,功能分区为这类系统的隔离和数据访问性能的提高提供了技术支持。图3展示了功能分区的一个概览图,其中库存数据和客户数据是进行了分离的。

图3 功能性分区通过上下文和子域来进行数据分区

功能分区策略可以帮助减少系统不同部分之间的访问冲突。

为扩展性设计分区

考虑每个分区的负载和大小,并且平衡他们是必不可少的,只有这样才能最大化扩展性。同时,开发者必须对数据进行分区,使其不超过单个分区存储区的物理限制。

在考虑设计分区扩展性的时候,可以遵循如下的一些步骤:

  1. 分析应用来理解数据的访问模式,每次的请求的大小,访问频率以及内在的等待时间,和一些计算处理需求比如存储过程。在很多情况下,都是少量实体需要大量的处理资源。
  2. 基于对应用的分析,判断当前以及未来的规模目标。比如数据大小以及工作负载,并且对数据进行分区来达到对应的规模目标。在横向分配策略中,选择合适的shard key是非常重要的,只有这样才能将数据尽可能均等的分区。关于更多的信息,可以参考Sharding模式.
  3. 分区的时候需要确保每个分区所需要的资源足够,能够在吞吐和大小上达到需求值。例如,某个分区的节点在存储空间,处理能力,网络带宽上都存在限制。如果数据存储和处理需求很容易就超过这些限制的话,重新选择新的分区策略或者分割数据就是十分必要的了。比如,一个扩展的方法可能会通过使用分离数据仓库的方式来分离核心应用的日志数据和应用特性相关的数据来防止服务器上数据存储超过了单个节点的容量上限。如果数据仓库的总数超过了节点限制,使用分离的数据节点就十分必要了。
  4. 监控使用中的系统来验证数据如预期的那样工作,并且分区可以处理相应的负载。有可能分区的使用量和分析的并不是十分的一致的,如果需要的话,可以针对系统部分不合适的地方进行重新设计来尽可能的平衡各个分区的负载以达到性能和扩展性的需求。

需要注意的是,一些云环境中分配资源的时候会涉及到基础设施的限制,开发者应该确保自己选择的限制能够预留足够的空间以应对数据量的增长,包括磁盘空间,处理能力,带宽等。举个例子,如果开发者使用Windows Azure表存储,频繁的shard可能需要更多的资源来处理一个单表的请求(单表在指定时间内处理请求的数量是存在限制的)。在这种情况下,分片可能需要分割成多个表来分散负载。如果这些表的总大小超过了一个存储账户的容量,就需要创建额外的存储账户来分散负载。如果账户的数量超过了一个subscription中的账户上限,就需要使用多个subscription了。

为请求性能设计分区

通常可以通过使用小的数据集合和并行请求执行来增加查询性能。每个分区都包含全部数据的一部分,这样可以有效提高查询的性能。然而,分区并不是提高性能的唯一选择,还要看数据库是否配置正确。比如说,在使用关系型数据库的时候,是否配置了必要的索引来提高性能。

在为了提高查询性能而设计分区的时候可以参考如下的一些步骤:

  1. 分析应用来识别出:
    • 那些执行缓慢的请求。
    • 那些关键的而且必须快速执行的请求。
  2. 将性能缓慢的数据进行分区。必须确保如下方面:
    • 限制每个分区的大小,这样请求响应时间才能达到目标。
    • 在实现横向分区的时候,设计shard key的计算方式,让应用可以轻易找到正确的分区。这样可以防止请求扫描全部分区。
    • 考虑一个分区的位置对查询性能的影响。如果可能的话,尝试将数据保存在地理上接近应用程序和访问它的用户的分区中。
  3. 如果实体有吞吐和请求性能需求,使用基于实体的功能性分区。如果仍然无法满足需求,也应用横向分区来解决问题。在绝大多数场景下,单独使用一种分区策略就足够了,但是在某些场景下,需要考虑多种策略配合使用来进行分区。
  4. 跨分区的查询请求可以通过并行查询来增强性能。

为可用性设计分区

分区数据通过确保整个数据集不会因为一个端点的错误而影响全部的业务以及独立管理每个分区的数据集来提升应用的可用性。当设计和实现分区时,可以考虑如下影响可用性的因素:

问题和顾虑

在设计数据分区的时候会有如下的一些顾虑:

所有数据存储需要一些操作管理和监视活动。任务的范围可以从加载数据,备份和恢复数据,整理数据,并确保系统执行的正确性和有效性。

考虑影响运营管理的以下因素:

相关的模式

在应用程序和数据存储中实现数据分区时,可以考虑参考如下的一些模式或者方案: