当前位置: 首页 > news >正文

【跨国数仓迁移最佳实践6】MaxCompute SQL语法及函数功能增强,10万条SQL转写顺利迁移

本系列文章将围绕东南亚头部科技集团的真实迁移历程展开,逐步拆解 BigQuery 迁移至 MaxCompute 过程中的关键挑战与技术创新。本篇为第六篇,MaxCompute SQL语法及函数功能增强。

注:客户背景为东南亚头部科技集团,文中用 GoTerra 表示。

业务背景和痛点

MaxCompute 和BigQuery 都是业界领先的大数据处理平台,而SQL又是用户进行数据分析的主要工具。虽然大数据环境下的SQL语言通常都是基于ANSI SQL标准扩展而来的,但是每一个系统都有自己的方言特色,在一些语言细节和行为上存在着微妙的差别。

GoTerra 业务迁移面临着多方面全方位的挑战,其中最大的挑战之一是10万条SQL的转写问题。这些SQL有一部分复杂度非常高,有一两万行SQL代码,而且其中使用了非常多的比较高级的SQL特性。为了支持SQL转写,我们从多个团队调集了精兵强将,成立了专门的团队进行转换工具的开发工作。但是转换工具并不能解决所有的问题,有一些业务特性的差异必须通过对MaxCompute平台自身能力的增强和调整来实现。

方案概述

为了使用户业务能够平滑地从BigQuery迁移到MaxCompute,首先需要对两者之间的SQL语法差异进行了详细地分析,并基于分析结果进行方案设计。迁移过程中,我们需要重点关注BigQuery中的如下两类特性:

  1. BigQuery独有特性:这是指在启动业务迁移的时候,MaxCompute还没有提供的语法特性或者功能。针对这一类的特性,需要对MaxCompute进行增强,提供和BigQuery类似的业务功能。
  2. 两个平台都提供但是具体行为存在差异的那些特性。对于这种情况,MaxCompute为了保持行为兼容,不能直接修改自己的行为。MaxCompute的做法是增加一个odps.sql.bigquery.compatible语法开关,打开这个开关之后的语法行为会尽量和BigQuery保持一致。

在本次GoTerra 搬栈项目中,MaxCompute SQL主要新增了如下功能:

  1. auto partition表
  2. unnest语法
  3. Delta Table能力增强
    a. Merge INTO支持多次update和insert
    b. delete from支持别名
  4. 新增30+内建函数

在bigquery兼容模式下调整如下语法行为以保持和BigQuery一致:

  1. 列别名解析
  2. CTE支持输出同名列
  3. 隐式类型转换规则
  4. 某些类型转换行为
  5. pivot列名生成规则

下文中我们选取部分重点内容进行进一步描述

MaxCompute新增功能

一、Auto partition

MaxCompute 和BigQuery 的分区表概念说明

MaxCompute 和BigQuery 都支持分区表,但是它们的底层设计思路有很大的差异。

MaxCompute关于分区表的底层设计思路和Hive类似。它允许用户根据一个或多个列对表进行划分,从而将表中的数据分散存储在不同的物理位置上。下面是MaxCompute创建分区表的一个例子:

create table foo_table(id bigint) partitioned by (pt string);

假如这个table有两个partition pt='2025-06-29'和pt='2025-06-30',那么这两个partition中的数据,它们的pt列的取值分别为'2025-06-29'和'2025-06-30'。

BigQuery支持时间单位列分区(Time-unit column partitioning)和提取时间分区(Ingestion time partitioning),分区列的数据类型只能是时间类型,包括DATE、TIMESTAMP 或 DATETIME。Time-unit column partitioning表的建表语句如下:

create table my_dataset.foo_table (id int64, d date) 
partition by date_trunc(d, MONTH);

从这个建表语句中可以看到,它的底层逻辑是先对时间列按照指定的函数进行运算,上面的例子是使用date_trunc函数对时间列进行截取操作,截取的粒度是MONTH,然后根据截取的结果来作为分区的依据。也就是说,对于同一个分区中的数据,它们的时间列的具体取值可能是不同的,但是都处于相同的某个时间区间内。

BigQuery在Time-unit column partitioning的基础上,又提供了Ingestion time partitioning功能。表中有一个名字为_PARTITIONTIME的伪列,标记每行数据的提取时间,它会按照建表语句被截取至相应的边界(例如每小时或每天)。建表语句如下:

create table my_dataset.foo_ingestion_hour (a string) 
PARTITION BY TIMESTAMP_TRUNC(_PARTITIONTIME, HOUR);

向Ingestion time partitioning表里插入数据的时候,可以指定伪列_PARTITIONTIME的值,也可以不指定。假如不指定,则系统会根据当前时间来自动填充。

-- 插入数据,不指定伪列_PARTITIONTIME的值,由系统自动填充
insert into my_dataset.foo_ingestion_hour(a) values('hi1');-- 插入数据,指定伪列_PARTITIONTIME的值
insert into my_dataset.foo_ingestion_hour(_PARTITIONTIME, a) 
values (timestamp '2024-11-02 14:00:00', 'hi2');

Auto partition表

MaxCompute通过Auto partition表来实现和BigQuery Time-unit column partitioning类似的功能。在建表语句中,通过如下语法来创建auto partition表:

AUTO PARTITIONED BY (trunc_time(<col>, <datePart>) [as alias])

举例如下:

CREATE TABLE newtable (id INT64, d DATE) 
AUTO PARTITIONED BY (trunc_time(d, 'month') as ds);

上述建表语句生成的table有3列,分别是id, d和ds。ds是一个string类型的伪列(pseduo-column),它对应于对于列d的取值进行trunc_time运算后的取值。

Ingestion time partition表

MaxCompute在auto partition table的基础上来构建ingestion time partition表的能力。建表的时候通过指定tblproperties来标识ingestion time partition表。建表语句示例如下:

-- 指定分区的粒度是hour
create table foo_ingestion_hourly(_partitiontime timestamp_ntz, a string) 
auto partitioned by (trunc_time(_partitiontime, 'hour')) 
tblproperties('ingestion_time_partition'='true');-- 指定分区的粒度是day
create table foo_ingestion_daily(_partitiontime timestamp_ntz, a bigint) 
auto partitioned by (trunc_time(_partitiontime, 'day')) 
tblproperties('ingestion_time_partition'='true');

使用如下方式插入数据:

-- 不指定伪列_PARTITIONTIME,由系统自动生成
insert into foo_ingestion_hourly(a) values('hi1');
insert into foo_ingestion_daily(a) values(100);-- 指定伪列_PARTITIONTIME的值
insert into foo_ingestion_hourly(_PARTITIONTIME, a) values (timestamp_ntz '2024-11-02 14:00:00', 'hi2');
insert into foo_ingestion_daily(_PARTITIONTIME, a) values (timestamp_ntz '2024-11-02 00:00:00', 200);

auto partition表支持分区裁剪

分区表的主要优势在于它可以显著减少扫描的数据量。例如,在查询时如果指定了某个分区的条件,则只需扫描该分区的数据而不是整个表的数据,从而大大加快了查询速度。

为了下面的举例中描述方便,首先假设建表语句为:


create table table_daily(a bigint, ts timestamp) 
auto partitioned by (trunc_time(ts, 'day') as pt);

它支持在如下一些条件下进行分区裁剪:

  1. 使用partition列来进行数据过滤。例如:
    select * from table_daily where pt >= '2024-09-14';
  2. 直接使用时间列来进行数据过滤,例如:
    select * from table_daily where ts between timestamp '2024-09-14 00:00:00' and timestamp '2024-09-15 00:00:00';
  3. 对时间列调用trunc_time函数,并且trunc的粒度(year/month/day/hour)和建表语句对应,支持分区裁剪。例如:
    select * from table_daily where trunc_time(ts, 'day') = '2024-09-14';
  4. 对时间列调用datetrunc函数,并且trunc的粒度(year/month/day/hour)和建表语句对应,支持分区裁剪
    select * from table_daily where datetrunc(ts, 'day') = timestamp '2024-09-14 00:00:00';
  5. 对于其他的时间函数,部分函数支持分区裁剪(具体可以参考后续产品文档的说明),例如:
    select * from table_daily where to_date(ts, 'Asia/Jakarta') > date '2024-03-14'
  6. 假如分区裁剪条件涉及到scalar suBigQueryuery,系统会先计算scalar suBigQueryuery的值,然后根据suBigQueryuery的返回值来进行分区裁剪,例如:
    -- 系统会先计算select max(ts) from other_table的值,根据所得到的结果来对table_daily进行分区裁剪
    select * from table_daily where ts = (select max(ts) from other_table);

二、内建函数能力增强

我们对MaxCompute的内置函数能力进行了扩展,增加新的内建函数,并对已有内建函数的功能进行增强。

时间/时期函数

1. 日期时间构造能力增强,增加新的format,构造的时候允许指定时区信息

  • 新增TO_TIME/TO_TIMESTAMP/TO_TIMESTAMP_NTZ/TIMESTAMP函数
  • 新增TIME_ADD/TIME_SUB/TIME_DIFF/TIME_TRUNC/FORMAT_TIME函数
  • 新增CURRENT_DATE/CURRENT_TIMESTAMP_NTZ/CURRENT_MICROS函数
  • 增强TO_DATE/TO_CHAR函数功能
  • DATETRUNC/TO_CHAR/TO_DATE/TO_TIMESTAMP_NTZ/TO_TIME/TIMESTAMP支持时区参数
  • 增强时区格式

2. 时间函数支持指定更多的处理格式

  • DATETRUNC支持quarter/week(weekday)/isoweek参数
  • DATEDIFF支持week/week(weekday)/isoweek/ff6参数
  • DATEADD支持quarter/week/ff6参数
  • LAST_DAY支持year/isoyear/quarter/month/week/week(weekday)/isoweek参数
  • WEEKOFYEAR支持week(weekday)参数
  • 新增ISOYEAR函数

网络IP数据处理相关函数

  • 新增NET_IP_NET_MASK函数
  • 新增NET_IP_FROM_STRING/NET_SAFE_IP_FROM_STRING函数
  • 新增NET_IP_TO_STRING/NET_IPV4_TO_INT64函数
  • 新增NET_HOST/NET_PUBLIC_SUFFIX/NET_REG_DOMAIN函数

字符串及二进制转换

  • 新增BASE32函数
  • 新增CODEPOINT_ARRAY函数
  • 新增SAFE_CONVERT_BYTES_TO_STRING函数
  • 新增FORMAT_STRING函数
  • 增强REGEXP_EXTRACT函数功能
  • REVERSE函数支持输入binary

正则表达式相关函数

  • 新增REGEXP_CONTAINS函数
  • 增强REGEXP_EXTRACT/REGEXP_EXTRACT_ALL函数功能

Json类型相关函数

  • 新增JSON_STRIP_NULLS函数
  • 增强JSON_EXTRACT函数功能
  • TO_JSON函数对于struct value为NULL时做兼容性处理

聚合函数

  • 新增基于近似聚合算法的HLL++函数HLL_COUNT_INIT/HLL_COUNT_MERGE/HLL_COUNT_MERGE_PARTIAL/HLL_COUNT_EXTRACT
  • 新增PERCENTILE_CONT/PERCENTILE_DISC函数
  • 新增STRING_AGG/ARRAY_AGG函数
  • 新增APPROX_QUANTILES函数

地理函数

  • 新增ST_S2CELLIDFROMPOINT/ST_S2CELLIDNUMFROMPOINT函数
  • ST_UNION函数支持输入array

除了新增函数和增强已有函数来提供和BigQuery相同的计算能力,对于部分具有相近功能的函数,设计了精准的函数转换规则,将两个平台的内建函数进行了一对一映射,确保搬迁后函数行为一致。

三、bigquery兼容模式

通过设置odps.sql.bigquery.compatible的取值,可以调整MaxCompute的行为和BigQuery尽量保持一致,下面举几个例子

对列别名(alias)解析的影响

如下query,MaxCompute默认会报错,因为用户的query中group by a,这里的a是ambiguous,可能是t1.a,也可能是t2.a

--  MaxCompute默认行为
set odps.sql.bigquery.compatible=false;
witht1 as (select 1 a, 2 b),t2 as (select 1 a, 2 b)
select t1.a as a from t1 join t2 on t1.a=t2.a group by a;-- 会报错:
Semantic analysis exception - a is ambiguous, can be both t1.a or t2.a

但是同样的query在BigQuery里可以运行。原因是在select语句中有t1.a as a,也就是说为t1.a分配了一个别名a,导致group by a中的a被解释成了t1.a

-- BigQuery的行为:
witht1 as (select 1 a, 2 b),t2 as (select 1 a, 2 b)
select t1.a as a from t1 join t2 on t1.a=t2.a group by a;-- 输出结果
1

bigquery兼容模式下,上述query也能在MaxCompute运行

-- MaxCompute在bigquery兼容模式下的行为
set odps.sql.bigquery.compatible=true;
witht1 as (select 1 a, 2 b),t2 as (select 1 a, 2 b)
select t1.a as a from t1 join t2 on t1.a=t2.a group by a;-- 输出
+------------+
| a          |
+------------+
| 1          |
+------------+

CTE输出支持同名列

如下query,MaxCompute默认会报错。原因是MaxCompute检查出CTE的输出列中有重名,输出了两个列的列名都是a

--  MaxCompute默认行为
set odps.sql.bigquery.compatible=false;
with t as (select 1 as a, 2 as a, 3 as b)
select b from t;-- 会报错:
Semantic analysis exception - column reference xxx is ambiguous

但是同样的query在BigQuery里可以运行

-- BigQuery的行为:
with t as (select 1 as a, 2 as a, 3 as b)
select b from t;-- 输出结果
3

bigquery兼容模式下,上述query也能在MaxCompute运行

-- MaxCompute在bigquery兼容模式下的行为
set odps.sql.bigquery.compatible=true;
with t as (select 1 as a, 2 as a, 3 as b)
select b from t;-- 输出

隐式类型转换规则

如下query,MaxCompute默认会报错。

--  MaxCompute默认行为
set odps.sql.type.system.odps2=true;
set odps.sql.bigquery.compatible=false;select '1970-1-2' + interval 1 day;
-- 报错,原因是类型不匹配,string类型和interval类型之间不能进行相加操作
Semantic analysis exception - invalid operand type(s) STRING,INTERVAL_DAY_TIME for operator '+'

但是上述query在BigQuery可以运行,它可以把这个query中的string类型隐式类型转换为date类型,相当于select date '1970-1-2' + interval 1 day;

-- BigQuery的行为:
select '1970-1-2' + interval 1 day;-- 输出结果
1970-01-03T00:00:00

bigquery兼容模式下,上述query也能在MaxCompute运行,并且行为和BigQuery保持一致

-- MaxCompute在bigquery兼容模式下的行为
set odps.sql.type.system.odps2=true;
set odps.sql.bigquery.compatible=true;select '1970-1-2' + interval 1 day-- 输出结果
1970-01-03 00:00:00

业务价值

经过前文所描述的SQL语法功能增强之后,在bigquery兼容模式下,MaxCompute的语法特性已经能够非常好的兼容BigQuery。配合转换工具,GoTerra项目组顺利地完成了客户SQL的转写工作,有力地支撑客户的业务从GCP平台迁移到MaxCompute平台。整个业务切换中用户的业务运行平稳,用户体验良好。

迁移到MaxCompute之后,配合MaxCompute平台的其他核心业务特性和性能优化措施,SQL的整体查询性能和效率也有了很大的提高,这进一步体现了MaxCompute平台在语法兼容性、高性能和稳定性等诸多方面的整体优势。

http://www.dtcms.com/a/338343.html

相关文章:

  • 论往返之迴响:时间之织锦与信息之曼舞
  • [激光原理与应用-294]:理论 - 波动光学 - 衍射光学元件(DOE)
  • 如何生成和安全保存私钥?
  • oracle dg duplicate限速
  • 区块链技术原理(16)-以太坊节点与客户端
  • SpringBoot--JWT
  • WPF 打印报告图片大小的自适应(含完整示例与详解)
  • 初识CNN04——经典网络认识
  • 驱动开发系列64 - glCompileShader实现-GLSL 精度优化pass
  • 3.1 结构化输出(大模型的封闭与开放)
  • Windows系统上使用GIT
  • CMake指令:查找文件(find_file)、查找目录(find_path)、查找库文件(find_library)
  • Life:Internship in OnSea Day 57
  • 【Kubernetes】在 K8s 上部署 Prometheus
  • 1-Flask相关知识点
  • 恒创科技:日本服务器 ping 不通?从排查到解决的实用指南
  • 朝阳区24小时图书馆“焕新计划”启幕 文化讲座点亮夜间阅读之光
  • ST05跟踪MRP的运行(MD01)过程
  • 使用chmod 命令修改文件权限
  • 【完整源码+数据集+部署教程】空中目标检测系统源码和数据集:改进yolo11-UniRepLKNetBlock
  • mac 电脑安装类似 nvm 的工具,node 版本管理工具
  • 【机器人-基础知识】ROS2常用命令
  • Vue3 全新特性 defineModel 深度解析
  • CentOS Linux 7 (Core)上部署Oracle 11g、19C RAC详细图文教程
  • 【MySQL】超详细入门学习
  • vue3 + antd modal弹窗拖拽全局封装 使用useDraggable
  • LeetCode100 -- Day1
  • 嵌入式工程师常去的网址
  • 缺陷检测最新综述:针对现实世界工业缺陷检测的综合调查:挑战、方法与展望
  • C++对象的内存布局