当前位置: 首页 > 编程日记 > 正文

大主子表关联的性能优化方法

【摘要】
主子表是数据库最常见的关联关系之一,最典型的包括合同和合同条款、订单和订单明细、保险保单和保单明细、银行账户和账户流水、电商用户和订单、电信账户和计费清单或流量详单。当主子表的数据量较大时,关联计算的性能将急剧降低,在增加服务器负载的同时严重影响用户体验。作为面向过程的结构化数据计算语言,集算器 SPL 可通过有序归并的方法,显著提升大主子表关联计算的性能。 下面就来乾学院一探究竟:大主子表关联的性能优化方法。

一、        原理解释

所谓主子表关联计算,就是针对主表的每条记录,按关联字段找到子表中对应的一批记录。以订单(主表)和订单明细(子表)为例,两者以订单ID为关联字段。下图显示了关联计算过程中对主表中一条记录的处理情况,红色箭头代表没找到对应记录(不可关联),绿色箭头代表找到了对应记录(可关联):

undefined

假设订单(主表)有m条记录,订单明细(子表)有n条记录,在不考虑优化算法时,主表中每一条记录的关联都需要遍历子表,相应的时间复杂度为O(n)。而主表一共有m条记录,所以整个计算的复杂度就是O(m*n),显然过高。虽然数据库一般会采用hash方案来优化,但在数据量较大或较多表关联时,仍然会面临时难以并行、使用外存缓存数据的问题,性能依旧会急剧下降。

而对于集算器来说,针对大主子表关联算法,可以通过两步来实现显著优化:数据有序化、归并关联。

l  数据有序化

对主表和子表,首先分别按照关联字段排序,形成有序数据。

l  归并关联

首先在主表和子表上分别用指针指向第一条记录,然后开始比对,对于主表的第一条记录,如果子表遇到匹配的记录,则表示可以关联,记录后子表指针前移;如果遇到不匹配的记录,表示主表第一条记录的关联计算完成,此时子表指针不动,主表指针下移一位,指向第二条记录。以此类推……

优化后,单条记录的关联计算可用下图示意:

undefined

可以看到,经过优化,主表中单条记录的关联只需比对部分数据,不再需要遍历子表。事实上,对主表所有记录的关联,才会遍历一次子表,也就是复杂度为O(n)。再加上主表本身会遍历一次,因此整个计算的复杂度就是O(m+n)。

这样,经过集算器优化后,算法的时间复杂度变为线性,而且不再需要生成落地的中间数据,性能自然得到大幅提升。

当然,需要注意的是,有序化本身也会耗费时间,因此这种优化方法不适合只做一次的关联算法。但在实际业务中,关联算法通常会反复执行,这时有序化的开销就是一次性的,完全可以忽略不计。

二、        具体实现

下面还是以订单和订单明细为例,说明集算器优化大主子表关联的方法。

首先进行数据有序化(注意,这是一次性动作)。集算器脚本“数据有序化.dfx”如下:

image.png

A1连接Oracle数据源,A5关闭数据源。集算器可连接大部分常用数据源,包括数据库、Excel、阿里云、SAP等等。

A2、B2:用SQL语句分别取订单和订单明细,并按关联字段排序。由于数据量较大,无法一次性读入内存,因此这里用到了游标函数cursor。

A3、B3:分别创建组表文件“订单.ctx”和“订单明细.ctx”,用于存储有序化之后的数据。这里需要指定字段名,其中带#号的字段是主键,。数据将按主键排序,且主键的值不可重复。

A4-B4:将游标追加写入组表文件。

其次,对于通常会反复执行的关联算法,可以用集算器脚本“归并关联.dfx”实现如下:

image.png

A1、B1:读入组表文件“订单.ctx”和“订单明细.ctx”。注意组表默认为列式存储,因此只需读入后续计算需要的字段,从而大幅降低I/O。

A2:对有序游标A1、B1进行归并关联,其中“主表”、“子表”是别名,方便后续引用,如果省略别名,后续可以通过默认别名_1、_2引用。注意,函数joinx默认进行内关联,可用选项@1指定左关联,或者@f指定全关联。如果有多个游标都要与A1关联,可用分号依次隔开。

A3:对关联结果进行后续计算,例如汇总产品数量。事实上后续计算可以支持任意算法,也不是本文的讨论范围了。

上面介绍了集算器SPL脚本的写法,而在实际执行时,还需要部署集算器的运行环境。有两种部署方式可供选择:内嵌部署和独立部署。

l  内嵌部署

内嵌部署时,集算器的用法类似内嵌数据库,应用系统使用集算器驱动(JDBC)执行同一个JVM下的集算器脚本。

下面是Java调用“归并关联.dfx”的代码

image.png

在上述JAVA代码中,集算器脚本以文件的形式保存,调用语法类似存储过程。而如果脚本很简单,也可以不保存脚本文件,直接书写表达式,调用语法类似SQL,这时第5行可以写成:

image.png

这篇文章详细介绍了JAVA 调用集算器的过程:http://doc.raqsoft.com.cn/esproc/tutorial/bjavady.html

除了使用Java代码,也可以通过报表访问集算器,这时按照访问一般数据库的方法即可,具体可参考《让Birt报表脚本数据源变得既简单又强大》。

对于脚本“数据有序化.dfx”,可以用同样的方法执行。不过这个脚本通常只执行一次,所以也可以直接在命令行中执行,windows用法如下:

image.png

Linux 下用法类似,可以参考http://doc.raqsoft.com.cn/esproc/tutorial/minglinghang.html

l  独立部署

独立部署时,集算器的用法类似远程数据库,应用系统可以使用集算器驱动(JDBC或ODBC驱动)访问集算服务器。这种情况下,应用系统和集算器服务器通常部署在不同的机器上。

例如集算服务器的IP地址为192.168.0.2,端口号为8281,那么JAVA应用系统可以通过如下代码访问:

image.png

关于集算服务器的部署和使用,详细内容可参考http://doc.raqsoft.com.cn/esproc/tutorial/fuwuqi.html

关于JDBC和ODBC驱动的部署方法,可分别参考

http://doc.raqsoft.com.cn/esproc/tutorial/jdbcbushu.html

http://doc.raqsoft.com.cn/esproc/tutorial/odbcbushu.html

三、        多线程优化

前面介绍了基本的优化思路和实现方法,也就是针对数据本身的优化。而现实中服务器都是多核心CPU,因此可以进一步对上述算法进行多线程优化。

多线程优化的原理,是将主表和子表各分为N段,使用N个线程同时进行关联计算。

原理虽简单,但真正实现的时候,就会发现很多难题:

l  分段效率

想把数据分为N段,就要先找到每一段的起始行号,如果用遍历的笨办法数行号,显然会白白消耗大量的I/O资源。

l  数据跨段

理论上,关联字段值相同的子表记录,应该分到同一段。如果对子表随意分段,很可能形成跨段的数据。

l  分段对齐

更进一步,理论上,子表的第i段数据,应该与主表的第i段数据对齐,也就是主子表关联字段值的范围应该一致。如果两者各自独立分段,则可能导致分段数据难以对齐。

l  二次计算

如果后续计算不涉及聚合,例如只是过滤,那么只需将N个线程的计算结果直接合并。但如果后续计算涉及聚合,比如sum或分组汇总,那就要单独再进行二次计算聚合。

好在集算器已经充分解决了上述难题,分段时不会耗费IO资源、关联字段值相同的记录会分在同一段、子表和主表会保持对齐、各种二次计算无需单独实现。

具体来说,首先,数据有序化脚本需要做如下修改(红色字体为修改部分):

image.png

B3:生成“订单明细多线程.ctx”时,数据按“#订单ID”分段。这将保证订单ID相同的记录,将来会分到同一段。

归并关联的脚本需修改如下:

image.png

A1:@m表示对数据分段,形成多线程游标(也叫多路并行游标)。其中线程数量是默认值,由系统参数“最大并行数”决定,也可手工修改。例如希望生成4线程游标,A1应写成:

image.png

B1:同样生成多线程游标,并与A1的多线程游标对齐。

A2-A3:归并关联,再执行后续算法。这两步写法上没变化,但底层会自动进行多线程合并和二次计算,从而降低了程序员的编程难度。

四、        结构优化

在前面算法的基础上,还可以进一步提升计算性能,那就是以层次结构存储数据,直接记录关联关系。

具体来说,先用“结构优化有序化.dfx”生成组表文件:

image.png

B4:在主表的基础上附加子表,命名为订单明细。与主表不同的是,子表默认继承了主表的主键,因此可以省略订单ID,只需要写另一个主键产品ID。这样,2个表写在了一个组表文件中,从而才能形成层次结构。

B5:向子表写入数据。

此时,组表“多层订单.ctx”将按层次结构存储,逻辑示意图如下:

image.png

可以看到,每条主表记录与对应的子表记录,在逻辑上已经紧密相关,无需额外关联,这样便可大幅提高关联算法的性能。

进行关联计算时,使用以下脚本“结构优化归并关联.dfx”:

image.png

A1、B1:打开主表,以及附加在主表上的子表。

A2、B2:以多线程方式分别读取主表和子表。需要注意的是,多层组表里的实表之间天然具备相关性,因此无需特意指定子表和主表的分段关系,代码比之前更清晰简单。

A3,A4:归并关联并执行后续算法,这两步没变化。

五、        数据更新

前面的优化方式都基于库表全量导出为组表文件的情况,但实际业务中数据库表总会发生变化,因此需要考虑数据更新的问题,也就是要将变化的数据定时更新到组表文件中。

显然,更新数据应选择在无人查询组表文件时进行,一般都是半夜或凌晨。而更新的频率,则需要按照数据实时性要求来设定,例如每天一次或每周一次。至于更新的方式,需要按照数据的变化规律来考虑,最常见的是数据追加,有时也会遇到增删改。

下面先看数据追加:

订单和订单明细每天都会产生新记录,假设需要在每天凌晨2点将昨天新增的记录追加到组表文件中。下图显示了2018/11/23新增记录的情况,注意,有些订单(订单ID:20001)并没有对应的订单明细:

image.png

把主子表追加到组表文件中的脚本 “追加组文件.dfx”如下:

image.png

A2、B2:计算昨天的起止时间,以便查询新增数据。函数now获取当前时间点,理论上应该是2018-11-24 02:00:00。A2是昨天的起始时间点,即2018-11-22 00:00:00。B2是终止时间点,即2018-11-23 00:00:00。之所以在集算器中计算起止时间,主要是为了增加可读性和移植性。实际上也可以在SQL中计算。

A4:取出新增的主表和子表记录。这里用一句SQL取两张表的数据,主要是为了提高效率。由于有些订单并没有对应的订单明细,因此用订单左关联订单明细,且将对应不上的订单明细置空。计算结果如下:

image.png

A5、B5:拆出新增的主子表记录,结果示例如下:

image.png

A6-B8:将主表和子表追加到组表文件中。

脚本写完之后,还需要在每天的02:00:00定时执行,这可以使用操作系统内置的任务调度。

在Windows下,建立如下的bat批处理文件,:

image.png

再使用windows内置的"计划任务",定时执行批处理文件即可。

在linux下,建立如下的sh批处理文件,:

image.png

再使用crontab命令,定时执行批处理文件即可。

当然也可使用图形化工具定时执行脚本,比如Quartz。

需要注意的是,大多数情况下,能够选择无人使用组表文件的时候进行追加,但有些业务中组表文件全天都要使用,而有些项目对容错要求更高,要求追加失败时再次追加,这类项目就需要更加细致的追加方法,详情可参考《基于文件系统实现可追加的数据集市》。

除了追加这种主要的更新方式,业务中也会遇到增删改都存在的情况。

在这种情况下,就需要知道哪些是删除的记录,哪些是修改或新增的记录。如果条件允许,可以在原表中新加“标记”字段,并将维护状态记录在该字段中。如果不方便修改原表,则应当创建对应的“维护日志表”。例如下面两张表,分别是订单和订单明细的维护日志。

image.png

根据维护日志更新组表文件,可使用下面的脚本:

image.png

A2、B2:从数据库查出应删除的记录

A3、B3:从数据查出应修改和新增的记录

A5、B5:对组表进行删除操作。

A6、B6:从组表进行修改新增操作。

A7、B7:清空维护日志表,以便下次继续更新数据。

六、        T+0实时计算

通过定时追加,能保证组表文件与昨天的数据同步,从而实现T+1计算,但有时需要进行实时大主表关联,即T+0计算。

对于T+0计算,需要将两种不同的数据源进行混合计算,由于SQL或SP的数据模型较为封闭,因此难以实现混合计算,而使用集算器就非常简单。

比如对组表文件定时追加后,数据库当天又产生了如下新数据:

image.png

可使用如下脚本实现T+0实时计算:

image.png

A1:算出当天的起始时间点,即2018-11-26 00:00:00。

A3:针对数据库当天产生的新数据,进行关联计算。由于当天数据量较小,因此性能可以接受。

A4-A7:针对组表文件历史数据,进行高性能关联计算。

A8:合并当天和历史,并进行二次计算,以获得最终计算结果。其中符号|表示纵向合并,这是实现混合计算的关键。事实上,这种写法也表明集算器支持任意数据源之间的混合计算,比如Excel与elasticSearch之间。

关于T+0计算更多的细节,可参考相关文章《实时报表 T+0 的实现方案》

相关文章:

Windows7上配置Python Protobuf 操作步骤

1、 按照http://blog.csdn.net/fengbingchun/article/details/8183468 中步骤,首先安装Python 2.7.10; 2、 按照http://blog.csdn.net/fengbingchun/article/details/47905907 中步骤,配置、编译Protobuf; 3、 将(2)中生成的pr…

鲜为人知的静态、命令式编程语言——Nimrod

Nimrod是一个新型的静态类型、命令式编程语言,支持过程式、函数式、面向对象和泛型编程风格而保持简单和高效。Nimrod从Lisp继承来的一个特殊特性抽象语法树(AST)作为语言规范的一部分,可以用作创建领域特定语言的强大宏系统。它还…

机器学习进阶-图像形态学操作-腐蚀操作 1.cv2.erode(进行腐蚀操作)

1.cv2.erode(src, kernel, iteration) 参数说明:src表示的是输入图片,kernel表示的是方框的大小,iteration表示迭代的次数 腐蚀操作原理:存在一个kernel,比如(3, 3),在图像中不断的平移,在这个9…

无需成对示例、无监督训练,CycleGAN生成图像简直不要太简单

作者 | Jason Brownlee译者 | Freesia,Rachel编辑 | 夕颜出品 | AI科技大本营(ID: rgznai100)【导读】图像到图像的转换技术一般需要大量的成对数据,然而要收集这些数据异常耗时耗力。因此本文主要介绍了无需成对示例便能实现图…

Git使用常见问题解决方法汇总

1. 在Ubuntu下使用$ git clone时出现server certificate verification failed. CAfile:/etc/ssl/certs/ca-certificates.crt CRLfile: none 解决方法:在执行$ git clone 之前,在终端输入: export GIT_SSL_NO_VERIFY1 2. 在Windows上更新了…

服务器监控常用命令

在网站性能优化中,我们经常要检查服务器的各种指标,以便快速找到害群之马。大多情况下,我们会使用cacti、nagois或者zabbix之类的监控软件,但是这类软件安装起来比较麻烦,在一个小型服务器,我们想尽快找到问…

Ubuntu下内存泄露检测工具Valgrind的使用

在VS中可以用VLD检测是否有内存泄露,可以参考http://blog.csdn.net/fengbingchun/article/details/44195959,下面介绍下Ubuntu中内存泄露检测工具Valgrind的使用。Valgrind目前最新版本是3.11.0, 可以从http://www.valgrind.org/ 通过下载源码…

数据为王的时代,如何用图谱挖掘商业数据背后的宝藏?

这是一个商业时代,一个数据为王的时代,也是一个 AI 迎来黄金发展期的时代。据史料记载,商业在商朝已初具规模。斗转星移,时光流转,到 2019 年,商业形式已发生翻天覆地的变化,但是商业的本质——…

旋转卡壳——模板(对踵点)

这东西学了我大概两天吧。。其实不应该学这么久的,但是这两天有点小困,然后学习时间被削了很多\(QwQ\) 说几个坑点。 - 对于题目不保证有凸包的情况,要选用左下角的点,而非单纯的最下边的点构造凸包。 - 对于凸包中只有\(1/2\)个点…

SNMP 协议 OID的使用

为什么80%的码农都做不了架构师?>>> SNMP 协议 OID的使用 SNMP(Simple Network Management Protocol简单网络管理)协议 是现在网络管理系统(NMS)监控网络设备状态的协议,是现在网管事实上的标准…

颜色空间YUV简介

YUV概念:YUV是被欧洲电视系统所采用的一种颜色编码方法(属于PAL,Phase Alternation Line),是PAL和SECAM模拟彩色电视制式采用的颜色空间。其中的Y、U、V几个字母不是英文单词的组合词,Y代表亮度,其实Y就是图像的灰度值…

基于RNN的NLP机器翻译深度学习课程 | 附实战代码

作者 | 小宋是呢来源 | CSDN博客深度学习用的有一年多了,最近开始NLP自然处理方面的研发。刚好趁着这个机会写一系列 NLP 机器翻译深度学习实战课程。本系列课程将从原理讲解与数据处理深入到如何动手实践与应用部署,将包括以下内容:&#xf…

trash-cli设置Linux 回收站

trash-cli 设置 Linux 回收站 trash-cli是一个使用 python 开发的软件包,包含 trash-put、restore-trash、trash-list、trash-empty、trash-rm等命令,我们可以通过这条命令,将文件移动到回收站,或者还原删除了的文件。 trash-cli的…

磁盘有时也不可靠

实验服务器的磁盘是最近买的,当卖家问我要普通的还是高级的, 我选择了普通,现在追悔莫及。今天的分析更加详细。首先发现每次实验,出错的文件都不一样,所以应该不是临界条件的问题。下表总结了出错的位置,原…

从原理到落地,七大维度详解矩阵分解推荐算法

作者 | gongyouliu编辑丨Zandy来源 | 大数据与人工智能 ( ID: ai-big-data)导语:作者在《协同过滤推荐算法》这篇文章中介绍了 user-based 和 item-based 协同过滤算法,这类协同过滤算法是基于邻域的算法(也称为基于内存的协同过…

libyuv库的使用

libyuv是Google开源的实现各种YUV与RGB之间相互转换、旋转、缩放的库。它是跨平台的,可在Windows、Linux、Mac、Android等操作系统,x86、x64、arm架构上进行编译运行,支持SSE、AVX、NEON等SIMD指令加速。下面说一下libyuv在Windows7VS2013 x6…

封装 vue 组件的过程记录

在我们使用vue的开发过程中总会遇到这样的场景,封装自己的业务组件。 封装页面组件前要考虑几个问题:1、该业务组件的使用场景 2、在什么条件下展示一些什么数据,数据类型是什么样的,及长度颜色等 3、如果是通用的内容&#xff0c…

Service的基本组成

Service与Activity的最大区别就是一有界面,一个没有界面。 如果某些程序操作很消耗时间,那么可以将这些程序定义在Service之中,这样就可以完成程序的后台运行, 其实Service就是一个没有界面的Activity,执行跨进程访问也…

BP神经网络公式推导及实现(MNIST)

BP神经网络的基础介绍见:http://blog.csdn.net/fengbingchun/article/details/50274471,这里主要以公式推导为主。BP神经网络又称为误差反向传播网络,其结构如下图。这种网络实质是一种前向无反馈网络,具有结构清晰、易实现、计算…

AI应用落地哪家强?CSDN AI Top 30+案例评选等你来秀!

人工智能历经百年发展,如今迎来发展的黄金时期。目前,AI 技术已涵盖自然语言处理、模式识别、图像识别、数据挖掘、机器学习等领域的研究,在汽车、金融、教育、医疗、安防、零售、家居、文娱、工业等行业获得了令人印象深刻的成果。 在各行业…

安利Mastodon:属于未来的社交网络

我为Mastodon开发了一款安卓客户端,v1.0版本已经发布,欢迎下载使用 源码在这里:https://github.com/shuiRong/Gakki ??? 正文 Mastodon(长毛象)是什么? 是一个免费开源、去中心化、分布式的微博客社交网络,是微博、…

通过案例练习掌握SSH 的整合

1. SSH整合_方案01 ** 整合方案01 Struts2框架 Spring框架 在Spring框架中整合了Hibernate(JDBC亦可) 一些业务组件(Service组件)也可以放入Spring框架中迚行管理(昨天的例子) 1. 请求&#xff0…

tiny-cnn开源库的使用(MNIST)

tiny-cnn是一个基于CNN的开源库,它的License是BSD 3-Clause。作者也一直在维护更新,对进一步掌握CNN很有帮助,因此下面介绍下tiny-cnn在windows7 64bit vs2013的编译及使用。 1. 从https://github.com/nyanp/tiny-cnn下载源码&#xff1…

玩嗨的2亿快手“老铁”和幕后的极致视觉算法

作者 | Just出品 | AI科技大本营(ID:rgznai100)创立八年,短视频平台快手目前已经有超过两亿人在每天登陆使用,每天还有超过 1500 万条短视频被制作和上传,每天的累计观看数更是达到 150 亿。拥有如此庞大的用户数&…

lsmod命令详解

基础命令学习目录首页 原文链接:http://blog.sina.com.cn/s/blog_e6b2465d0101fuev.html lsmod——显示已载入系统的模块 lsmod 其实就是list modules的缩写,即 列出所有模块. 功能说明:显示已载入系统的模块。 语法:lsmod 说明&a…

javascript模块化、模块加载器初探

最常见网站的javascript架构可能是这样的: 一个底层框架文件,如jQuery一个网站业务框架文件,包含整站公用业务模块类(如弹框、ajax封装等)多个业务文件,包含每个具体页面有关系的业务代码为了减少一个HTTP请求,我们可能…

tiny-cnn执行过程分析(MNIST)

在http://blog.csdn.net/fengbingchun/article/details/50573841中以MNIST为例对tiny-cnn的使用进行了介绍,下面对其执行过程进行分析:支持两种损失函数:(1)、mean squared error(均方差);(2)、cross entropy(交叉熵)。在MNIST中使…

关于element的select多选选择器,数据回显的问题

关于element的select多选,数据回显的问题 在工作中遇到这样一个问题,新建表单时用element的select多选以后,在编辑的时候打开表单发现其他数据能正常显示,多选却无法正常回显。在网上找了很多后,终于解决了这个问题&am…

360金融发布Q2财报:净利6.92亿,同比增长114%,大数据与AI加持的科技服务是新亮点?

8月23日,360金融发布未经审计的2019年第二季度业绩报告。财务数据显示,2019年第二季度,360金融实现收入22.27亿元人民币,较2018年二季度9.79亿元增长128%;净利润为6.18亿元,而去年同期为净亏损1.42亿元&…

SPRING3.X JSON 406 和 中文乱码问题

2019独角兽企业重金招聘Python工程师标准>>> 简要 最近使用Spring3.2.3 版本 在使用 JSON message convertion 的时候,老是出现406 返回类型不匹配的问题,去网上google 了一番 也没有一个明确的说法,只能自己去调试。 Maven 依…