分布式数据库OLAP

OLAP

OLAP是一种用于分析多维数据的技术,它可以帮助用户快速地查询、汇总和分析大量的数据。

Decision Support Systems

服务于组织管理、运营和规划级别的应用程序,通过分析历史数据,帮助人们对未来的问题和问题做出决策。

OLAP的两种常见的schema是star schema和snowflake schema,它们都是基于维度模型的逻辑结构,用于描述事实表和维度表之间的关系。事实表是存储度量值(如销售额、利润等)的表,维度表是存储描述性信息(如产品、客户、时间等)的表。

  • star schema:star schema是最简单的数据仓库schema,它的结构类似于一个星星,中心是一个事实表,周围是若干个维度表。事实表和维度表之间通过外键来建立关系,每个维度表只有一个层次,即只有一个表来表示一个维度。star schema的优点是查询性能高,因为只需要少量的连接操作;缺点是数据冗余高,因为每个维度表都包含了所有的属性值。

  • snowflake schema:snowflake schema是star schema的扩展,它的结构类似于一个雪花,中心也是一个事实表,周围也是若干个维度表,但是每个维度表可以进一步分解为子维度表,形成多个层次。snowflake schema对维度表进行了规范化处理,减少了数据冗余和存储空间;缺点是查询性能低,因为需要更多的连接操作。

总结一下,star schema和snowflake schema的主要区别有以下几点:

  • star schema的结构是星形的,snowflake schema的结构是雪花形的。
  • star schema每个维度只有一个表,snowflake schema每个维度可以有多个子表。
  • star schema是自上而下的设计方法,snowflake schema是自下而上的设计方法。
  • star schema占用更多的存储空间,snowflake schema占用更少的存储空间。
  • star schema没有规范化维度表,snowflake schema规范化了维度表。
  • star schema查询性能高,因为连接操作少;snowflake schema查询性能低,因为连接操作多。
  • star schema查询复杂度低,容易理解;snowflake schema查询复杂度高,难以理解。

Start VS Snowflake Schema
Issue 1 : Normalization

  • 雪花模式占用的存储空间较少。
  • 非标准化数据模型可能会导致完整性和一致性冲突

Issue 2 : Query Complexity

  • 雪花模式需要更多的连接来获取查询所需的数据。
  • 对星型架构的查询(通常)会更快。

Execution Models

Push VS Pull

方法1:Push Query to Data

  • 将查询(或其中的一部分)发送到包含数据的节点。
  • 在通过网络传输之前,在数据所在的地方执行尽可能多的过滤和处理。

方法2:Pull Data to Query

  • 将数据带到正在执行需要其进行处理的查询的节点。

OLAP中的查询模型两种方法:Push Query to Data 和 Pull Data to Query,是指在分布式数据库系统中,如何在不同的节点上执行查询和处理数据的两种策略。简单来说,就是将查询发送到数据所在的地方,还是将数据发送到查询所在的地方。

Push Query to Data 的方法是将查询(或其一部分)发送到包含数据的节点。这样可以减少网络传输的开销,因为在数据所在的地方可以尽可能多地进行过滤和处理,只返回必要的结果。这种方法适合于共享无关(Shared Nothing)的架构,即每个节点都有自己的CPU、内存、本地连接的磁盘,而且数据库被划分为跨节点的不相干子集。例如,Snowflake、Doris、ClickHouse等都采用了这种方法¹²³。

假设我们有一个分布式数据库系统,其中有四个节点,每个节点存储了一个不同的数据分区。我们想要执行一个查询,计算每个国家的平均年龄。我们可以将这个查询分解为两个阶段:第一阶段是在每个节点上对本地数据进行聚合,计算每个国家的人数和年龄总和;第二阶段是将第一阶段的结果发送到一个协调节点上,对所有国家进行最终的聚合,计算每个国家的平均年龄。这样,我们就可以避免将所有数据都发送到协调节点上,而只需要发送一些中间结果。

Pull Data to Query 的方法是把数据传输到正在执行查询的节点,该节点需要数据进行处理。这样可以利用更多的计算资源,因为可以在任何节点上执行查询。这种方法适合于共享磁盘(Shared Disk)的架构,即每个节点通过互连访问一个逻辑磁盘,但也有自己的私有内存和短暂的存储。例如,Oracle Exadata、Amazon Redshift等都采用了这种方法 。

假设我们有一个共享磁盘数据库系统,其中有四个节点,每个节点都可以访问同一个逻辑磁盘。我们想要执行一个查询,计算每个国家的平均年龄。我们可以将这个查询分配给任意一个节点来执行。该节点需要从磁盘上读取所有数据,并在本地内存中进行聚合。如果磁盘上的数据没有被缓存到内存中,那么这个过程可能会比较慢。但是如果磁盘上的数据已经被其他节点缓存过了,那么这个过程就会比较快。这样,我们就可以利用缓存效果来提高查询性能。

两种方法各有优缺点,具体选择哪一种要根据数据量、网络带宽、计算能力、查询复杂度等因素综合考虑。一般来说,如果数据量较大,网络带宽较小,计算能力较强,查询复杂度较高,那么 Push Query to Data 的方法更合适;如果数据量较小,网络带宽较大,计算能力较弱,查询复杂度较低,那么 Pull Data to Query 的方法更合适。

节点从远程源接收的数据缓存在缓冲池中。

  • 这允许DBMS支持大于可用内存量的中间结果。
  • 重启后临时页面不持久化

如果节点在执行过程中崩溃,长时间运行的OLAP查询会发生什么情况?

Query Fault Tolerance

大多数无共享分布式OLAP DBMS设计为假定节点在查询执行期间不会失败。
如果在查询执行期间有一个节点失败,则整个查询将失败。

DBMS可以在执行期间为查询拍摄中间结果的快照,以便在节点发生故障时恢复查询。

Query Planning

我们之前讨论过的所有优化仍然适用于分布式环境。

  • 谓语下推
  • 过早预测
  • 优化Join Orderings

Query Plan Fragements

方法一:Physical Operators

  1. 生成单个查询计划,然后将其分解为特定于分区的片段。

方法二:SQL

  • 将原始查询重写为特定于分区的查询。
  • 允许在每个节点进行本地优化。

Physical Operators 和 SQL,是指在分布式数据库系统中,如何将一个查询计划分解为多个子查询,然后在不同的节点上执行和合并的两种策略。简单来说,就是将一个查询计划转换为物理操作符或者SQL语句,然后根据数据分区的情况,将它们发送到相应的节点上。

Physical Operators 的方法是将一个查询计划表示为一系列的物理操作符,例如扫描、选择、投影、连接、聚合等。然后,根据数据分区的方式,将这些操作符划分为不同的片段(Fragment),每个片段对应一个或多个操作符。每个片段都有一个输入和一个输出,输入可以是一个数据分区或者另一个片段的输出,输出可以是一个结果集或者另一个片段的输入。这样,就可以将一个查询计划拆分为多个并行或串行的子查询,每个子查询都在一个节点上执行。这种方法的优点是可以利用物理操作符的优化技术,例如选择性传播、流水线处理、向量化等,来提高查询性能。这种方法的缺点是需要对物理操作符进行复杂的划分和调度,以及处理数据依赖和通信开销。

SQL 的方法是将一个查询计划重写为多个SQL语句,每个SQL语句对应一个数据分区或者一个中间结果。然后,根据数据分区的位置,将这些SQL语句发送到相应的节点上执行。每个节点上执行完SQL语句后,会返回一个结果集或者一个中间结果集。最后,将所有结果集或者中间结果集合并成最终的结果集。这种方法的优点是可以利用SQL语言的灵活性和表达能力,以及在每个节点上进行本地优化。这种方法的缺点是需要对SQL语句进行复杂的重写和转换,以及处理数据一致性和冗余问题。

Distributed Join Algorithms

要连接表R和S,DBMS需要在同一节点上获得正确的元组。

一旦数据到达节点,DBMS就会执行我们在前几节中相同的连接算法。

分布式OLAP中的Distributed Join Algorithms是指在分布式数据库系统中,如何高效地执行两个或多个分布式表之间的连接操作的一些算法。连接操作是一种常见的OLAP查询,它可以根据一个或多个共同的属性,将不同表中的数据组合在一起。例如,可以根据客户ID,将订单表和客户表连接起来,得到每个订单对应的客户信息。

分布式数据库系统中,数据通常被划分为多个分区(Partition),并存储在不同的节点上。这样可以提高数据的可扩展性和容错性,但也给连接操作带来了挑战。因为连接操作涉及到的两个或多个表可能分布在不同的节点上,而且每个节点上可能只有部分数据。因此,为了完成连接操作,需要在节点之间进行数据的传输和处理,这会增加网络开销和计算开销。

为了解决这个问题,分布式OLAP中提出了一些Distributed Join Algorithms,它们可以根据不同的场景和需求,选择合适的方式来执行连接操作。一般来说,这些算法可以分为以下几类:

  • Broadcast Join:这种算法是将连接操作中的一个较小的表(通常是右表)完整地复制到所有节点上,然后在每个节点上使用本地的连接算法(如Hash Join或Sort Merge Join)来完成连接操作。这种算法适合于右表很小,而左表很大的情况,因为它可以避免左表的数据传输。但是如果右表也很大,那么这种算法就会占用大量的网络带宽和内存空间。
  • Shuffle Join:这种算法是根据连接键(Join Key)将连接操作中的两个表(左表和右表)都划分为多个子集,并将相同连接键值的子集发送到同一个节点上,然后在每个节点上使用本地的连接算法来完成连接操作。这种算法适合于两个表都很大,并且连接键值均匀分布的情况,因为它可以平衡各个节点的负载和网络开销。但是如果连接键值分布不均匀,那么这种算法就会导致数据倾斜和负载不均。
  • Colocate Join:这种算法是在数据划分和存储时就考虑到了连接操作的需求,使得需要连接的两个表都按照相同的方式划分为多个子集,并存储在相同的节点上。这样,在执行连接操作时就无需进行数据传输,只需要在每个节点上使用本地的连接算法来完成连接操作。这种算法适合于经常需要进行连接操作,并且可以预先知道连接键值的情况,因为它可以最大程度地减少网络开销和计算开销。但是如果需要进行多种不同方式的连接操作,或者无法预先知道连接键值,那么这种算法就会失效或者低效。

四种场景详细介绍

场景1:每个节点复制一张表。每个节点并行连接其本地数据,然后将其结果发送到协调节点。

这种场景对应的算法是 Broadcast Join,即将连接操作中的一个较小的表(通常是右表)完整地复制到所有节点上,然后在每个节点上使用本地的连接算法(如Hash Join或Sort Merge Join)来完成连接操作。这种算法适合于右表很小,而左表很大的情况,因为它可以避免左表的数据传输。但是如果右表也很大,那么这种算法就会占用大量的网络带宽和内存空间。

例如,假设我们有一个分布式数据库系统,其中有四个节点,每个节点存储了一个不同的数据分区。我们想要执行一个查询,连接订单表(Order)和客户表(Customer),根据客户ID(CustomerID)进行连接。假设订单表很大,而客户表很小。我们可以使用 Broadcast Join 的算法来执行这个查询,具体步骤如下:

第一步:将客户表完整地复制到所有节点上。
第二步:在每个节点上使用本地的连接算法(如Hash Join或Sort Merge Join)来连接订单表和客户表,根据客户ID进行连接。
第三步:将每个节点上的连接结果发送到协调节点上,进行最终的合并和输出。
这样,我们就可以完成这个查询,并得到每个订单对应的客户信息。

场景2:表在连接属性上进行分区。每个节点对本地数据执行连接,然后发送到协调节点进行合并。

这种场景对应的算法是 Colocate Join,即在数据划分和存储时就考虑到了连接操作的需求,使得需要连接的两个表都按照相同的方式划分为多个子集,并存储在相同的节点上。这样,在执行连接操作时就无需进行数据传输,只需要在每个节点上使用本地的连接算法来完成连接操作。这种算法适合于经常需要进行连接操作,并且可以预先知道连接键值的情况,因为它可以最大程度地减少网络开销和计算开销。但是如果需要进行多种不同方式的连接操作,或者无法预先知道连接键值,那么这种算法就会失效或者低效。

例如,假设我们有一个分布式数据库系统,其中有四个节点,每个节点存储了一个不同的数据分区。我们想要执行一个查询,连接订单表(Order)和客户表(Customer),根据客户ID(CustomerID)进行连接。假设订单表和客户表都按照客户ID进行了划分,并且相同客户ID值的数据都存储在相同的节点上。我们可以使用 Colocate Join 的算法来执行这个查询,具体步骤如下:

第一步:在每个节点上使用本地的连接算法(如Hash Join或Sort Merge Join)来连接订单表和客户表,根据客户ID进行连接。
第二步:将每个节点上的连接结果发送到协调节点上,进行最终的合并和输出。
这样,我们就可以完成这个查询,并得到每个订单对应的客户信息。

场景3:两个表都在不同的键上分区。如果其中一个表很小,则DBMS“广播”该表到所有节点。

这种场景对应的算法是 Broadcast Join 的一种变体,即当两个表都在不同的键上分区时,如果其中一个表很小,则DBMS“广播”该表到所有节点,然后在每个节点上使用本地的连接算法来完成连接操作。这种算法适合于右表很小,而左表很大的情况,因为它可以避免左表的数据传输。但是如果右表也很大,那么这种算法就会占用大量的网络带宽和内存空间。

例如,假设我们有一个分布式数据库系统,其中有四个节点,每个节点存储了一个不同的数据分区。我们想要执行一个查询,连接订单表(Order)和客户表(Customer),根据客户ID(CustomerID)进行连接。假设订单表按照订单ID(OrderID)进行了划分,而客户表按照客户姓名(CustomerName)进行了划分。假设订单表很大,而客户表很小。我们可以使用 Broadcast Join 的变体算法来执行这个查询,具体步骤如下:

第一步:将客户表完整地复制到所有节点上。
第二步:在每个节点上使用本地的连接算法(如Hash Join或Sort Merge Join)来连接订单表和客户表,根据客户ID进行连接。
第三步:将每个节点上的连接结果发送到协调节点上,进行最终的合并和输出。
这样,我们就可以完成这个查询,并得到每个订单对应的客户信息。

场景4:这两个表都不在连接键上分区。DBMS通过跨节点“洗牌”表来复制表。

这种场景对应的算法是 Shuffle Join,即根据连接键(Join Key)将连接操作中的两个表(左表和右表)都划分为多个子集,并将相同连接键值的子集发送到同一个节点上,然后在每个节点上使用本地的连接算法来完成连接操作。这种算法适合于两个表都很大,并且连接键值均匀分布的情况,因为它可以平衡各个节点的负载和网络开销。但是如果连接键值分布不均匀,那么这种算法就会导致数据倾斜和负载不均。

例如,假设我们有一个分布式数据库系统,其中有四个节点,每个节点存储了一个不同的数据分区。我们想要执行一个查询,连接订单表(Order)和客户表(Customer),根据客户ID(CustomerID)进行连接。假设订单表和客户表都不在客户ID上进行了划分。我们可以使用 Shuffle Join 的算法来执行这个查询,具体步骤如下:

第一步:根据客户ID将订单表和客户表都划分为多个子集,并将相同客户ID值的子集发送到同一个节点上。
第二步:在每个节点上使用本地的连接算法(如Hash Join或Sort Merge Join)来连接订单表和客户表,根据客户ID进行连接。
第三步:将每个节点上的连接结果发送到协调节点上,进行最终的合并和输出。
这样,我们就可以完成这个查询,并得到每个订单对应的客户信息。

Semi-Join

Semi-Join算法是一种分布式数据库系统中执行连接操作的技术,它可以根据一个表中的数据,来过滤另一个表中的数据,而不需要将两个表真正地连接起来。Semi-Join算法的结果只包含左表或者右表中的数据,而不包含另一个表中的数据。

Semi-Join算法的作用和使用场景有以下几点:

  • Semi-Join算法可以提高查询性能,因为它不需要将两个表完全连接起来,也不需要返回多余的数据。它只需要检查两个表中是否存在匹配的记录,然后返回其中一个表中的记录。
  • Semi-Join算法可以实现子查询(Subquery)的功能,即根据一个表中的数据,来筛选另一个表中的数据。例如,如果我们想要查询所有有订单的客户,我们可以使用子查询:

SELECT * FROM Customer WHERE CustomerID IN (SELECT CustomerID FROM Order);

或者使用左半连接:

SELECT * FROM Customer LEFT SEMI JOIN Order ON Customer.CustomerID = Order.CustomerID;

这两种方式都可以得到相同的结果,但是使用左半连接可能更高效,因为它避免了子查询产生的临时表。

  • Semi-Join算法可以实现排除(Exclusion)的功能,即根据一个表中的数据,来排除另一个表中的数据。例如,如果我们想要查询所有没有订单的客户,我们可以使用反向子查询(NOT IN):

SELECT * FROM Customer WHERE CustomerID NOT IN (SELECT CustomerID FROM Order);

或者使用右半连接:

SELECT * FROM Customer RIGHT SEMI JOIN Order ON Customer.CustomerID = Order.CustomerID;

这两种方式都可以得到相同的结果,但是使用右半连接可能更高效,因为它避免了反向子查询产生的临时表。

Semi-Join算法的执行过程可以分为以下几个步骤:

  • 第一步:在左表所在的节点上计算出左表在连接键上的投影,并将其发送到右表所在的节点上。
  • 第二步:在右表所在的节点上根据接收到的左表投影和右表进行半连接操作,并将结果发送回左表所在的节点上。
  • 第三步:在左表所在的节点上根据接收到的右表结果和左表进行最终的连接操作,并输出结果。

例如,假设我们有一个分布式数据库系统,其中有四个节点,每个节点存储了一个不同的数据分区。我们想要执行一个查询,连接订单表(Order)和客户表(Customer),根据客户ID(CustomerID)进行连接。假设订单表存储在节点1上,而客户表存储在节点2上。我们可以使用Semi-Join算法来执行这个查询,具体步骤如下:

  • 第一步:在节点1上计算出订单表在客户ID上的投影,即Order.CustomerID,并将其发送到节点2上。
  • 第二步:在节点2上根据接收到的Order.CustomerID和客户表进行半连接操作,即Customer LEFT SEMI JOIN Order.CustomerID,并将结果发送回节点1上。
  • 第三步:在节点1上根据接收到的Customer结果和订单表进行最终的连接操作,即Order JOIN Customer,并输出结果。

这样,我们就可以完成这个查询,并得到每个订单对应的客户信息。

Cloud System

供应商提供作为托管DBMS环境的数据库即服务(DBaaS)产品。

方法1:Managed DBMS:供应商提供作为托管DBMS环境的数据库即服务(DBaaS)产品。(大多数使用的方法)
方法2:Cloud-Native DBMS: 该系统被明确设计为在云环境中运行; 通常基于共享磁盘体系结构。

Serverless Databases

“无服务器”DBMS并不总是为每个客户维护计算资源,而是在租户空闲时驱逐他们。

Serverless Databases是一种云数据库服务,它可以根据用户的需求自动地分配和调整计算和存储资源。用户不需要关心数据库的配置和扩展,也不需要预先支付固定的费用,而只需要按照实际使用的资源来付费。Serverless Databases可以提高用户的开发效率和成本效益,也可以提高数据库的性能和可靠性。

Serverless Databases的“无服务器”DBMS是指数据库管理系统(DBMS)不会为每个客户或租户(Tenant)持续地保留计算资源,而是在租户空闲或低负载时将其驱逐(Evict),从而释放资源给其他租户使用。这样,DBMS可以根据租户的活跃度来动态地分配资源,避免了资源的浪费和闲置。当租户再次发起请求时,DBMS会重新分配资源给租户,并恢复其状态和数据。这个过程通常是对用户透明的,用户不需要关心资源的分配和回收。

Serverless Databases的“无服务器”DBMS有以下几个优点:

  • 它可以降低用户的成本,因为用户只需要为实际使用的资源付费,而不需要为预留的资源付费。
  • 它可以提高数据库的弹性,因为数据库可以根据负载的变化自动地扩展或缩减资源,而不需要人工干预或停机。
  • 它可以提高数据库的可用性,因为数据库可以在多个区域或可用区中分布资源,从而提高容灾能力和故障恢复能力。

当然,Serverless Databases的“无服务器”DBMS也有一些挑战和限制,例如:

  • 它可能会增加用户的响应时间,因为当租户被驱逐后再次发起请求时,需要重新分配资源并恢复状态和数据,这可能会导致一定的延迟。
  • 它可能会影响用户的数据一致性,因为当租户被驱逐后再次发起请求时,可能会遇到数据同步或缓存失效的问题。
  • 它可能会限制用户的功能和控制权,因为用户不能自己配置和管理数据库的参数和设置,也不能自己选择数据库的版本和类型。

Data Lakes

云数据库系统 Data Lakes是一种在云平台上构建和管理数据湖的服务。数据湖是一个集中式存储库,可以存储任何类型和规模的数据,无论是结构化的还是非结构化的还是非结构化的。数据湖可以支持多种分析方法,如 SQL 查询、大数据处理、机器学习、实时分析等,从而帮助用户从数据中获取更多的价值和见解。

云数据库系统 Data Lakes有以下几个优势:

  • 它可以提供弹性和可扩展性,因为它可以根据用户的需求自动地分配和调整计算和存储资源,而无需用户关心配置和扩展的细节。
  • 它可以提供经济性和高效性,因为它可以按照实际使用的资源来收费,而无需预先支付固定的费用或为闲置的资源付费。
  • 它可以提供安全性和可靠性,因为它可以利用云平台的安全措施和备份机制来保护数据的完整性和可用性,以及遵守相关的法规和合规要求。
  • 它可以提供多样性和灵活性,因为它可以支持多种数据源和数据格式,以及多种分析工具和框架,让用户可以自由地选择适合自己需求的方案。

猜你喜欢

转载自blog.csdn.net/weixin_47895938/article/details/132328022