跨库 JOIN--SPL 轻量级多源混算实践 6

简介: 本案例涉及车辆、交通、公民系统多源数据混合计算,重点演示SPL对不同表关系(维表关联、主子表关联)的高效处理。通过区分关联类型,SPL可自动选择最优算法,提升计算效率,适用于大规模数据分析场景。

数据结构不一样的多源混合计算会更常见,比如不同业务系统混合分析。

数据结构说明
车辆管理系统(DB_Vehicle)保存了车辆与车主等相关信息,其中车主信息表 owner_info 表结构简化如下:

71839211ec22f65746e246e8e3990ca6_1748232120260100.png

主键 owner_id 是车主身份证号。

车辆表 vehicle_master 简化结构如下:
ec6574918fb307e29d44c6de0a671074_1748232120346100.png
vin 设为主键,plate_no 也是唯一的,两个字段在逻辑上都可以视为主键。

交管系统(DB_Traffic)存储了车辆交通信息,其中违章记录表 traffic_violation 结构如下:
0aed7631c65874c506959f6c210c3948_1748232120415100.png
公民信息系统(DB_CitizenEvent)存储了公民相关信息,其中公民事件表 citizen_event 结构如下:
92e75fa063d9be81a818ed3ea15f9072_1748232120466100.png
4 个表间逻辑关系简单描述是这样的:
32000bd2640d66b3e52a930d69c4fd83_1748232120523100.png
从逻辑上看,citizen_event 是 owner_info 是多对一的关系,作为维表的 owner_info 的规模要远小于 citizen_event。

traffic_violation 和 vehicle_master 这两个表也是一对多的关系,规模都可能很大,从后者的角度来看,traffic 更像一个子表,这两个表构成主子关系(plato_no 是 vehicle 表的逻辑主键)。

为什么要区分表间关系呢?

日常的等值 JOIN 基本都会涉及主键(多对多的关联基本没有业务意义),大体可以分为两种:维表关联是一种,是普通字段和维表的主键关联(如 citizen_event 和 owner_info);另一种是某个表的主键与另一个表的主键或部分主键的关联(如 vehicle_master 和 traffic_violation,traffic_violation 表中,可以把 plate_no 和 violation_id 视为共同主键,即 violation_id 是从属于 plate_no 的)。

SPL 在做 JOIN 运算时会根据不同的关联情况选择不同的方法,简化编码的同时还能提升计算效率。

配置数据源连接
vehicle:

jdbc:mysql://127.0.0.1:3306/db_vehicle?useSSL=false&useCursorFetch=true

traffic:

jdbc:mysql://127.0.0.1:3306/db_traffic?useSSL=false&useCursorFetch=true

citizen:

jdbc:mysql://127.0.0.1:3306/db_citizenevent?useSSL=false&useCursorFetch=true

计算用例
要做这样几个计算:

  1. 按城市统计最近一年有车公民的事件数量,用于分析各城市有车人的“行为活跃度”

  2. 找出近一年获得表彰(Commendation)的车主姓名和事件描述,用以识别“优秀市民”

  3. 按年份和品牌统计车辆违章次数,用于分析某些品牌的车是否更容易违章,用于驾驶行为和车辆品牌的关联研究

下面来实现前面第一个计算需求:按城市统计最近一年有车公民的事件数量。要关联 owner_info 和 citizen_event 两个表,也就是维表的关联计算。

维表的关联
esProc 实现:
image.png
A2 从 vehicle 库查询车主信息,query@x 表示数据全部加载内存后关闭数据库连接,使用 keys@i 设置主键并建立索引,通常事实表会远大于维表,这个索引会被复用很多次,能加快计算速度。

A4 查询事件表,筛选最近一年的数据,都读入内存。

A5 使用 switch 进行外键关联。由于外键指向的维表记录是唯一的,switch 直接将关联字段 citizen_id 转换成 A2 中的记录(实际在内存中存储的是维表记录所在地址)。
7b79423a3a5400466a02818878024460_1748232120618100.png
这种转换是一次性的,后续可以重复使用,而且可以同时处理多个维表的外键关联。关联完成后通过“关联字段. 维表字段”方式就能引用任意维表字段。A6 就通过 citizen_id.reg_city 获得注册地进行分组汇总。

整体运行如下:
ebaffad272df0a830d674602162759d0_1748232120742100.png
接下来继续:找出近一年获得表彰的车主姓名和事件描述。

在前面代码的基础上增加:
image.png
还是基于 A5 的关联结果进行计算,实现了复用。
d5bb4b9f59e63af34a4991dbf1188b50_1748232120870100.png
esProc 显著区分外键关系有很大好处,单从书写和理解上,通过点(.)操作符(类似对象. 属性)就能引用外键表的所有字段,有多少层都可以(维表还可能有维表),也很容易表达自关联 / 循环关联的情况。

当 citizen_event 表的数据量很大时,用 esProc 仍然可以处理。不过,当数据量大到无法全部放进内存时,内存地址化方法就不再有效了,因为在外存无法保存事先算好的地址,这时就只能边读入边地址化。

按城市统计所有车公民的事件数量:
image.png
与全内存的写法大部分一样,区别在 A4 使用 cursor 创建游标分批读取数据。esProc 的游标是延迟游标,附加在游标上的计算等到最后取数时才会真正计算。
8fe7694dfcc368465d643969fbb96dee_1748232121001100.png
但游标是一次性的,如果想再进行其他计算,比如还要获得表彰的车主。再基于 A5 计算是得不到结果的(注意 A7 的计算结果):
06be95012c17be41aa952d811f4a248c_1748232121127100.png
这时可以使用 esProc 提供的管道机制:
image.png
A6 和 A7 基于 A5 创建管道(A7 是简化写法),B6 基于管道进行分组汇总,结果返回给 A6:
c85a83b1a337fe53eefe7e25521a6ef4_1748232121266100.png
B7 则根据另一个管道筛选获得表彰的数据,A7 的结果:
17ff54ef7d2963997a9f5b2dc2fc312d_1748232121368100.png
主子表的关联
按年份和品牌统计车辆违章次数。
image.png
A5 使用 join 函数根据 plate_no 关联了两个表,其关联结果是这样的:
0e6b39c00a19acc454943696b6645fa7_1748232121484100.png
保留了两边完整记录的多层集合,点开可以看到
4ff3cdba0c3a3593679607b78c6ddba8_1748232121588100.png
关联完成后,A6 就能通过多层引用进行分组汇总。
43e9e4afd65fc9e955fdd89f8041c1a5_1748232121689100.png
处理主子表关联时,我们使用了与外键关联 switch 不同的 join 函数,join 函数提供了一些选项,@1 表示左连接,@f 表示全连接,@d 做差集等,用来满足不同的连接需求。事实上,外键关联也可以使用 join 函数来完成。

那为什么不统一用 join 呢?

这里我们看到的都是两个表关联,如果存在多个维表(大部分情况),使用 switch 可以将维表(维表可能还有维表)都附加到事实表上,但用 join 就很难表达这种层次关系,书写也不方便。

主子表关联时的两个表可能都很大,利用表的关联字段都是主键(或部分主键)的特性,可以采用有序归并的算法一次遍历就完成关联。

按年份和品牌统计车辆违章次数:
image.png
A2 和 A4 使用 cursor 创建游标,里面的 SQL 都对 plate_no 排序。

A5 使用 joinx 做有序归并,返回的仍是游标。剩下的代码就跟全内存时一样了。

有序遍历利用了关联键有序的特性,只适用于主子表的关联(可对主键有序),但不适用于前面那种维表的外键关联。因为同一个表上可能有多个要参与关联的外键字段,不可能让同一个表同时针对多个字段都有序。这也是区分 JOIN 后采用了不同函数(算法)的原因。

再次强调,无论是跨库还是跨其他任何数据源,SPL 在处理时只要数据源能接入,后续计算都一样,因为 SPL 提供了统一的序表和游标数据对象。

相关文章
|
1月前
|
存储 JavaScript
手机号码生成器的实现代码
根据选择的运营商号段生成随机号码,支持生成、保存、导出及复制操作,历史记录可持久化存储,方便实用。
|
1月前
|
存储 关系型数据库 数据库
【赵渝强老师】PostgreSQL数据库的WAL日志与数据写入的过程
PostgreSQL中的WAL(预写日志)是保证数据完整性的关键技术。在数据修改前,系统会先将日志写入WAL,确保宕机时可通过日志恢复数据。它减少了磁盘I/O,提升了性能,并支持手动切换日志文件。WAL文件默认存储在pg_wal目录下,采用16进制命名规则。此外,PostgreSQL提供pg_waldump工具解析日志内容。
130 0
|
1月前
|
人工智能 运维 Cloud Native
阿里云Serverless计算产品入选Gartner®报告「领导者」象限!
近日,Gartner® 发布了 2025 年度全球《云原生应用平台魔力象限》报告,阿里云凭借 Serverless 应用引擎 SAE(以下简称 SAE)和函数计算 FC,成为亚太地区唯一入选「领导者象限」的科技公司。
180 16
|
1月前
|
运维 监控 Cloud Native
从本土到全球,云原生架构护航灵犀互娱游戏出海
本文内容整理自「 2025 中企出海大会·游戏与互娱出海分论坛」,灵犀互娱基础架构负责人朱晓靖的演讲内容,从技术层面分享云原生架构护航灵犀互娱游戏出海经验。
240 15
|
1月前
|
SQL 人工智能 JSON
Flink 2.1 SQL:解锁实时数据与AI集成,实现可扩展流处理
本文整理自阿里云的高级技术专家、Apache Flink PMC 成员李麟老师在 Flink Forward Asia 2025 新加坡[1]站 —— 实时 AI 专场中的分享。将带来关于 Flink 2.1 版本中 SQL 在实时数据处理和 AI 方面进展的话题。
145 0
Flink 2.1 SQL:解锁实时数据与AI集成,实现可扩展流处理
|
运维 大数据 Apache
|
1月前
|
人工智能 监控 JavaScript
Playwright初学指南 (3):深入解析交互操作
本文深度解析Playwright如何通过智能等待、自动重试等机制解决Web自动化中60%的交互失败问题。从基础点击/输入到高级拖拽/iframe操作,提供企业级解决方案和性能优化技巧,帮助开发者实现98%的操作成功率,打造稳定高效的自动化测试体系。
|
1月前
|
JavaScript 安全 前端开发
如何开发人事及OA管理系统的薪酬管理板块?(附架构图+流程图+代码参考)
本文介绍了如何构建一个高效、合规的企业薪酬管理系统,涵盖薪酬模块的重要性、核心功能、系统架构设计、数据模型、开发实现及安全合规要点。内容包括薪酬配置、数据导入、自动化计算、审批发放、工资条生成与安全分发、报表看板、权限审计等关键环节,并提供详细的业务流程、架构图、核心代码示例及落地开发技巧。适用于HR、财务及技术人员快速搭建薪酬管理系统,提升发薪效率,降低人工错误与合规风险。
|
1月前
|
XML 安全 测试技术
【干货满满】分享什么是API接口测试
API接口测试是验证应用程序编程接口功能、性能、安全性及兼容性的关键环节,通过模拟请求并验证响应结果,确保接口能正确处理各种输入和场景。测试内容涵盖功能验证、性能评估、安全防护、兼容性验证及系统可靠性。相比UI测试,API测试无需界面依赖,支持数据驱动与自动化,适用于持续集成流程。常见接口类型包括RESTful、SOAP和GraphQL API,广泛应用于电商、金融及社交平台,保障系统间数据交互的安全与高效。