物化视图如何快速完成数据聚合操作?

SQL在过去十年逐渐走向没落,如今春风吹又生,SQL正在复苏。随着应用程序变得越来越复杂,数据驱动的应用场景越来越多,人们慢慢意识到需要一种数据处理语言。而SQL作为一种用于处理结构化数据的机制,它的有效性大家有目共睹。

在此篇文章中,我们将讨论VoltDB如何实现物化视图,以及为什么VoltDB的物化视图非常迅速。

01 物化视图和普通视图有什么区别?

一般而言,这里会有两种视图:普通视图和物化视图。VoltDB只使用物化视图,因此我们倾向于将它们简称为“视图”。
“普通”视图是假装是数据表的SELECT查询语句。在大多数情况下,视图是只读的,这点很重要,因为涉及多个表的SELECT查询语句可能会非常复杂,而物化视图意味着该复杂逻辑只需要执行一次,然后就可以在应用程序中多次反复使用。
物化视图就像“普通”视图,不过它缓存了结果。

02 为什么早期的“普通”视图存在各种问题?

起初普通视图很流行,但随着时间的流逝逐渐走下神坛。大多数人想要的视图包含诸如GROUP BY和SUM()之类的功能,这意味着该视图每次访问时都必须遍历大量数据。
因此,如果我有一个视图计算了“每个客户的销售额”,并且想知道客户“x”的销售额,那么当访问该视图时,需要计算所有客户的销售额,然后过滤出客户为”x”的那一行。
这显然会很慢,因此一些数据库开发商试图通过使用巧妙的查询优化器来解决此问题,这些优化器需要将视图的SQL与要运行的查询合并,人们开始怀疑视图的麻烦超过了它们带来的价值。

03 为什么物化视图做起来不容易?

由于用户希望使用GROUP BY和SUM()做为视图,但是“普通”视图很慢,因此明显的解决方法是缓存视图的SELECT语句的结果,这就是实例化视图的来源。
但是,在老的RDBMS中,普通视图到物化视图,带来了问题是基础表上的UPDATE和INSERT性能却很差。
为什么?我们想象一下,有一个3亿行的表,该表跟踪某人驾驶的汽车的颜色。在该表中插入一行相对容易,因为我们每个人只有一行,因此不太可能发生冲突。但是,如果我们向GROUP BY ‘car_color’添加物化视图,我们突然发现我们的插入内容无法完成,直到它们排队将物化视图中的行更新为’红色’,'蓝色’或任何其他的什么颜色,之后才可以完成数据的插入。
这种开销在单节点系统中是不好的,而在集群环境中则更是灾难性的,因为“蓝色”的行可能在不同的服务器上,让问题从单行插入变成了分布式环境下的复杂的两阶段提交事务。

04 为什么VoltDB的物化视图的性能表现很好?

VoltDB是一个真正快速的OLTP系统,我们希望它同时可以完成物化视图中的聚合计算。想要理解这一点,需要您更深入地了解VoltDB的体系结构,但是对于本篇文章而言,您需要了解的是:
VoltDB按CPU内核水平划分数据,并为每个CPU内核有一个专用线程,该线程按顺序处理所有请求。当我们尝试更新实例化视图总计时,这避免了分布式的竞争操作。

VoltDB中的物化视图是随着事务发生而自动更新的表。

在处理每个请求时,VoltDB识别对其物化视图的本地节点部分做出必要更改。如果该视图称为SALES_BY_CUSTOMER,则每个分区将具有其自己的子集,并且当 “SALES”表发生更新时,数据行插入SALES表的同时也会更新物化视图。

更新每个事务的物化视图听起来应该很慢,但实际上每个视图将每个事务的SQL速度降低仅仅在1-5%。

如果更新表“X”且其视图为“Y”,则对“X”和“Y”的更改是属于同一个事务。VoltDB中的物化视图无需重新计算,并始终反映当前最新的数据状态。

因此,在VoltDB中读取实例化视图的成本几乎与读取表的成本相同:<1毫秒。

如你所见,物化视图的实现并不是一件容易的事,但是如果能做好,它们可以提供对聚合数据的真正即时可视性,上层应用开发工作将变得轻松很多。
现在让我们进入VoltDB的物化视图应用场景。

4.1 物化视图:在实时数据流中的聚合场景

一种常见的模式是使用实例化视图按秒聚合高速事件流,然后使用SQL查询按分钟,小时或其他单位进一步聚合。这样可以快速聚合不同粒度级别的时间序列数据。
这是从VoltDB下载工具包中包含的时间window应用程序中的示例,该应用程序根据用户指定的时间范围计算平均值:

CREATE TABLE timedata
(
uuid VARCHAR(36) NOT NULL,  val BIGINT NOT NULL, 
 update_ts TIMESTAMP NOT NULL 
); 
CREATE VIEW agg_by_second 
( 
 second_ts, 
 count_values, 
 sum_values 
) 
AS SELECT TRUNCATE(SECOND, update_ts), COUNT(*), SUM(val) 
 FROM timedata 
 GROUP BY TRUNCATE(SECOND, update_ts); 
— Find the average value over all tuples across all partitions for the last 
— N seconds, where N is a parameter the user supplies. 
CREATE PROCEDURE Average AS 
 SELECT SUM(sum_values) / SUM(count_values) 
 FROM agg_by_second 
 WHERE second_ts >= TO_TIMESTAMP(SECOND, SINCE_EPOCH(SECOND, NOW) – ?);

VoltDB SQL支持将时间戳转换为SECOND,MINUTE,HOUR,DAY,DAYOFMONTH,DAYOFYEAR,MONTH,WEEK,WEEKOFYEAR,WEEKDAY和YEAR的功能,所有这些都可以与视图定义和查询混合并匹配。

4.2物化视图:横向扩展性场景

由于物化视图的大小与VoltDB的其余部分一样,因此可以在快速传入的写入/更新工作负载中分配汇总计算的成本,并使快速读取状态汇总的成本非常低廉。
在上面的示例中,如果以15k / sec的速率插入元组并且有四个分区,那么要计算最后十秒的平均值,VoltDB将需要扫描15万行。使用实例化视图时,它需要每秒扫描1行乘以分区数,即40行。以秒为单位预先汇总表的总和和计数具有巨大的优势。

05 如何结合流式数据分析与单个事件决策?

由于物化视图始终是最新的,在事务上是一致的并且可以通过标准SQL访问,因此在运行事务时,可以轻松使用视图中维护的聚合。
我们的选民示例是这样做:该示例维护一个物化视图,视图是按电话号码对传入记录进行分组,强制每个电话号码投票限制的事务使用该视图查找传入电话号码注册的先前呼叫。一个更复杂的示例可以使用上面讨论的时间序列函数来强制执行每分钟最大访问量,以实现高速配额执行。
现在,让我们使用表决器示例来展示在可伸缩摄取工作负载中分摊聚合成本的运行时优势。该示例包括一个热图,该热图绘制了在每个州中哪个参赛者领导投票的图表。当然,查询该热图的数据需要按州计数选票。
使用实例化视图,这可以在亚毫秒级的时间内完成(它需要为六个参赛者的每个分区读取50行)。反之,如果没有实体化视图,则必须扫描所有投票,并且需要花费大量执行时间(并且需要读取数百万行)。
以下是一些具体数字,它表示显示在最新的Intel Core i7 CPU上每个分区大约10GB数据(约1.3亿行)的执行时间(没有物化视图,将扫描所有1.3亿行,执行时间约为8秒):

26> select state, count(*) from votes where state=’MA’ group by state; 
STATE C2 
—— ——– 
MA 3,839,860 
(Returned 1 rows in 8.13s) 
With the view execution time is sub-millisecond: 
31> select state, sum(num_votes) from v_votes_by_contestant_number_state where state 
= ‘MA’ group by state; 
STATE C2 
—— ——– 
MA 3,839,860 
(Returned 1 rows in 0.00s)

视图的实现对数据摄取的工作量有一定的影响,但是即使使用物化视图,数据库每个内核每秒也可以处理超过4万个请求。视图开销可以通过水平可伸缩性来补偿—所需的工作可以跨内核和集群中的服务器扩展。这种权衡使得可以在高吞吐量的每个事件事务中使用实时聚合。
v_votes_by_contestant_number_state视图显示1个分区吞吐量:

Throughput 42252/s, Aborts/Failures 0/0 
Throughput 45407/s, Aborts/Failures 0/0 
Throughput 46352/s, Aborts/Failures 0/0 
Throughput 44341/s, Aborts/Failures 0/0

1个没有v_votes_by_contestant_number_state_view的分区吞吐量:

Throughput 50525/s, Aborts/Failures 0/0
Throughput 52503/s, Aborts/Failures 0/0
Throughput 50961/s, Aborts/Failures 0/0
Throughput 51190/s, Aborts/Failures 0/0

06 总结

VoltDB是一个内存ACID向外扩展SQL数据库。它每秒可以处理数万至数百万个事务,还能够针对高速数据流进行实时SQL分析。
VoltDB实时分析功能的重要部分围绕其物化视图实现而构建。VoltDB的物化视图支持实时汇总和摘要,并支持将实时分析与按事件决策结合在一起。

如果您对VoltDB的工业物联网大数据低延迟方案、全生命周期的实时数据平台管理等感兴趣,欢迎私信,进入到我们的官方交流群。

上一篇:聊聊 Java String 源码的排序算法


下一篇:Java中“==”、“compareTo()”和“equals()”的区别