基于 Impala 的高性能数仓实践之物化视图服务
本文将主要介绍 NDH Impala 的物化视图实现。
接上篇,前两篇分别讲了执行引擎和虚拟数仓,它们是让一个 SQL 又快又好地执行的关键。但如果某些 SQL 过于复杂,比如多张大表进行 Join 并有大量的聚合类操作,那么再优秀的执行引擎也无法保证能够秒级执行完成,虚拟数仓的弹性扩展能力也很难及时跟上,这正是物化视图能够发挥作用的场景。
1 物化视图简介
在计算机领域,物化视图是一个数据库对象,结构化保存了一个 SQL 查询的结果集,创建物化视图的过程可称为物化,是预计算的一种形式。物化视图是一个比较宽泛的概念,可能是远端数据的一份本地拷贝,也可能是一个表或多表 Join 后某些行 / 列的子集,也可以是将聚合函数作用到表或 Join 结果后的汇总类信息。
1.1 物化视图分类
根据物化视图中原始数据表的个数,可分为单表和多表物化视图;根据物化视图 SQL 中是否存在聚合类操作,可分为明细和聚合物化视图;根据物化视图的结果集在更新时是否需要全量刷新,可将其分为全量和增量物化视图,后者又可称为分区物化视图,增量数据写入新分区中。
1.2 特点与作用
普通视图仅表示一个 SQL 语句,是个逻辑概念,而物化视图则有实体对象 / 表与之对应。这样在执行查询时,就可以通过查询对应的物化视图表数据,从而快速得到查询结果。物化视图使用查询重写机制,当执行查询时,引擎自动选择合适的物化视图进行查询重写,完全对应用透明。
一般用于进行性能加速,具有比较广泛的使用场景,如果业务查询存在如下特征,即可尝试用物化视图进行加速:比如查询耗时较多且查询频次较高、相同或相似的查询多并发或周期性发生、业务对查询结果的数据实时性没有过于严格的要求,允许分钟或小时级的延迟等。具体业务方面,在数仓领域,T+1 类 BI 报表是典型的可以通过物化视图进行优化查询的场景。
1.3 使用效果评估
物化视图使用得当能够数倍,甚至数十倍提升查询性能,但其也不是万能的,如果不分场景盲目创建物化视图,其结果可能适得其反。一方面是因为物化视图的创建和更新有成本,另一方面,判定 SQL 是否命中的逻辑也在性能敏感的代码路径上引入了额外的耗时。
笔者认为,使用物化视图的效果评价需要考虑 2 个方面:首先,其是否带来了查询的性能提升。这是最基本的目标;其次,是否降低了集群总的资源消耗,包括计算资源和存储资源。这是进阶目标,在每个物化视图都有大量的查询命中时,将会明显减少每个查询对 CPU 和内存资源的需求,同时也不再需要访问原始表,减少 IO 资源消耗。
1.4 使用现状
物化视图是进行查询性能优化的重要手段,传统的商业数据库,比如 Oracle、IBM 和 SQL Server,以及目前商业数仓系统,比如 Snowflake、AnalyticDB 等,均具有强大的物化视图能力。
目前开源的数仓系统,在物化视图支持程度上相对不足,Doris、Clickhouse、Druid 等暂时仅支持单表聚合类物化视图(或称为聚合索引),也有数仓引擎已经在开发多表物化视图,比如 Meta 的 PrestoDB 等。网易 NDH Impala 在物化视图能力建设上投入较早,目前已在生产环境上规模使用,本章的后续小节主要介绍 NDH Impala 的物化视图实现和实践。
2 Impala 物化视图
2.1 设计架构
在设计 Impala 物化视图系统时,秉持了尽可能通用的原则,希望其不仅仅能够用于改写 Impala 查询 SQL,未来能够方便的集成到其他的查询引擎上。下图所示为物化视图设计架构图,包括独立的物化视图服务、嵌入在 Impala Coordinator 中的物化视图改写模块和用于优化物化视图的 Impala 管理服务器。
物化视图服务提供了多种物化视图管理的 API,包括视图的创建、定义更新、数据更新 / 同步、启用 / 禁用、视图信息展示、视图使用统计和视图删除等。Impala 管理服务器保存的历史查询信息为物化视图服务的视图创建、定义更新和视图管理提供数据支撑。物化视图服务使用 MySQL 作为元数据库,包括了视图定义、状态和使用统计等信息。Impala Coordinator 节点集成了物化视图透明改写模块,用于判断查询 SQL 是否能够命中某个 / 某些物化视图,若命中,则对其进行透明改写。
2.2 视图创建
NDH Impala 提供两种物化视图创建模式,分别适用于网易数帆的有数 BI 场景和通用场景,两者均需使用 NDH Impala 专有的管理服务器,通过历史查询信息优化来物化视图定义。前者基于有数 BI 的数据模型和图表历史查询行为来生成物化视图 SQL,后者则是通过分析历史查询信息来提取 SQL 模板信息,为满足要求的 SQL 模板创建物化视图。下面主要介绍有数 BI 场景下的视图创建,包括预检查、物化视图 SQL 优化和更新。
预检查
预检查用于在物化视图创建之前,提前剔除掉不合理或暂不支持的创建请求。主要有以下几种情况:
(1)判断是否为实时或准实时表。Impala 物化视图服务暂不支持实时场景,所以,如果待优化的 BI 数据模型或 SQL 模板中存在 Kudu 表或 Arctic/Iceberg 表,则拒绝创建请求。
(2)判断是否能够进行模糊匹配。主要包括聚合或多表关联时条件自由筛选场景,举多表外关联为例,假设表 t1 和 t2 均为分区表,分区字段均为 dt,但两表并没有通过 dt 字段进行关联,用户在查询时会灵活地为 t1 和 t2 进行 dt 分区各取不同的值 / 范围,由于外连接的非关联字段过滤在关联前和关联后进行是不等价的,因此无法模糊匹配。对于聚合场景,若物化视图对表 user 的 age 列取平均值,若查询对 user 进行了筛选,显然 age 列的平均值会失效。
(3)判断是否适合用物化视图进行优化。引起查询慢的原因很多,物化视图适合对查询本身慢进行优化,对于偶发或波动类的因素,其优化效果并不明显。这些因素包括存储层性能抖动(比如 HDFS 的 DN 或 NN 性能波动)、HMS 元数据加载(未命中 catalog 缓存导致查询时需即时加载)、排队执行(查询并发超阈值或内存资源不够)、集群网络抖动(网络拥塞等)。物化视图服务通过从 Impala 管理服务器获取该数据模型或 SQL 模板的历史查询 profile 信息来进行前述因素的判定。
对于上述情况,物化视图创建请求直接拒绝,并返回拒绝的原因。
SQL 优化
若是 BI 的数据模型场景,对通过了预检查的创建请求,还需要对其传入的 “裸” 物化视图 SQL(模型 SQL)进行优化。我们先说原因,再说如何进行优化。
为什么要进行物化视图 SQL 优化?本质上说是为了提升前述的物化视图使用效果。从实践经验看,数据模型 SQL 中可能涉及 TB 甚至 PB 级别的大表,这些表一般是按日期,比如天进行分区,模型中会对多个分区表按分区字段进行关联。大部分情况下,在数据模型层面并没有约束大表的分区范围,也就是说,如果完全按数据模型 SQL 进行物化视图构建,所需消耗的计算和存储资源可能是单个图表查询的数十、上百倍甚至更高倍数。为了提升数个图表查询的性能而消耗过多的资源显然是不可取的。而现实情况是,图表查询中的分区范围往往是有明显的规律性的,一般集中在最近一个季度,大部分都是一个星期内的时间,如下图所示:
本着最优的投入产出比,我们就可以基于以往的查询规律,将物化视图的作用域缩短到某个分区范围内,比如昨天、最近一周、最近一个月等等。
那么具体是如何做的呢?NDH Impala 做的很多优化都用到了 Impala 查询 Profile,在实现物化视图系统时也不例外。在进行物化视图 SQL 优化时,会从历史查询中筛选出属于该数据模型的所有图表查询 SQL,分析其 Profile 信息,进一步过滤出待优化的 SQL 集合。这些 SQL 一般满足如下一个或多个条件:查询耗时超过阈值(已排除队列等待、元数据加载等因素)、消耗的内存资源超过阈值、扫描数据量超阈值、扫描的行数超阈值等。
经过前述条件筛选后,还可根据图表查询的触发类型等维度区分优化的优先级,比如用户触发的图表查询优先级高于 BI 后台进行结果预缓存的查询。
对于筛选出来的 SQL 集合,逐个解析 SQL 语句,提取各事实和维度表的分区范围,并按大小进行排序。默认取最大的分区范围作为物化视图的有效范围,若该范围过大,则根据配置的参数选择更加合适的分区范围,比如选择 90% 以上查询都在其中的范围值。
多物化视图
基于上面的优化逻辑,对一个数据模型,会创建至少一个物化视图,但也可能创建多个物化视图,这主要跟用户在该数据模型上创建的图表特点有关,比如,往往需要给列表筛选器单独创建物化视图。
先解释下什么是列表筛选器。下图是用于展示 Impala 慢查询的图表,可分为红框和蓝框两部分,其中红框用于进行过滤条件筛选,筛选的结果在蓝框中展示。红框中有倒三角下拉标记的这些控件就是列表筛选器。
以 hostname 筛选器为例,其用于查看某几个特定 Coordinator 上的慢查询。要对其进行筛选,首先就需要获取可选的 Coordinator 节点,这就需要执行一条查询 SQL:对存放 Impala 历史查询信息的 basic_info 表的 hostname 列全表扫描并 group by 取结果。由于默认是不带分区过滤的全表扫描,因此无法命中物化视图,且由于全表扫描,查询性能较差,因此会专门为没有分区过滤条件的一个或多个列表筛选器创建物化视图。
分区物化视图
为了能够提高物化视图的数据更新效率,在创建物化视图时会判断是否能够对物化视图数据进行增量更新。物化视图服务通过解析物化视图 SQL,判断各表是否用分区字段进行关联,若是,则可以创建分区物化视图,在进行数据更新时,只需要处理前一天新产出的数据。
SQL 更新
SQL 更新指的是物化视图创建后,在使用过程中,根据用户 / 业务查询行为的变化,动态调整物化视图 SQL。可以通过物化视图的查询命中率来评估是否需要调整。这里所说的命中率是指同个数据模型下的图表查询,能够命中物化视图的百分比。
假设某个数据模型的物化视图,创建后的命中率一直高于 80%,若最近几天命中率下降到 50% 以下,那么可能有两种情况导致,一种是数据模型被修改,另一种是图表或筛选条件发生变化。对于前一种情况,有数 BI 产品会通过服务视图服务提供的接口自动触发物化视图 SQL 更新。
对于后一种情况,需要物化视图服务通过后台的分析线程周期性分析查询命中率,在命中率明显变差后,触发物化视图 SQL 更新。
2.3 视图使用
判断一个查询能否命中某个物化视图并在命中后自动改写使其查询物化视图表,这是个数据库领域十分有技术含量的工作,有多篇顶级论文提出了不同的解法。在 Impala 物化视图服务中,命中判断和透明改写的核心是基于 Apache Calcite 提供的两种算法 [1]。但光靠 Calcite 现有的能力还不足以满足线上规模使用的要求,Impala 物化视图服务在此基础上做了大量的优化。本小节先简述 Calcite 改写实现,再介绍所做的增强。
Calcite 透明改写能力
总的来说,目前有三种物化视图改写实现,分别是基于语法、基于规则和基于结构进行查询命中判断并改写。其中,基于语法的改写方式通过文本匹配或者语法匹配实现,将查询的文本与物化视图的文本或语法树进行比较,完全匹配即可进行改写。该方式实现难度低、改写效率最高,但只能匹配完整的查询语句或子查询,适用范围太小。
Apache Calcite 实现了基于规则和基于结构的物化视图改写方式,详见 “Substitution via rules transformation”[2] 和 “Rewriting using plan structural information”[3]。
基于规则的改写方式使用范围较广,可以对大量重写,功能实现也较为简单,改写匹配速度快,其局限是改写依赖转换规则来寻找等价关系,因此需要穷举所有可能的转换关系来实现复杂视图的重写。一些复杂的视图不可能穷举所有的等价关系,例如存在很多的 Join 联接或者复杂的 Project 关系,基于规则的改写适用的范围取决于规则的数量。
基于结构的改写方式由微软在 2001 年 SIGMOD 论文《Optimizing queries using materialized views: A practical, scalable solution》[4] 系统化的提出。其将查询表示为 SPJG 标准形式 (Join-Select-Project-GroupBy),提取查询中的 Join,Projects,Filters,Grouping 和 Aggregations 五种表达式,运用一系列的步骤分别与物化视图对应的表达式进行匹配并得到补偿表达式。
基于结构的改写相比基于规则的方式更容易扩展,使用范围更广,不足之处在于搜索成本较高。在实践中发现,使用后者对复杂 SQL(比如存在 10 + 以上表关联)进行改写时,其性能远不如前者,尤其是该查询 SQL 还命中了多个物化视图时,可能无法达到优化查询性能的目的,甚至适得其反。因此,Impala 物化视图服务会同时使用基于规则和基于结构的改写实现,并使用前者作为默认的改写方式。
改写能力优化
为了增强改写能力,提高改写性能,Impala 物化视图服务对改写方式进行了优化,主要包括元数据缓存、命中预判定、支持更多 SQL 语法和改写校验等,如下图所示。
元数据缓存
前文的物化视图服务架构图提到物化视图信息(既物化视图元数据)是持久化在 MySQL 中的,而对用户查询进行透明改写需要用到物化视图元数据,显然,每次进行改写时都从 MySQL 加载元数据会对改写性能造成很大影响,因此,需要在 Impala Coordinator 缓存物化视图元数据信息。
进一步,原生的 Calcite 改写框架在执行改写前,需要基于物化视图元数据信息(MaterializationActor.Materialization)动态生成物化视图改写对象(RelOptMaterialization),这是一个比较耗时的过程,假设某个用户查询有 10 个候选物化视图,仅构造物化视图改写对象可能就需要耗费数百毫秒甚至数秒时间。为了降低改写过程的耗时,需在 Coordinator 提前构造 RelOptMaterialization 并将其缓存。
为了确保缓存的元数据是最新的,Coordinator 会每隔数秒(可配置)从 MySQL 获取新增或更新过的物化视图信息,构建 MaterializationActor.Materialization 和 RelOptMaterialization 对象并替换原有的缓存对象。
命中预判定
Calcite 物化视图改写过程是串行的,也就是系统会逐一判断每个候选的物化视图是否能够成功改写物化视图。元数据缓存能够缩短每次判断的时间,但无法减少判断次数。为此,我们加入了命中预判定来减少候选的物化视图个数。
目前采用的命中预判定策略类似上述的基于语法的改写实现,如果查询 SQL 和物化视图 SQL 一样,无需走命中判定和改写,直接返回改写后 SQL。更通用的方法是根据 SQL 语句自身的一些特点,设置一些规则,提前过滤掉部分不可能用于改写的物化视图。这些规则包括但不限于:
- 判断满足物化视图中涉及的表均在查询 SQL 中
- 判断满足查询返回的 selectList 属于物化视图 selectList 的子集
- 若查询 SQL 存在 Sort 算子(定义见下文),判断是否满足 Sort 算子的校验
- 若匹配的物化视图对象数量仍超过阈值,再通过归一化 SQL 进行匹配筛选
通过以上预判定,可以大大减少参与改写的物化视图数量,提高改写效率。而且,可以提前识别不可能命中物化视图的查询 SQL,减少无效的改写行为。
语法 / 算子支持
Calcite 虽提供了基于规则和结构的改写方式,但在每种实现中,支持的语法和算子还不够多,比如不支持改写带外连接(outer join)的查询,不支持分组和排序相关的算子等。此外,Impala 上大量的内置 UDF 函数显然也是不支持的。这些都严重限制了物化视图在业务场景中的使用,均需要我们进行增强。
Calcite 不支持多表外连接场景,我们通过二次开发,在确保改写结果正确性的基础上,支持以下情况的物化视图改写:
除上图之外,从表(左连接时为右表,右连接时为左表)子查询过滤有个特殊情况可以允许改写,即当从表子查询过滤条件全为关联(on)字段且这些字段过滤条件和主表过滤一致。
Calcite 也还未支持查询中包含 Sort 算子(包括排序、分页,即 order by/limit/offset)的场景,但目前 NDH Impala 支撑的业务查询,绝大部分都带有 Sort 算子。我们增加了对包含 Sort 算子的查询进行改写的支持。实现上,目前是通过改写前移除查询 SQL 中的 Sort 算子,改写后校验补偿 Sort 算子的方案来实现。
带 Group 算子的查询存在改写后丢失 Group 算子的问题,而 NDH Impala 支撑的业务查询中带有 Group 算子的查询比例较高。因此,对 Group 算子改写也进行了完善,补偿方式上与 Sort 算子相似。
改写校验和回退
为了确保经过 Calcite 改写的 SQL 是正确的,需对改写后 SQL 进行校验,比如确认是否包含物化视图表。通过验证后,会增加 SQL 注解,用于标识该 SQL 非原始 SQL。若改写结果校验不通过,或改写过程中出现任何异常,或改写后 SQL 在 Impala 进行解析和鉴权等处理时出现任何异常,都会替换回原始查询 SQL,确保查询正常执行。
2.4 数据更新
与基于 Calcite 的透明改写模块不同,NDH Impala 物化视图系统的数据更新模块是全新开发的,提供了多种更新方法,处理了更新失败场景并设计了可扩展的元数据更新方案。
更新方法
物化视图表的数据在创建后不能一成不变,当原始表有新数据写入或对历史数据进行更新后,需要尽快更新物化视图表数据,确保命中物化视图后查到最新的结果。对于像 Impala 这样的存算分离查询引擎,数据的写入和更新一般基于 Hive 或 Spark,并不能天然地感知到表中数据的变化,在我们的方案中,提供了四种数据更新的方法,分别是依托有数大数据平台的离线数据产出日志、依托 NDH 的 Hive Metastore(HMS) DDL 日志、兜底的定期检查原始表的数据文件修改时间,最后还有手动更新。
有数大数据平台的数据血缘模块能够根据数据产出的上下游关系,推送离线数据产出日志,物化视图服务订阅产出消息,将其持久化到 MySQL 的产出日志表中,再由更新线程消费这些产出日志,驱动包含该原始表的一或多个物化视图进行数据更新。若原始表没有配置数据血缘,通过 NDH HMS 组件的 DDL 操作日志来驱动。相对来说,基于产出日志的更新效率更高。
如果表没有数据血缘,数据写入和更新也不走 HMS,那么上述两种更新策略均失效。此时,需为物化视图配置数据更新时间窗,更新线程在物化视图进入时间窗后会定时检查物化视图中各原始表数据目录的数据文件,检查修改时间,若大于上次物化视图更新时间,这需驱动物化视图更新。
更新失败处理
若由于某种原因,物化视图更新失败,则需要进行重试,若仍然失败,则需要将该物化视图失效掉,禁止查询 SQL 命中该物化视图。之后在由管理员介入进行问题排查。
元数据更新
前文提到的物化视图的创建、定义更新、数据更新和禁用等行为,均需要创建或更新物化视图元数据。尤其是数据更新时,还需要修改物化视图用于进行命中判定的物化视图 SQL。下面举个简单的例子:
SELECT XXXXXX
FROM `music_db`.`xxxx` AS `t1`
INNER JOIN `music_iplay`.`xx` AS `t2` ON CAST(`t1`.`userid` AS VARCHAR(255)) = `t2`.`user_id`
LEFT JOIN `music_iplay`.`xxx` AS `t3` ON `t1`.`day` = `t3`.`dt` AND `t1`.`unionid` = `t3`.`union_id`
LEFT JOIN `music_iplay`.`xxxxxxx` AS `t4` ON `t2`.`live_type` = `t4`.`live_type` AND `t2`.`dt` = `t4`.`dt` AND `t2`.`user_id` = CAST(`t4`.`user_id` AS VARCHAR(255))
WHERE `t1`.`day` = partitionColumn_1
上图是一个物化视图的定义 SQL,有 4 个原始表,分别进行内连接和左连接,其中 t1 表为 T+1 分区表,分区字段 day。“partitionColumn_1” 是数据更新站位标志,表示查询 t1 表的最近分区(昨天)。与之对应,物化视图改写 SQL 为(假设今天为 2022-8-11):
SELECT XXXXXX
FROM `music_db`.`xxxx` AS `t1`
INNER JOIN `music_iplay`.`xx` AS `t2` ON CAST(`t1`.`userid` AS VARCHAR(255)) = `t2`.`user_id`
LEFT JOIN `music_iplay`.`xxx` AS `t3` ON `t1`.`day` = `t3`.`dt` AND `t1`.`unionid` = `t3`.`union_id`
LEFT JOIN `music_iplay`.`xxxxxxx` AS `t4` ON `t2`.`live_type` = `t4`.`live_type` AND `t2`.`dt` = `t4`.`dt` AND `t2`.`user_id` = CAST(`t4`.`user_id` AS VARCHAR(255))
WHERE `t1`.`day` = '2022-08-10'
到了第二天(2022-8-12),t1 表的 2022-8-11 号分区数据产出,物化视图完成数据更新后,需要更新改写 SQL 的 WHERE 条件(WHERE t1.day = '2022-08-11')并持久化到 MySQL 中。
SELECT XXXXXX
FROM `music_db`.`xxxx` AS `t1`
INNER JOIN `music_iplay`.`xx` AS `t2` ON CAST(`t1`.`userid` AS VARCHAR(255)) = `t2`.`user_id`
LEFT JOIN `music_iplay`.`xxx` AS `t3` ON `t1`.`day` = `t3`.`dt` AND `t1`.`unionid` = `t3`.`union_id`
LEFT JOIN `music_iplay`.`xxxxxxx` AS `t4` ON `t2`.`live_type` = `t4`.`live_type` AND `t2`.`dt` = `t4`.`dt` AND `t2`.`user_id` = CAST(`t4`.`user_id` AS VARCHAR(255))
WHERE `t1`.`day` = '2022-08-11'存放物化视图元数据的MySQL表中包含createTime和updateTime两列,分别表示物化视图元数据的创建和最后一次更新时间,Impala集群各Coordinator通过这两个字段即可获取新增或修改过的元数据,更新Coordinator内存中的物化视图缓存,确保进行查询命中判定和改写时使用的是最新的元数据。
2.5 视图管理
Impala 物化视图提供了管理页面,能够进行视图信息展示,使用统计并进行一些常用的视图操作。如下图示例:
通过管理页面可以看到每个视图的基本信息,包括创建和最近更新时间、视图是否启用、视图表的记录数和数据量等。通过详情页能进一步查看视图定义,包括建表 SQL、更新 SQL 和改写 SQL 等。管理页面还提供物化视图的命中明细信息,可以查看每个物化视图命中了哪些用户查询,展示那些查询的基本信息等,如下所示:
通过物化视图命中率统计还能够查看视图的使用效果,如下所示:
通过管理页面提供的信息,用户能够删除一些性价比较低的物化视图,如近期不再有查询命中,或者消耗资源过多等。
就目前来说,Impala 物化视图服务更多还是聚焦在优化和增强核心模块,包括视图创建、改写和更新的效率。在管理页面上提供的信息还比较有限。
3 实践和总结
Impala 物化视图服务上半年在网易云音乐的 BI 重点报告场景规模化落地使用,明显改善了在 BI 报表预缓存未命中场景下的报表查看性能体验。在落地过程中,也遇到了不少问题。
3.1 问题和挑战
遇到的问题是多方面的,在技术上主要集中在视图的命中判定和改写、视图数据更新上。经过充分调研和对比,确定前者基于 Calcite 快速获得能力,避免重复造车。后者由于平台依赖性较强,采用全自研的方式。
更多的困难来自于落地使用。在刚开始落地使用时,遇到直接基于 BI 数据模型创建的物化视图性价比较低问题(创建和更新物化视图所需资源过多,带来的查询性能提升不足以抵消增加的成本),问题原因主要是模型上没有设置分区表的分区过滤条件,比如 t1 表保存了近 3 年的数据分区,但报表上用到的 t1 数据仅为最近 1 个月。由于模型上未对分区进行筛选,导致直接基于模型 SQL 创建物化视图时会全量物化近 3 年所有的数据,其资源浪费不言而喻。在此情况下,我们引入了视图创建审核机制,先确认模型 SQL 的合理性,若未带分区约束,则通过 Impala 管理服务器保存的历史查询来确定所需的分区范围,物化视图使用的性价比明显提升。
但审核需要人工查看每个数据模型下发的 SQL,统计查询的分区区间,性价比虽提升了,但人力投入增加了,因此又逐步开发了自动进行历史查询的分区范围统计的能力,提高创建效率。引入历史查询来确定分区范围提高了性价比,但用户的查询行为不会一成不变,因此,又有了周期性的重新获取分区范围的思路,并逐步提高自动更新能力。
3.2 使用思考
物化视图是一种有效的性能提升手段,无需赘言,但从实践来看,物化视图具有较高的使用门槛,尤其是通过多表连接的物化视图,往往需要有数据库背景的研发或运维才能用好。原因如下:首先,物化视图通过 SQL 来表示定义,这对于不熟悉 SQL 的用户不友好;其次,只有充分了解用户具体会执行哪些 SQL,创建的物化视图才能有足够高的查询命中率;最后,只有不断识别用户查询 SQL 的条件变化,才能使物化视图维持较高的查询命中率。
在大数据场景,如果数据分析师具备较强的 SQL 能力,那么物化视图可作为一种轻量级 ETL 手段,用来快速进行查询性能加速。这个主要是从敏捷和灵活性角度出发来考虑的,假设数据分析师直接通过多表连接进行自助分析和 Ad-Hoc 查询,由于表的数据量大或表关联多,可能性能会比较差,影响数据分析效率。若分析师向数据加工团队提需求建个新表用于分析查询,这需要新增一个 ETL 任务,数据加工团队可能需要排期来做,从提出需求到新表就绪可能需要数个工作日甚至数周时间,若查询的需求多而易变,那对数据加工团队是个不小的负担。这种情况下,若数据分析师具备较强的 SQL 能力,可以发挥物化视图服务的灵活性,无需数据加工团队介入,新表(物化视图表)的就绪时间也可以大大缩短。
但在更普遍的情况下,分析师对 SQL 较为陌生,比如使用 BI 产品进行数据分析时,传统的物化视图技术并不适合。要用好物化视图,需要在数据库 / 数据仓库和最终用户之间有个中间的沟通角色,对于传统的关系型数据库,这个角色就是数据库管理员(DBA),其职能包括审核业务的表结构、分析慢 SQL、巡检数据库服务日志和数据库性能调优等等,DBA 掌握了使用物化视图进行查询性能优化所需的信息。在大数据分析领域往往存在 DBA 角色缺失的情况,客观原因很多,不是本文讨论重点,在此不展开。
在这样的背景下,为了发挥物化视图的能力,需要对其进行一些变革。路径很多,其中就包括让物化视图更加智能,知道应该创建什么样的物化视图,知道什么时候应该及时调整物化视图,这正是我们在物化视图系统实现后期做的尝试。但这条路肯定是漫长的,如何尽可能避免对具体业务场景的依赖还需不断探索。比如 Impala 物化视图服务为有数 BI 产品做的智能化方案,并不一定在其他产品上有同样的效果。另一种变革的方式是从产品侧驱动,比如有数 BI 产品提供物化视图能力,优势包括相比数仓系统更了解用户(报表)查询行为、能够用比 SQL 更直观的方式来表示物化视图、在 BI 系统产生 SQL 前即可完成查询的改写、可以做得更加通用不依赖具体的数据源等。
4 小结
本文先简要介绍了物化视图定义、分类、特点和作用等基本知识,再重点介绍了 NDH 在 Impala 上实现的物化视图系统,包括基于 Apache Calcite 进行二次开发的透明改写能力,基于离线数据产出日志、HMS DDL 日志等方式驱动的物化视图数据自动更新服务,以及视图管理模块。进一步介绍了在网易云音乐场景落地实践及经验教训,思考如何才能高效发挥物化视图的查询性能优化能力。最后需要说明的是,以上均为笔者个人的一些实践和体会,欢迎各位大佬提供意见建议、批评指正。
参考链接:
-
[1] https://calcite.apache.org/docs/materialized_views.html
-
[2] https://calcite.apache.org/docs/materialized_views.html#substitution-via-rules-transformation
-
[3] https://calcite.apache.org/docs/materialized_views.html#rewriting-using-plan-structural-information
-
[4] https://www.cnblogs.com/listenfwind/p/16029792.html
作者简介
荣廷,网易数帆数据库技术专家,10 年 + 数据库相关工作经验,目前主要负责高性能数仓和云原生数据库研发工作。