【Oracle】准实时大规模数据提取

article/2025/8/21 23:14:35

文中使用的Oracle版本为10g。

这篇文章是之前本人在前公司内部做可行性分析报告中的其中一个板块的内容,具体讲述的是为了做大规模数据提取和数据清洗做了一个试验demo。先说结论,一般来说像这种操作不应优先考虑关系型数据库去解决。本文中提到的采用Job模拟多线程操作的方式只是不得已而为之,请各位看官慎用。

前因

需要将数据库中表(190+)按照人员编码和业务发生时间进行汇总和排序,得到用户在某时间内的所有操作行为。一般情况下想到使用UNION ALL连接后去重排序,但实际上会面临以下6个问题:

  1. 需要数据提取的表目前有197个且以后还会随着业务增长而增加(不要问为什么这样设计,别人项目管不了);

  2. 表中数据都是千万级或者亿级,查询获取数据量大;

  3. 数据获取动作要求准实时,也就是说在用户触发后需要短时间内获得反馈结果;

  4. 服务器性能低,基本上不存在系统调优的情况;

  5. 数据库表不能进行改造,会有别的项目直接访问这些表(不能理解知道为什么允许这样做);

  6. Java后台程序尽量保持良好的可读性;

基于上述的情况,其实不难分析出两点:

第一,表个数不固定,所以功能必须是可动态配置的;

第二,大量的表和大量的数据获取,在SQL语句上不能直接使用UNION ALL操作。需要考虑到可执行SQL 语句长度会受到限制。譬如,每一个表SQL查询格式将会是

select 字段a,字段b,字段c,字段d,字段e,字段f 
from 表名
where gather_person = 'gather_person内容'
and gather_date between to_date('开始时间','yyyy-mm-dd HH24:MI:SS') 
and to_date('结束时间','yyyy-mm-dd HH24:MI:SS');

这里通过WHERE条件缩小表数据查询范围,在得到数据集后再进行UNION ALL其他数据集操作。但是这197个表下来这个SQL语句将非常庞大。运行起来会占用大量的CPU和内存资源。

其次,即使是使用了UNION ALL连接数据库也是采取串行的查询方式,对于速度提升没有太大帮助,因此使用UNION ALL的方案先按下不表。

既然不能通过一次查询得到结果,那就采用分步获取后再统一查询这种方法(虽然有点麻烦)。通过存储过程进行分步收集数据后再通过统一查询得出结果,初步逻辑结构如下:

在这里插入图片描述

存储过程的处理逻辑基本按照上图所示。这里最重要是利用job来模拟多线程处理来提高数据获取效率。job会分别起一个会话来获取数据,执行后数据将会放入一个结果表中。该结果表作为获取数据存储对象按照基础字段建立好索引和分区。最后条件查询结果表将去重后的结果集放入游标返回。

由于用户会进行多次数据获取操作,无可避免结果表中会存在重复的数据出现。这部分重复数据将会在晚上执行另一个job进行数据清理。

测试代码讲解

这次的存储过程是用package来做,因为整个查询和数据获取的过程都是一个整体,所以放在一起在可读性和完整性都比较好,如下图:

在这里插入图片描述

package提供存储过程接口,如下图:

在这里插入图片描述

存储过程入口是generate_trajectory方法,dynamic_thread和batch_generate都是嵌套调用的关系,而最后的generate_test_data是用来生成测试数据的,在实际操作上不需要用到。

其中package body 的generate_trajectory方法,如下图:

在这里插入图片描述

定义了各种变量,最后为job定义了一个动态数组

在这里插入图片描述

为传入参数设定一个默认值,由于传入参数中若设定默认值会被传入NULL所替代,所以在这里需要用nvl函数进行预设。在获取数据之前先查询该用户在该时间段是否已经存在数据在结果表中,若存在先进行删除。

在这里插入图片描述

根据传入参数thread和配置表中数据进行除法,向下取整后得到一个job中需要执行多少个表的统计,并按照数据进行后续的分页统计。这里调用了dynamic_thread方法,这个方法执行后将返回job编码,获取job编码后放入动态数组中。

由于除法会存在不整除的情况,这部分数据获取将会单独拿出来进行获取,这里直接调用batch_generate方法处理即可。

在这里插入图片描述

通过循环去监听全部Job是否已经全部完成,直接统计结果表中去重后的tablename个数,由于每个表的插入语句都是批量插入的,若结果表中存在该表名,则证明该表已经获取完毕(下面会讲到)。

在这里插入图片描述

这里对退出循环监听有一些判断,还有的就是超过了5次循环后将会认为超时会强制退出。在这里为了不频繁查询结果,使用了dbms_lock.sleep系统函数做了一下休眠。

在这里插入图片描述

退出监听后将会认为是数据获取完毕,这时就需要删除掉刚刚产生出来的job,最后通过查询语句将结果集放入到一个游标中进行返回。

package body 的dynamic_thread方法,如下图:

在这里插入图片描述

这个存储过程就是用来生成job用的,job参数是必须要返回的,不然不知道对应的job是那个。

package body 的batch_generate方法,如下图:

在这里插入图片描述

定义好各种的变量和游标。

在这里插入图片描述

游标SQL用来分页获取配置表中的字段和表名。

在这里插入图片描述

通过遍历游标获取到变量信息后进行了一些数据的replace操作是为了使结果表与原始表字段能对应起来。将插入语句写成 insert into… select … from …的方式进行批量插入当中用到/*+APPEND*/进行直接路径插入加快插入速度。

在这里插入图片描述

至此处理过程结束。

package body 的generate_test_data方法,如下图:

在这里插入图片描述

传入参数是rowcounts和gather_person_custom字段,调用这个存储过程的时候可以定义给配置表中定义的每个表批量新增多少条数据,并且这些数据对应的gather_person为那个人。方便数据库层面对数据进行调试。

在这里插入图片描述

这里的语句针对目前配置表中数据进行整理,其中包括一些表复合主键的问题处理。

在这里插入图片描述

遍历游标获取到表名,时间字段和主键字段。插入前先删除之前上次操作的该人员的数据,并对表名进行修改,后续需要创建临时表,所以这里需要对表名进行修改保证表名在30个字符以内。

在这里插入图片描述

这里用了类似java的try…catch…方式创建表。其中create table … as select … from …在创建临时表时可能会出现表已存在的情况,这时候需要先将已存在的表删除(因为已存在的表中还存在数据,与其删除数据,还不如用drop table … purge方式连表一起删除,这样还可以释放一些表空间),之后再重新新建表。

在使用create table … as select … from …创建表的时候其实数据也复制了过去了,所以之后添加数据可以通过count方式获取到临时表中记录条数,若新增条数达到用户设定界限则退出循环。若有些表本来就不存在数据的情况下就直接退出循环。

循环插入数据是通过insert into … select … from …的方式进行插入,由于各个表的字段不同,这里就用*来代替了。

在这里插入图片描述

数据插入完成后,先在临时表中进行数据整理,包括更新时间、更新主键和更新gather_person。然后再重新批量插入回原始表中。插入完成后将会把临时表删除释放表空间。

测试结果

v1.0

首先使用了generate_test_data方法为这190+张表每张表插入10w条数据。存储过程第一次获取数据此时结果表为空,启动参数如下:

在这里插入图片描述

虽同时启动10个job执行但结果不太理想,共耗时11138秒,如下图:

在这里插入图片描述

这样的结果显然是不能接受的,于是想出了第二套方案。

v1.1

在generate_trajectory方法中屏蔽了删除原来数据的操作,如下图:

在这里插入图片描述

由于可以通过晚上从定时器上进行去重工作,这部分的删除工作可以忽略不做来减少时间。

接着在数据插入之前先删除表中的所有索引,如下图:

在这里插入图片描述

在启动多个job执行之前先干掉结果表中的所有索引来提高插入速度。

再者,循环启动多线程的时候设定一个休眠时间,如下图:

在这里插入图片描述

休眠时间为每个线程生成产生一个时间差,不至于多个线程同时启动抢占资源造成锁表现象出现。

将原来“查询结果表中表的数量是否与配置表中一致”的判断条件去掉,换成用job是否完成来判断,如下图:

在这里插入图片描述

结果表中数据量庞大的时候用count来统计表中数量,其实是做了全表扫描性能很差,查询一次要差不多180+秒。于是回归原点来想了一下,反正这里为了都是判断所有的job是否完成这样的话还不如直接找job的信息来判断,于是想到了user_jobs表。当表中this_sec(执行时间)为NULL则表明job已经完成了这时就可以remove掉,若不是就休眠5秒再来判断。

以查询速度来看,这个比之前查询结果表统计的时间少很多而且更能够确切知道job是否已经完成了,完成一个干掉一个。

在删除掉job之后就要重新为结果表添加上索引,如下图:

在这里插入图片描述

在最后查询的时候将并行查询打开,如下图:

在这里插入图片描述

由于结果表是一个分区表,而且数据量庞大。所以查询开启并行觉得很有必要。

在这里插入图片描述

在创建表的时候就将并行开启了。

同理,在batch_generate方法中,在插入部分也将并行开启了,如下图:

在这里插入图片描述

维护数据方法data_maintain里面将去重的删除方法改成分批删除,并且通过游标查询找到rowid,直接通过rowid定位进行delete操作,提高删除效率。如下图:

在这里插入图片描述

每一个批次10000数据删除。

v1.1测试结果(新增插入)

在这里插入图片描述

同样的启动参数,整个获取过程用了1949.778秒。在相同的条件下(结果表为空)v1.1比v1.0中的快了差不多6倍。

v1.1测试结果(存量更新/插入)

在这里插入图片描述

在原有数据的基础上再一次执行获取数据,用了2476.376秒。

v1.2

再继续查看服务器允许的最大并行数

在这里插入图片描述

修改结果表的并行度改成16

在这里插入图片描述

修改存储过程中并行数为14

v1.2测试结果(新增插入)

在这里插入图片描述

在结果表为空的情况下获取的结果是1933.32秒,看上去增加并行数并没有减少执行时间。再深入到获取每个方法的执行时间后发现,其实时间都消耗在创建索引的地方去了,因为创建索引是需要内部排序的,三个索引就需要全表扫描三次,这个时间的确是不可忽视。

结论

像这种需要获取大量大表的操作,在关系型数据库中其实不太适合实时获取的,若将这部分数据都放在后台拆分后定时获取效果将会好很多。在使用并行操作后性能瓶颈已不在插入操作,而在重建索引。如果在插入数据前不屏蔽索引又会产生大量索引的redo信息,导致插入慢情况。

经过比较后发现,插入后重建索引效率比直接插入的效率还要高,但是根据demo来看,试验结果还是令人不太满意。目前存储过程在不重建索引的情况下能够将190+表共1800w+条(设定值内)数据在15分钟内插入完成,因此重建索引问题不解决,不推荐使用关系型数据库做类似的获取操作。


http://chatgpt.dhexx.cn/article/BUUnTFeI.shtml

相关文章

实时数据流采集工具Flume

实时数据流采集工具Flume 实时数据流采集工具Flume1.1 Flume的介绍1.2 Flume的特点1.3 Flume的功能架构1.4 Flume的功能原理1.5 Flume的安装部署1.6 Flume两种常见基础架构1.6.1 多路复用流Multiplexing The Flow1.6.2 Consolidation 1.7 Flume中常用的三大基础组件1.7.1 sourc…

必须了解的实时数据架构

随着互联网的发展进入下半场,数据的时效性对企业的精细化运营越来越重要, 商场如战场,在每天产生的海量数据中,如何能实时有效的挖掘出有价值的信息, 对企业的决策运营策略调整有很大帮助。此外,随着 5G 技…

puppeteer-0-大背景:服务端:生成图片 合成海报 截屏

背景 最近接手一个任务,大致要求就是可以动态合成图片。 没听懂?那我再解释下: 大致就是如上功能。 这个时候,会的,或者稍微会的,或者真的会的,就开会七嘴八舌了:吧唧吧唧.... 前…

小程序多张图片合成海报分享功能

项目有个需求:代言人模块分享出去时。需要合成几张图片作为一张海报分享出去 需要用到canvas组件,组件的部分合成图需要下载下来,所以后端要配置好下载合法域名如下图,配置ok后开发者工具会看到合法域名 代码逻辑 1、点击分享弹框…

php生成推广二维码海报、合成图片demo

php生成推广二维码海报、合成图片 1、海报背景图。背景图一般存服务器,程序本地读取; 2、推广二维码。可以是二维码图片链接,也可以是字符串图像流。如果自己生成二维码,详见phpqrcode官网,地址:https://sourceforge.net/projects/phpqrcode。 3、开启PHP的GD扩展 inde…

微信小程序海报生成图片合成工具类

背景 我目前参与小程序的产品研发,为了方便产品的转发和推广,会对课提供生成海报转发或者分享的功能,前期海报合成这个功能是由项目组的老同事负责开发,后来小程序海报这块功能需要单独做一个功能用于专门根据不同的课程类型来生…

平面设计新手如何用PS制作出一张合成海报

本文由:“学设计上兔课网”原创,图片素材来自网络,仅供学习分享 平面设计新手如何用PS制作出一张合成海报?本期教程给大家制作一张关于VR战争合成海报,从合成海报来讲我们要做到以下几点: 创意部分:如何通过创意表现产品功能或其他想要表现的点, 有了创意找到合适的…

小程序画布合成二维码海报图,并保存到相册

小程序画布合成二维码海报图,并保存到相册 实现效果如下图: 步骤分析 1,先获取需要合成的海报和二维码。 2,获取图片的本地路径,如果图片不是网络连接则不需要此操作。 3,通过手机型号,根据…

拿来就用的Java海报生成器ImageCombiner(一)

背景 如果您是UI美工大师或者PS大牛,那本文一定不适合你;如果当您需要自己做一张海报时,可以立马有小伙伴帮您实现,那本文大概率也不适合你。但是,如果你跟我一样,遇上到以下场景,最近公司上了不…

ps海报合成教程_怎样合成创意环保海报图片的PS教程

效果图合成的非常不错,作者用虚实结合的方法来表现主题。创作的时候先构思好想要的画面,然后搜集相关素材(没有的素材可以想办法自己拍摄),然后按自己思路溶图,润色即可。最终效果 一、拖入原素材,素材大家可以在网络上…

如何制作动态海报?教你一招在线合成GIF海报

相信大家在网上都见过静态海报吧!那么,当我们想要将静态的海报制作成炫目的动态海报来吸引大众的目光的时候,应该如何在线制作GIF动态海报呢?这时候,大家就可以使用**gif制作**工具,上传图片一键就可以在线…

设计素材|这些创意海报合成,你会发现创意比技术更重要!

是不是各位也同样收到过这样的话:“这次的海报还是缺点创意啊”、“再改改,润润色”; 想创意熬了三天,海报改了无数版,结果领导还是不满意。 随着设计行业竞争的日益激烈 公司对设计师的水平要求越来越高 如果你是…

怎样合成gif海报?手把手教你一键制作动态海报

相信不少新媒体小编们,在工作中会遇到需要自己设计海报的情况。如果是设计一般的静态海报使用一些修图软件就可以制作,但是当我们需要将这些静态海报变成gif动图海报的时候,应该如何制作gif动态图片海报呢?接下来,小编…

PHP使用Grafika合成图片,生成海报图

需求背景: 在小程序上生成海报图,但在保存图片时,只能保存其中的小程序码图片,保存下来的图片过于单调,且无法确认该图片的作用性,所以需要调整为保存一整张海报图。 海报效果图: 需求分析&a…

合成海报的小程序插件-票圈海报

概述 票圈海报 是一个用于生成海报的小程序插件,通过灵活简单的配置(JSON)就可以生成精美的分享海报,适用于小程序的朋友圈分享等营销裂变场景。 可以绘制文字、图片、线条、色块到海报画布,支持设置宽高、透明度、层级甚至圆角等属性。 亮…

PHP 海报二维码合成

组合参数方法: /*** [user_qrcode 参数组合]* param [type] $bgImg [背景图]* param [type] $codeImg [二维码]* param string $name [名称]* param string $remask [备注]* param string $content [描述]* param integer $key [生成二维码名称…

vue两张图片合成一张(海报二维码合成工具)

公司要经常换海报&#xff0c;一个海报要不同的二维码&#xff0c;做一个工具解决一下。 效果图如下 二维码图片的位置和海报大小可以随意调整 直接放代码&#xff1a; <template><div class"all-tool"><div :style"bgStyle" class"…

【Web技术】961- 3分钟搞定海报合成

背景 在推广业务中&#xff0c;常常会遇到合成带二维码海报分享功能&#xff0c;并且为了推广力度&#xff0c;需要同时在APP、WEB、小程序都有此功能加大曝光&#xff0c;各端都需要单独编写&#xff0c;复用能力差&#xff0c;效率低。本身合成海报业务并无难度&#xff0c;在…

如何合成动态海报?手把手教你一键在线合成gif海报

相信大家在平时都见过那种gif动态海报图片吧&#xff01;是不是觉得只有专业的设计师才能制作呢&#xff1f;其实&#xff0c;这种gif动态海报制作起来非常的简单&#xff0c;只需要准备几张图片尺寸相同图文内容不同的图片&#xff0c;再使用**在线动画制作**工具&#xff0c;…

波束形成MATLAB代码

常规的波束形成方法 clc; clear; close all;fs 1000; c 150; N 128; f 100; lambda c / f; d 0.5 * lambda; theta 1: 1: 180; t (0:1:1000-1) / fs;A zeros(1, length(theta)); A(5) 5; A(20) 4; A(25) 5; A(50) 3; % 在这四个方向上有目标 S zeros(length(th…