1.2.2 大规模并行处理(MPP)架构
读写分离架构结合了当前业务需要读多写少的实际场景,分散了数据库读写操作的压力,能够利用多台服务器节点的计算资源,提高数据库架构的整体性能。然而,这种数据库架构没有分散数据库的存储压力,数据库不同节点间存在大量数据冗余。
随着大数据时代的到来,当数据库单表数据条目达到千万条甚至上亿条,数据容量达到数十TB、数百TB甚至数PB时,单台数据库服务器的存储能力将会成为系统的瓶颈。总结起来,海量数据的影响主要包括以下几个方面。
(1)在海量数据场景下,数据库读写性能下降,当数据量太大时,索引也会变得很大,数据库性能将不断下降。
(2)数据文件将变得很大,数据库维护起来会面临新的困难。
(3)数据容量越大,数据备份与恢复需要的时间也越长。
基于上述原因,单台数据库服务器存储的数据量不能太大,需要控制在一定的范围内。在数据容量不断增大的应用场景下,除备份需要外,将数据分散存储到多台数据库服务器上也是必然要求。
在这种场景下,数据在各节点如何分布将是MPP架构的重点,下面首先介绍MPP架构的基本结构,然后重点介绍MPP架构各节点之间的数据分布方式。
1. 基本结构
MPP架构的核心在于,数据库各节点都有自己的硬件资源,各节点之间通过专有网络联系,相互之间不存在共享资源,因而在处理数据时并行处理能力和扩展能力更好。在此场景下,各节点相互独立且完全对等,无共享存储资源,各自处理自己的数据,在需要面向全局处理数据时,查询结果需要通过中央节点或临时的中央节点进行协调,处理后的数据在节点间进行流转。一般来说,这种MPP架构被称为无共享(Share Nothing)架构,因此需要对集群的数据整体进行切分,按照一定规则分配到不同节点。在此基础上,每台服务器都可以独立工作,但每台服务器都仅能处理自身的数据。
虽然MPP架构的各个节点之间相互独立,但数据库集群在运行过程中,一般需要由控制节点来处理外部访问请求。基于控制节点的特殊地位,MPP架构存在两种部署方案:一种方案是,MPP架构通过设置独立的控制节点来管理数据库各数据节点,即完全不共享架构;另一种方案是,通过各数据节点自身来实现控制节点的功能,可以在数据节点中指定控制节点,也可以由各数据节点自由选择控制节点,具体需要由特定算法来实现,即完全对等不共享架构。
MPP架构如图1-3所示。
图1-3 MPP架构
当前,不同的数据库软件厂商在实现MPP架构时对控制器节点的处理方式不相同,有侧重于独立控制器节点的,也有在数据节点上独立运行控制功能的。两种实现方式在性能上各有优劣,单节点控制器可能存在单节点故障或性能瓶颈,但在节点数量不多或在控制节点优化处理的情况下,控制器节点能够做好MPP架构中各数据节点的访问分配功能。在部署过程中,完全不共享架构由于要单独部署控制器,会增加部署难度。
2. 架构指标
MPP架构通过各个节点分散数据存储,从而提高架构整体性能。海量数据如何在各节点之间分布,后面会详细介绍。为进一步说明MPP集群的整体性能,下面根据数据库架构的各项指标分别进行介绍。
(1)一致性:MPP架构中不存在共享资源,即不存在数据副本,因此可以不考虑其数据的一致性问题。如果MPP架构中为了实现容灾备份增加了节点,则需要根据其副本交互情况进行考虑。
(2)性能:读写操作需要分散到数据存储的各个节点,因此可以充分利用各节点计算性能,但集群整体性能并不是线性提高的,因为数据访问结果最终需要汇聚,实际性能与实现算法相关。MPP集群在提高性能时,主要通过对数据的分布式存储实现。
(3)高可用性:MPP架构的主要功能在于分散存储数据,因此在没有进行数据备份的情况下,如果某个节点丢失,则会导致这个节点上的数据无法访问。但在一般情况下,MPP架构可以通过各节点副本提供冗余保护、自动故障检测和管理,从而让系统具有高可用性。对于有独立控制节点的MPP架构,还需要对其控制节点做冗余备份,否则一旦控制节点出故障就会影响整个数据库集群的运行。
(4)可扩展性:MPP架构具有高可扩展性,支持集群架构节点的扩容和缩容,即通过增加节点,不断提高服务器架构的整体性能和存储容量。当前常用的MPP架构数据库可以将数据节点扩充到数百个甚至数千个。
(5)部署难度:节点数据量的增加会增加MPP架构的部署难度,需要在各节点间布置高速网络,如果通过第三方中间件来实现,则需要考虑软件和数据库的兼容性。
3. 数据垂直切分
MPP架构各对等数据节点之间要进行数据分配,最直观的思路是按照业务逻辑功能进行数据切分。对于任何一个业务系统,一般包含多个业务模块,因此在其整体数据规模增大之后,各个模块可独立部署到独立服务器上。例如,对于一个购物网站,其包含用户信息、订单信息、商品信息等不同业务模块,因此可以将这3个模块的数据分别存储到不同的数据节点上,从而实现数据的切分,减少单节点的数据量。
这种业务功能切分能够分散单节点数据量,适用于各模块数据之间独立性较强的情况。但在不同模块之间数据交互比较频繁、连接查询比较多的情况下,数据库整体性能可能受到影响。例如,当不同功能模块的数据切分到不同节点后,数据库无法进行连接查询。以查询某类用户群体的订单情况为例,只能在一个数据库中先查询某类用户群体的信息,然后根据用户信息去另一个数据库中查询订单数据。如果数据库未根据业务功能进行切分,则实际上这个查询过程是很简单的,只需要进行一次连接查询即可。
另外,数据分散之后,在进行事务处理的时候,需要考虑不同数据库数据的一致性。一些中间件软件能够实现分布式数据库的事务处理过程,但不同的数据库需要适配,不是所有的MPP数据库都能够适配,有时候在性能上也无法满足需求。一般的做法是通过业务层代码实现数据的事务一致性,这对程序的代码实现要求较高,系统设计前一定要充分考虑任何数据失效或程序运行失败的场景。
根据系统业务功能进行数据切分,是一种粗粒度的数据切分方式。在单表数据量较大、数据字段较多时,可进行更细粒度的数据切分,即针对字段进行切分,将一张表切分成两张表。例如,在用户个人信息表中,包含了身份证号、姓名、年龄、性别、个人简介等多个字段,当数据量较大时,可以将常用的字段(如身份证号、姓名等)放到一张表中,其余不常用的字段放到另一张表中,这样在单表查询时可以减少数据量,从而提升查询性能。
在数据切分过程中,也可以根据实际情况进行多次切分,但需要掌握的原则是,数据切分应尽量根据数据查询的频繁程度进行,这样才能更好地贴合实际的应用需求。同样地,对字段进行切分会引入新的问题。例如,在数据量很大时,没有从根源上解决数据量的问题,对字段的切分可能会违背基本的数据库范式设计标准(目前互联网应用不严格要求数据库设计遵循各项范式标准)。
以上两种数据切分方式,是在粗、细两种维度进行的数据切分。这两种方式都是在纵向上对数据进行切分的,因此一般称为数据垂直切分。在进行数据垂直切分时,MPP架构一定要充分考虑不同业务之间的相互关系,尽量避免两种数据切分方式在连接查询、事务一致性及性能提升上不明显的情况。数据的垂直切分,尤其是按业务功能将数据进行切分,一定是在数据库单节点无法满足数据容量需求的情况下进行的。如果没有这一需求,那么单纯将数据按垂直方向切分得到的好处可能无法抵消由此带来的性能损失。数据垂直切分架构如图1-4所示。
图1-4 数据垂直切分架构
4. 数据水平切分
在数据库数据容量不断增大后,单表数据量通常很大,会达到千万条甚至上亿条数据的规模。此时对单表进行查询,会在性能上会遭遇瓶颈。这种场景需要对表进行切分,将这些数据分散到不同的表中,甚至分散到不同的数据库节点中。对数据条目的切分,不需要对表结构进行修改,因此一般称为数据水平切分。
目前,数据水平切分没有严格的实现标准,其主要取决于表的数据访问性能。当数据量大到访问表数据对业务有影响时,就可根据实际情况进行切分。对于一个字段比较多、内容存储容量消耗大的表,当数据量达到百万条或千万条级别时就影响访问效率了,这种表要尽早切分;对于一些简单的包含几个简单字段的表,存储数据量达到千万条时可能也不影响效率,在这种情况下也可以不对数据进行切分。因此,实际的数据切分一定要根据实际的业务需求进行判断。但是,当数据量不断增长时,需要提前考虑数据切分方案。
数据水平切分的规则有很多,有不同的算法可以实现,下面介绍几种比较常见的数据分布算法。
1)范围分布
按照某个字段的取值范围进行数据切分,不同范围的数据将分散到不同的数据库中。例如,以用户数据中的ID为例,可以按照500万条的范围进行分段,前500万条数据存在第一个数据节点中,第500万条到第1000万条数据存在第二个数据节点中,以此类推。具体的范围切分,需要考虑实际的业务表字段数量和大小。分段过小会导致节点数量太多,分段过大可能会导致单表仍然存在性能问题。数据按范围切分的好处在于,随着数据量的增加,可以平滑地将数据扩充到新的数据库节点中,同时不需要对旧数据进行修改。但按范围切分存在一个隐含的缺点,即最后一个节点存储的数据量可能小于前面几个节点,在节点数量较少时数据分布不够均匀。
2)哈希分布
哈希分布指的是按照表中一列或多列字段的数据进行哈希计算,并根据哈希值和哈希映射表,将数据分布到映射的节点上。例如,以订单表为例,开始时系统规划了8个数据库节点,最简单的实现方式是通过订单ID对8进行取余操作,从而将订单划分到不同的节点中。对数据进行哈希切分,需要重点关注初始表数据的选取,一定要根据实际的数据量,以及未来数据量的增长情况进行有效预判,从而对数据进行切分。哈希分布的优点在于数据分布非常均匀,但是一旦数据量增长迅速,在需要增加节点时,扩充新的节点就需要对所有数据重新进行哈希计算。
3)配置分布
配置分布指的是通过指定独立的配置记录来查找数据节点。同样以订单数据为例,通过新增一张配置表记录所有订单和节点之间的匹配信息,当需要查询某个订单时,首先查询这个配置表,就可以知道数据存放在哪个节点上。配置分布方式设计比较简单,使用起来也非常灵活,在数据扩充的时候,只需要在最原始的配置表中增加记录即可。配置分布的缺点在于每次查询时需要首先查询配置表,即多查询一次。如果数据条数比较多,如数据量增大到几亿条时,那么配置表本身可能也存在性能瓶颈。因此,使用配置分布时要预估整体数据量。
5. 实现方式
与读写分离架构类似,商业数据库公司一般会根据产品应用场景设计专用的后台进程或控制程序来实现数据分配,也有一些开源数据库软件产品通过自研式程序封装或第三方中间件的形式来实现数据分配。一般而言,MPP架构比读写分离架构的实现方式要复杂一些:读写分离架构只需要识别DML语言是写操作还是读操作,判断SQL操作中的SELECT、UPDATE、INSERT、DELETE等关键字即可;MPP架构不仅要判断操作类型,还要判断SQL操作的表及不同的SQL函数,各节点在面向不同的SQL操作时处理的过程也不一样。
对于SELECT、UPDATE、DELETE等操作,如果针对的是全表操作,那么直接将SQL指令发向各节点,各节点返回执行结果即可;如果针对的是一部分数据,则根据当前数据分布规则,只需要将SQL指令发向匹配规则的节点。INSERT操作类似,需要根据插入的一条或多条INSERT语句按照数据分布规则由匹配节点分步执行。
其他的特定操作,需要根据不同的SQL规则分别制定实现流程。例如,对于JOIN、ORDER BY等操作,由于数据分散在各个节点中,需要在程序封装代码或第三方中间件中同各节点进行多次操作,最终将获取结果进行合并,其整体实现性能依赖各节点实现过程。对于COUNT操作,执行过程可以和JOIN操作类似,但为了增强查询性能,减少每次在各个节点进行COUNT操作带来的性能影响,可以设置一个专用记录表,记录各节点、各表当前的数据量,这样每次在数据库中执行COUNT操作时,就可以通过累加各节点已经记录的表数据量快速获得结果。这种方式也有缺点,当各节点数据发生变化,即进行增、删操作时,都需要实时更新专用记录表。
MPP架构对不同SQL操作的处理方式不同,主要是因为不同SQL指令的原理是不一样的。同样,MPP架构在实现过程中对数据切分方式的使用也不是固定的。什么情况下采用数据垂直切分,什么情况下采用数据水平切分,在MPP架构设计之初就需要规划好。例如,当单台数据库服务器的访问量超过机器存储容量的一半时,就要考虑通过多台数据库服务器进行数据切分。如果业务功能模块能够有效拆分,或者业务对数据的操作集中在某些字段上,那么可以考虑使用数据垂直切分方法;如果存储的压力主要源于一些关键表的数据量太大,那么可以考虑采用数据水平切分方法。不管是哪种方法,MPP架构的实现方式不是一成不变的,一定是和业务深度关联的。