简介:GBase UP统一数据平台技术白皮书由南大通用数据技术股份有限公司编写,面向企业数据平台架构师、数据库运维与选型人员,以及关注国产化大数据方案的技术决策者。该白皮书围绕融合GBase 8a MPP、GBase 8s与开源Hadoop生态的统一数据平台展开,解决多源异构数据难以统一管理、OLAP/OLTP/NoSQL三类计算模型各自为政造成的「数据竖井」问题。资源包内含1个pdf文档,大小约892KB,目录结构完整,依次覆盖产品简介与技术特点、混搭集群与最小部署方案、支持的操作系统与硬件环境、技术指标,以及异构引擎透明访问、跨引擎数据交换与关联查询、读写分离、数据生命周期管理、统一用户授权、BLOB on Hadoop、UDF扩展等核心能力,并附ODBC、JDBC、ADO.NET、C API开发接口说明。目前已有103人学习。读者可借此快速掌握该平台的架构全貌与关键机制,为技术选型、方案对比和部署规划提供一手依据。
1. 当六个业务库要拼成一张客户视图,GBase-Up 统一数据平台到底在统一什么
上周接到一个需求:一张客户 360 报表要同时取订单库、会员库、工单系统,外加对象存储里的一份埋点明细,四边的字段口径还各不相同。按老路子走,就是 ETL 把数据抽进数仓,建模、加工、再出报表,新增一个源就要重跑一遍开发链路。GBase-Up 统一数据平台给的是另一条思路:数据先不动,把元数据统一起来,再用一个 SQL 入口把异构源当成同一个库来查,能下推到源端的尽量下推,下推不了的由平台层做联邦计算和结果归并。它真正要解决的不是"再建一个库",而是"让散落在各处的库看起来像一个"。适合两类人:手里已经有 GBase 8a/8s/8c 或别的数据库、想先做逻辑统一再谈物理归集的数据平台团队;以及需要快速给业务开跨源查询口子的数据开发。白皮书讲的是设计意图,落到工位上是一堆具体的注册、映射、下推和调参动作。
2. GBase-Up 统一数据平台的分层架构与统一 SQL 入口
2.1 从数据源接入到服务治理的四层拆解
把统一数据平台拆开看,白皮书里的架构图基本都可以归到四层:接入层、元数据层、查询计算层、服务治理层。接入层负责"怎么连上",元数据层负责"看见什么",查询计算层负责"怎么算",服务治理层负责"谁能用、用在哪、出了事怎么查"。很多团队落地失败,不是某一层技术不行,而是四层的边界没划清,把元数据治理的活干成了纯技术活。
| 分层 | 关键能力 | 常见组件形态 | 落地检查项 |
|---|---|---|---|
| 数据源接入层 | JDBC/ODBC 驱动、CDC 日志解析、对象存储与文件、消息队列、REST 接口 | 连接器池、CDC 采集任务 | 驱动版本是否与源端主版本对齐 |
| 统一元数据层 | 库表结构采集、类型映射、统计信息、血缘、权限标签 | 元数据服务 + 元数据仓储 | 采集周期是否覆盖新增表 |
| 统一查询计算层 | SQL 解析、逻辑计划、下推判定、联邦执行、结果归并 | 查询引擎 + 执行器集群 | 下推命中率是否可观测 |
| 服务治理层 | 统一 SQL 网关、数据 API、任务调度、审计与限流 | 网关 + 调度 + 审计 | 慢查询与越权访问是否留痕 |
这四层里,接入层和元数据层是最容易被低估的。接入层决定了你未来能接多少种源,元数据层决定了业务能不能自助找到表。查询计算层是性能的大头,但它能优化的前提是下面两层给的信息足够准——统计信息缺失时,优化器只能拍脑袋选 join 策略。服务治理层平时不起眼,一旦要控权限、限并发、追责,没有这层就得靠人肉排查。
2.2 统一 SQL 入口与元数据采集:两条主线怎么配合
统一 SQL 入口的执行链路,常见做法是:SQL 文本进网关 → 词法语法解析成抽象语法树 → 结合统一元数据生成逻辑计划 → 做下推判定,把能下推的谓词、聚合、join 尽可能拆回源端 → 各源并行执行 → 平台层做结果归并与二次计算 → 返回结果集。这里最关键的一步是下推判定:把过滤条件下推到源端,网络传输量可能从千万行降到几万行;判断错了,就会出现"看着 SQL 很短、跑起来要命"的情况。
元数据的采集通常有两条路。一条是主动拉取,平台按周期扫描源端的 catalog,自动发现新增表和字段变更;另一条是注册制,由数据开发显式登记,好处是可控,坏处是容易漏。实务里我会两条都留着:自动拉取负责覆盖度,注册制负责口径审批。类型映射是这里最容易埋雷的地方,源端一个tinyint(1)、一个enum、一个无时区的datetime,映射到统一类型后行为可能完全不同。
| 源端类型 | 统一类型 | 需要注意的点 |
|---|---|---|
| varchar / text | STRING | 注意源端字符集与长度截断 |
| decimal(p,s) | DECIMAL(p,s) | 精度不一致会让下推聚合失败 |
| datetime / timestamp | TIMESTAMP | 时区语义要在注册时写清 |
| tinyint(1) | BOOLEAN 或 INT | 语义判断错会导致条件恒真 |
| json / array | STRING 或半结构化类型 | 复杂类型通常无法下推 |
提示:口径统一从来不是技术问题,而是治理问题。字段命名、枚举取值、时间基准这三件事没定下来,SQL 引擎再强也拼不出可信的客户视图。
2.3 白皮书里的架构图怎么变成一张部署清单
架构图看着漂亮,真正部署时第一个要回答的问题是:哪些角色能合并到一台机器上。小规模场景常见做法是把元数据服务、查询网关合到两台机器做主备,执行器单独铺开;规模一上来,元数据仓储(通常还是落到关系库)必须独立,否则元数据查询会和业务查询互相抢 IO。
| 节点角色 | 起步数量 | CPU/内存参考 | 磁盘 | 说明 |
|---|---|---|---|---|
| 元数据服务 | 2 | 8C/32G | 系统盘 + SSD 数据盘 | 做主备,避免单点 |
| 查询网关 | 2 | 8C/16G | 系统盘 | 承担 SQL 解析与结果归并 |
| 执行器 | 3 起 | 16C/64G | SSD,容量按落盘阈值定 | 联邦计算与中间结果 |
| 调度节点 | 1~2 | 4C/8G | 系统盘 | 同步任务、元数据刷新 |
| 元数据仓储 | 2 | 8C/32G | SSD | 独立部署,别和其他角色混用 |
执行器数量不是越多越好。联邦查询会把中间结果拉到平台层做归并,执行器一多,数据在网络上来回搬的成本反而更高。我一般的做法是先按 3 台起步,压测时观察执行器 CPU 和网络出口带宽,哪个先打满就往哪个方向扩。磁盘容量主要看两个量:结果集落盘阈值和 CDC 的本地缓存。
3. 用 GBase-Up 搭一条最小可跑通的跨源查询链路
3.1 环境准备与组件清单
先把最小集合跑通,再谈扩展。最小集合包括:一台元数据服务节点、一台查询网关、一台执行器、一个可访问的源库(比如 MySQL 或另一个 GBase 实例),外加对应的 JDBC 驱动。驱动版本这件事必须提前核对,源端主版本和驱动版本错位,最典型的症状是连得上但元数据采集不全。
| 准备项 | 检查方式 | 不通过的典型症状 |
|---|---|---|
| 网络连通 | telnet 源库端口 | 注册数据源时长时间卡住 |
| 驱动版本 | 对比源端主版本 | 采集不到表或字段类型错乱 |
| 只读账号 | 用该账号手工执行一条 select | 注册成功但查询报权限错 |
| 字符集 | 源端 show variables | 中文乱码或长度校验失败 |
| 时间基准 | 确认源端时区配置 | 按天聚合的数据错位一天 |
只读账号建议单独开,不要复用业务账号。联邦查询会把源端的压力带到平台上,一旦某个开发写了全表扫描,用业务账号出事的概率会高很多。账号权限收到库级或表级,是后面做限流和审计的基础。
3.2 注册第一个数据源与映射逻辑表
注册这一步是把"一个物理库"变成"统一命名空间下的一张逻辑表"。下面是一段示意语法,具体子命令和参数名以实际所装版本的客户端为准,但字段语义八九不离十。
-- 第一步:登记数据源,连接信息集中在这里,后续映射表只引用别名 CREATE DATASOURCE mysql_order TYPE 'mysql' HOST '10.20.30.11' PORT 3306 DBNAME 'order_db' USER 'up_reader' PASSWORD '******' OPTIONS ('fetchSize' = '2000', 'maxPoolSize' = '20'); -- 第二步:把源表映射成统一命名空间下的逻辑表 -- 字段顺序与类型必须与源表一致,否则下推判定会直接放弃 CREATE FOREIGN TABLE dw.ods_order_main ( order_id STRING, user_id STRING, amount DECIMAL(18,2), pay_time TIMESTAMP, status INT ) SERVER mysql_order OPTIONS ('schema' = 'order_db', 'table' = 't_order_main'); -- 第三步:刷新统计信息,让优化器有依据选 join 策略 ANALYZE TABLE dw.ods_order_main;代码里的三个动作对应三件事。CREATE DATASOURCE把连接串收敛成别名,好处是换密码、换地址只改一处。CREATE FOREIGN TABLE建立逻辑表,这一步决定了后续 SQL 能否下推——字段类型写宽了,谓词下推就可能失败。ANALYZE TABLE采集统计信息,行数、NDV(不同值个数)这些指标直接影响 join 顺序。很多人跳过第三步,然后在压测阶段抱怨"同样的 SQL 一会儿快一会儿慢",原因就在这里。
登录信息里的fetchSize控制单次从源端拉取的行数,设置过小会增加往返次数,设置过大则吃平台侧内存,一般从 1000 到 5000 之间试。maxPoolSize是连接池上限,源库连接数紧张时优先调小它,而不是去调并发度。
3.3 写第一条跨源查询并确认下推是否生效
数据源注册好之后,跨源查询写起来和单库 SQL 没什么区别。下面这条是把订单逻辑表和会员逻辑表做 join,两边在不同源上。
SELECT u.user_id, u.level, COUNT(o.order_id) AS ord_cnt, SUM(o.amount) AS gmv FROM dw.ods_user_profile u JOIN dw.ods_order_main o ON u.user_id = o.user_id WHERE o.pay_time >= TIMESTAMP '2025-01-01 00:00:00' GROUP BY u.user_id, u.level;这条 SQL 的执行过程是:pay_time的过滤条件下推到订单源端,先在 MySQL 里把 2025 年之后的订单筛出来;level的分组如果源端支持也在源端做;跨源的 join 需要把两边结果拉到平台层归并,归并的规模取决于下推后剩下的行数。判断下推是否生效,最直接的办法是看执行计划:
EXPLAIN VERBOSE SELECT /* 查询计划里重点看 pushdown 标记 */ u.user_id, u.level, SUM(o.amount) AS gmv FROM dw.ods_user_profile u JOIN dw.ods_order_main o ON u.user_id = o.user_id GROUP BY u.user_id, u.level;看计划时抓三个点:Filter 节点是否落在源端扫描之上,Aggregate 是否被标注为下推,Join 的实现方式是广播还是重分布。如果发现过滤条件没有下推,先回头查字段类型是否一致——源端bigint映射成统一类型里的STRING,比较时就会触发隐式转换,优化器只能放弃下推。
增量场景常见做法是用水位线表驱动,而不是每次都全量重跑:
-- 水位线推进:先查上次跑到的位置,再按区间加工 INSERT INTO dw.dwd_order_daily SELECT DATE(o.pay_time) AS dt, COUNT(*) AS ord_cnt, SUM(o.amount) AS gmv FROM dw.ods_order_main o WHERE o.pay_time >= :last_watermark AND o.pay_time < :next_watermark GROUP BY DATE(o.pay_time); -- 任务成功后更新水位线,失败则保持原值,下次重跑这一段 UPDATE dw.etl_watermark SET last_value = :next_watermark, update_time = CURRENT_TIMESTAMP WHERE job_name = 'dwd_order_daily';水位线写在前、区间闭合在后,是为了避免边界重复计数。pay_time >= last和< next这种左闭右开写法,是增量加工里最不容易出错的形式。失败重跑时水位线不动,下一轮还会覆盖同一区间,配合加工表的按天覆盖写就能保证幂等。
4. GBase-Up 统一数据平台的调优参数与慢查询定位
4.1 联邦查询里最值得先调的几组参数
调参之前先明确一件事:统一数据平台的性能瓶颈,八成都出在"数据搬了多少"上,而不是"引擎算得多快"。所以参数优先级从高到低是:下推相关、结果集规模相关、并发相关、超时相关。
| 参数类别 | 常见参数名(示意) | 默认倾向 | 调整思路 |
|---|---|---|---|
| 下推控制 | pushdown.enabled/pushdown.agg | 开启 | 排错时可临时关闭,用于对比验证 |
| 拉取批量 | fetch.size | 1000 左右 | 宽表调小,窄表可调大到 5000 |
| 结果落盘 | result.spill.threshold | 偏小 | 执行器内存充足时可放宽,减少磁盘 IO |
| 并发度 | executor.concurrency | 中等 | 先加执行器,再加并发度 |
| Join 策略 | join.strategy/broadcast.threshold | 自动 | 小表广播阈值要按实际维表大小调 |
| 超时 | query.timeout | 偏长 | 网关侧设短,执行器侧设长 |
| 元数据刷新 | meta.refresh.interval | 小时级 | 源端频繁加表时缩短,但要评估源库压力 |
| 慢查询阈值 | slow.query.threshold | 未开 | 生产必开,否则事后无从追查 |
broadcast.threshold这个参数值得单独说。跨源 join 时,平台通常会把小表广播到各个执行节点,大表做重分布。阈值设小了,维表被当成大表去重分布,网络直接爆掉;设大了,一张几十万行的表被广播到每个节点,执行器内存扛不住。判断方法很简单:看维表的实际行数,阈值设成它的 1.5 到 2 倍留点余量。
4.2 一条慢 SQL 从发现到定位的完整路径
发现慢 SQL 靠监控,定位靠执行计划,验证靠对比实验。这三步缺一不可。
# 第一步:从慢查询日志里捞出耗时 Top 20,先看有没有共同的源 grep 'elapsed' /var/log/gbase-up/gateway/slow.log \ | sort -t'=' -k2 -nr \ | head -20 # 第二步:把问题 SQL 单独拿出来跑执行计划,重点看扫描行数与下推标记 # 示意命令,子命令名以实际版本为准 up-cli profile --sql-file q.sql --format json --top 20-- 第三步:做一个对照实验,手动关闭下推再跑一次,看耗时差多少 SET pushdown.enabled = false; -- 执行原 SQL,记录耗时 SET pushdown.enabled = true; -- 再执行一次,对比两次结果逻辑说明:第一步定位"哪些 SQL 慢",第二步定位"慢在哪一段",第三步确认"慢是不是下推没生效造成的"。如果关掉下推反而更快,往往说明源端本身压力大或者下推拆分得不合理,这时候要考虑把同步链路提前、把数据先落到平台侧。如果关掉下推明显更慢,说明下推本身是对的,问题在结果集归并阶段,去看执行器的内存和落盘指标。
几个常见的判断信号:执行计划里出现大行数的TableScan且没有 Filter,通常是过滤条件下推失败;Exchange节点的数据量远大于预期,是 join 策略选错;执行器 GC 频繁且落盘文件持续增长,是结果集超出了内存承受范围。
4.3 三类高频报错与处置方式
第一类是驱动与源端不匹配。症状是数据源注册成功,但采集出来的字段类型对不上,或者查某些表直接报语法错。处置方式是核对源端主版本和驱动版本,必要时在数据源配置里显式指定兼容模式,不要指望自动协商。
-- 查看某个数据源实际采集到的字段映射,用于比对 DESC FOREIGN TABLE dw.ods_order_main; -- 若类型明显异常,先删除再重新映射,避免残留错误缓存 DROP FOREIGN TABLE dw.ods_order_main;第二类是隐式类型转换导致的性能塌陷。SQL 写得没问题,执行计划也正常,但耗时不规则地波动,多半是字符集或类型不一致。源端varchar和统一类型STRING之间做比较,如果一方有隐式转换,索引就用不上。处置方式是统一比较双方的类型,必要时在映射层把类型写准,而不是在 SQL 里用函数包一层。
第三类是元数据不同步。业务反馈"昨天还能查的表今天找不到",最常见的原因是源端改了表结构而采集周期还没到。这时候手工触发一次元数据刷新,同时把变更频繁的库的刷新周期调短。但要注意,刷新周期过短会给源库的 catalog 查询带来持续压力,尤其是表数量上万的库。
注意:任何一次参数调整都只动一个变量,并记录调整前后的耗时基线。批量改参数的后果是出了问题不知道是哪一个引起的。
5. 从 POC 到生产:统一数据平台的验收验证清单
白皮书给出的是目标形态,POC 能不能通过验收,取决于你有没有一套可复现的验证动作。我一般会准备一张验收表,每一项都要有明确的判定标准和采样方法,避免最后变成"感觉还行"。
| 验收项 | 判定标准 | 采样方法 |
|---|---|---|
| 源覆盖度 | 已接入源占计划源的 100% | 逐源跑一条 count(*) 并记录耗时 |
| 下推命中率 | 核心 SQL 下推生效比例 ≥ 90% | 批量抓执行计划,统计 Filter 落在源端的比例 |
| 结果一致性 | 与源端直查结果逐行比对为零差异 | 抽样 20 张表做全字段比对 |
| 并发表现 | 20 并发下 P95 耗时不超过单并发 3 倍 | 用固定 SQL 模板压测,逐步加并发 |
| 故障恢复 | 杀执行器进程后 5 分钟内自动恢复 | 演练时直接 kill -9 |
| 权限边界 | 越权查询被拒绝且留痕 | 用低权限账号尝试访问非授权表 |
结果一致性这一项最容易被敷衍。跨源查询里,浮点精度、时区、字符集、空值语义这四个点都会造成差异,尤其是NULL参与聚合时的行为,源端和平台层不一定一致。比对时不要只比总数,要逐行比、逐字段比,字段类型不同的列要加上显式转换再比。
-- 一致性比对:把平台侧结果与源端直查结果做差集 SELECT 'platform_only' AS src, order_id, amount FROM dw.ods_order_main WHERE pay_time >= TIMESTAMP '2025-01-01 00:00:00' EXCEPT SELECT 'platform_only' AS src, order_id, amount FROM src_direct.ods_order_main WHERE pay_time >= TIMESTAMP '2025-01-01 00:00:00' UNION ALL SELECT 'source_only' AS src, order_id, amount FROM src_direct.ods_order_main WHERE pay_time >= TIMESTAMP '2025-01-01 00:00:00' EXCEPT SELECT 'source_only' AS src, order_id, amount FROM dw.ods_order_main WHERE pay_time >= TIMESTAMP '2025-01-01 00:00:00';EXCEPT双向跑一遍,能同时暴露"平台多出来"和"源端多出来"两类问题。金额字段用DECIMAL而不是DOUBLE,否则会出现 0.01 级别的伪差异,排查起来很浪费时间。
压测时有个细节值得留意:不要用同一条 SQL 反复跑。统一数据平台通常带结果缓存或源端缓存,第二次跑出来的漂亮数字没有参考价值。准备 5 到 10 条结构不同、源组合不同的 SQL 轮着跑,并把缓存开关显式关掉。另外,压测期间一定要开着执行器的资源监控,重点看内存水位和落盘文件增长速率,这两个指标比平均耗时更早暴露容量问题。
最后给一个日常运维里很实用的小技巧:给每类跨源查询模板配一条轻量级的"探活 SQL",每天定时跑一次并记录耗时。耗时的绝对值意义不大,但一旦它比过去七天的均值高出 50%,基本可以断定是源端数据量增长、统计信息过期或者下推失效三者之一。在业务反馈之前发现问题,比事后救火从容得多。
本文还有配套的精品资源,点击获取