系统设计:分片或者数据分区

2021-09-03 15:00:26 浏览数 (1)

定义

数据分区(也称为分片)是一种将大型数据库(DB)分解为许多较小部分的技术。它是跨多台计算机拆分一个DB/表的过程,以提高应用程序的可管理性、性能、可用性和负载平衡。

原因

数据分片的理由是,在达到一定的规模后,通过增加更多的机器来水平扩展比通过增加更强大的服务器来垂直扩展更便宜、更可行。

一、划分方法

可以使用许多不同的方案来决定如何将应用程序数据库分解为多个较小的数据库。下面是各种大规模应用程序使用的三种最流行的方案。

A.水平分区

在这个方案中,我们将不同的行放入不同的表中。例如,如果我们在一个表中存储不同的位置,我们可以确定地区编码小于1000的位置存储在一个表中,而地区编码大于1000的位置存储在一个单独的表中。这也称为基于范围的分片,因为我们将不同范围的数据存储在不同的表中。

这种方法的关键问题是,如果不仔细选择用于分片的范围值,则分区方案将导致服务器不平衡。比如北京可能比其他的地区多很多数据。

B垂直分区

在这个方案中,我们将数据划分为与特定功能相关的表存储在它们自己的服务器中。例如,如果我们正在构建类似电商网站的应用程序—我们可以决定将用户信息放在一台DB服务器上,商家列表放在另一台服务器上,商品放在第三台服务器上。

垂直分区易于实现,对应用程序的影响较小。这种方法的主要问题是,如果我们的应用程序经历了额外的增长,那么可能需要在不同的服务器上进一步划分特定于功能的数据库(例如,单个服务器不可能处理1.4亿用户对100亿张照片的所有元数据查询)

C基于目录的分区

解决上述方案中提到的问题的松耦合方法是创建一个查找服务,该服务了解当前的分区方案,并将其从DB访问代码中抽象出来。因此,为了找出一个特定的数据实体所在的位置,我们查询保存每个元组键到其DB服务器之间的映射的目录服务器。这种松散耦合的方法意味着我们可以在不影响应用程序的情况下执行诸如向DB池添加服务器或更改分区方案之类的任务。

二、划分标准

A.基于密钥或散列的分区(哈希分区)

在这个方案下,我们将散列函数应用于我们存储的实体的一些关键属性;这就产生了分区号。例如,如果我们有100个DB服务器,并且我们的ID是一个数值,每次插入一条新记录时,它都会递增一。在本例中,哈希函数可以是'ID0',这将为我们提供可以存储/读取该记录的服务器号。这种方法应该确保在服务器之间统一分配数据。这种方法的根本问题是,它有效地修复了DB服务器的总数,因为添加新服务器意味着更改哈希函数,这将需要重新分配数据和服务停机。解决这个问题的一个方法是使用一致的哈希。

B列表分区

在这个方案中,每个分区都被分配一个值列表,所以每当我们要插入一个新记录时,我们都会看到哪个分区包含我们的键,然后将它存储在那里。例如,我们可以决定居住在冰岛、挪威、瑞典、芬兰或丹麦的所有用户将存储在北欧国家的分区中。

C循环分区(哈希取模)

这是一个非常简单的策略,可以确保数据分布的一致性。对于'n'分区,'i'元组被分配给分区(i mod n)。

D组合分区

在这个方案下,我们将上述任何一种分区方案结合起来设计一个新的方案。例如,首先应用列表分区方案,然后应用基于哈希的分区。一致散列可以被认为是散列和列表分区的组合,其中散列将密钥空间减少到可以列出的大小

三、切分常见问题

在分片数据库上,可以执行的不同操作有一些额外的限制。这些限制大多是由于跨多个表或同一表中多行的操作将不再在同一服务器上运行。以下是切分带来的一些限制和额外的复杂性:

A.联表查询join和逆范式的使用

在一台服务器上运行的数据库上执行联接是很简单的,但是一旦一个数据库被分区并分布在多台计算机上,执行跨数据库碎片的联接通常是不可行的。由于必须从多个服务器编译数据,这样的连接将不会提高性能。解决这个问题的一个常见方法是对数据库进行非规范化,以便可以从单个表执行以前需要的联接的查询。当然,服务现在必须处理所有非规范化的危险,比如数据不一致。

B引用完整性

正如我们所看到的,在分区数据库上执行跨分片查询是不可行的,类似地,在分片数据库中强制执行数据完整性约束(如外键)可能非常困难。

大多数RDBMS不支持不同数据库服务器上的数据库之间的外键约束。这意味着在分片数据库上需要引用完整性的应用程序通常必须在应用程序代码中强制实现。通常在这种情况下,应用程序必须运行常规的SQL作业来清除悬空引用。

C重新分区

我们必须改变分片方案的原因可能有很多:

1.数据分布不均匀,例如某个特定的邮政编码有很多地方放不进一个数据库分区。

2.一个shard负载很大,比如DB shard处理的用户照片请求太多。

在这种情况下,要么我们必须创建更多的DB shard,要么必须重新平衡现有的shard,这意味着分区方案发生了变化,所有现有数据都移动到了新的位置。在不引起停机的情况下这样做是非常困难的。使用类似于基于目录的分区的方案确实会使重新平衡体验更加愉快,但代价是增加系统的复杂性并创建新的单点故障(即查找服务/数据库)。

那么上面基于谷歌系统设计理论上来说具体实践应该如何操作?笔者之前在京东经历过上述的过程,每张表的数据量根据表的描述复杂度直接影响数据存储量和性能指标,按照笔者当时的单表平均数据量400-700万之间出现了数据倾斜,以及由于业务上涨单个大促15亿多数据导致需要重新分区,具体实践案例参考2018年文章MySQL分库分表的实践。

参考资料

grok_system_design_interview.pdf

0 人点赞