尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Oracle到瀚高数据库数据抽取工具:从类型映射到断点续传

发布时间:2026/9/25 8:44:28

资讯中心
01
ARTICLE

Oracle到瀚高数据库数据抽取工具:从类型映射到断点续传

Oracle到瀚高数据库数据抽取工具:从类型映射到断点续传
简介面向 Oracle 与瀚高HGDB数据库运维及数据迁移人员这份资源提供可直接运行的数据库抽取工具用于将 Oracle 中的数据抽取、转换后导入瀚高或实现两库间的定期同步重点解决异构迁移时的类型差异、PL/SQL 对象兼容及数据一致性问题。压缩包共 660 个文件总大小约 55.29MB其中包含核心启动组件 exe/jar、动态链接库 dll、配置类 properties 与 xml以及依赖的 JRE7 环境相关资源包内还附带涵盖全球主要时区的映射数据可满足跨地域系统部署与迁移场景整个包内组件归类清晰便于按需取用。目前已有 771 人学习使用。工具基于 JRE7 运行对 Oracle 10g3 版本做了针对性适配适合在数据库升级、系统割接或主备同步场景下快速部署借助内置转换规则与同步机制可有效降低人工迁移出错率提升数据迁移的规范性与可维护性。1. 瀚高数据库抽取工具从 Oracle 到 HGDB先解决“能跑通”再谈性能做数据库迁移时最烦的环节就是从 Oracle 抽数据到瀚高数据库HGDB。Oracle 的 expdp 导出的 dump 不能被 HGDB 直接使用因为两边类型系统、字符集、SQL 方言都不一样。这个瀚高数据库抽取工具的核心思路很简单通过 JDBC 同时连上 Oracle 和 HGDB按配置文件读表结构、做类型映射、分批拉取最后通过 COPY 方式把数据灌入 HGDB。它不是可视化向导是命令行工具适合 DBA 在服务器上反复跑。如果你正在做 Oracle 迁 HGDB 的项目或者需要定期把 Oracle 的增量数据同步到 HGDB这个工具能省掉大量手写 SQL 的重复劳动。2. 准备环境JDBC 驱动、Python 依赖和表结构预检2.1 需要的驱动版本与兼容组合先说结论这个抽取工具依赖 JDK 8 运行时HGDB 的 JDBC 驱动走的是 PostgreSQL 协议Oracle 驱动用 ojdbc8Python 层用 3.8 以上配合 cx_Oracle 和 psycopg2。很多朋友在第一步就翻车是因为用了新版 ojdbc11 连接旧版 Oracle 11g导致登录直接报 ORA-28040: No matching authentication protocol。老实说这种问题排查起来全是泪不如一开始就固定一套验证过的组合。组件版本建议说明JDK8HGDB 官方驱动要求 Java 8 及以上Oracle JDBCojdbc8兼容 Oracle 11g/12c/19cHGDB JDBChgdbjdbc 对应 HGDB版本本质是 PostgreSQL JDBC 的增强版Python3.8 - 3.10推荐 3.9依赖库支持最稳cx_Oracle8.3.0已改名为 python-oracledb但仍可用 cx_Oracle 接口psycopg22.9.xHGDB 兼容 PG 协议用它写入安装命令如下我一般会建一个独立的虚拟环境避免污染系统 Python。python3 -m venv hgdb_tool source hgdb_tool/bin/activate pip install cx_Oracle8.3.0 psycopg2-binary2.9.9 pyyaml这里 cx_Oracle 内部依赖 Oracle Client Library需要保证服务器上能解析到tnsnames.ora或者直接用cx_Oracle.connect()传 DSN。psycopg2 用binary版省去编译数据库驱动的时间如果服务器是离线环境建议把三个依赖包提前下载成.whl拷贝进去。很多同事直接pip install cx_Oracle就跑了结果在无外网机器上现场装不上这就是最基本的依赖管理没做到位。2.2 用 SQL 做抽取前预检在写抽取配置之前我习惯先把源库所有待抽取字段的类型、非空约束、主键列拉出一个清单否则后面跑一半才发现字段类型不支持只能中断重来。你打开任意一个 Oracle 客户端执行这条 SQLSELECT table_name, column_name, data_type, data_length, nullable, column_id FROM all_tab_columns WHERE owner SOURCE_SCHEMA AND hidden_column NO ORDER BY table_name, column_id;注意all_tab_columns会包含虚拟列和隐藏列hidden_column NO可以过滤掉。跑出来的结果建议导出成 CSV作为后续类型映射的输入。还有一条 SQL 也要跑查主键和唯一索引因为单表数据量大的话需要通过主键分片去抽取。SELECT c.table_name, cc.column_name, c.constraint_type FROM all_constraints c JOIN all_cons_columns cc ON c.owner cc.owner AND c.constraint_name cc.constraint_name WHERE c.owner SOURCE_SCHEMA AND c.constraint_type IN (P, U) ORDER BY c.table_name, cc.position;拿到的结果我会直接存成precheck_result.csv然后丢给后续脚本生成初始配置。这一步看似多此一举但能提前排查出来几个常见问题字段类型是NCLOB而 HGDB 没对应类型、表里没有主键导致无法做断点续传、分区表被当成普通表处理导致全表扫描。这些问题在预检阶段发现成本几乎为零拖到抽取跑起来再发现就得反复改配置重启任务了。2.3 Python 脚本的目录结构与入口参数解压工具包之后建议保持下面这个目录结构不要改动文件名因为脚本里到处引用了相对路径。hgdb_extract/ ├── config.yml ├── extractor.py ├── schema_mapper.py ├── resume_state.json ├── sql/ │ ├── oracle_precheck.sql │ └── hgdb_types.txt └── logs/config.yml是唯一的配置入口抽取的表名、连接串、批大小都在里面。extractor.py是主程序负责连接两个库、执行分批查询、写入目标库。schema_mapper.py做 Oracle 类型到 HGDB 类型的映射如果遇到自定义类型会在这里报错。resume_state.json是断点续传的状态文件每次成功抽完一批就会更新它。logs/目录下保留每次抽取的运行日志方便出问题时翻案底。入口参数只有三个--config指定配置路径--table指定要抽取的单个表--debug打印每个批次的 SQL 和行数。我一般在第一次跑某张表时会用--debug确认 SQL 拼接正确再正式全量跑。3. 核心抽取流程连接、映射、分批拉取3.1 建立 Oracle 与 HGDB 双连接主程序的main()里面第一步就是读取config.yml然后建立两个连接。Oracle 用cx_Oracle.connect(user, password, dsn)HGDB 用psycopg2.connect(dbname..., host...)。注意 HGDB 的 JDBC 和 psycopg2 走的是 PG 协议所以连接参数里不要写什么service_name直接写数据库名。import cx_Oracle import psycopg2 import yaml def load_config(path): with open(path, r, encodingutf-8) as f: cfg yaml.safe_load(f) return cfg def get_connections(cfg): oracle_cfg cfg[source] hgdb_cfg cfg[target] conn_oracle cx_Oracle.connect( useroracle_cfg[user], passwordoracle_cfg[password], dsnoracle_cfg[dsn] ) conn_oracle.current_schema oracle_cfg[schema] conn_hgdb psycopg2.connect( dbnamehgdb_cfg[dbname], userhgdb_cfg[user], passwordhgdb_cfg[password], hosthgdb_cfg[host], porthgdb_cfg.get(port, 5866) # HGDB 默认端口 5866 ) conn_hgdb.autocommit True return conn_oracle, conn_hgdbdsn可以直接写cx_Oracle.makedsn(oracle_host, 1521, service_nameorcl)也可以写在一个tnsnames.ora中但为了脚本可迁移我建议用makedsn直接拼。HGDB 端口这里注意很多管理员习惯用 5432但瀚高默认是 5866如果连不上先检查端口是否被改过。设置conn_hgdb.autocommit True是因为我们要批量灌数每批提交一次反而会拖慢速度和增加 WAL 压力交给 psycopg2 内部按批提交更合适。3.2 类型映射规则VARCHAR2 到 varcharNUMBER 到 numericOracle 和 HGDB 的类型名称不一样必须做映射。映射表写死在schema_mapper.py里我列几个典型的Oracle 类型HGDB 类型备注VARCHAR2(n)varchar(n)长度语义一致但 HGDB 没有字节/字符区分NUMBER(n, 0)numeric(n, 0)整数型建议保留 numeric 而非 int避免精度丢失NUMBER(n, 2)numeric(n, 2)金额类DATEtimestamp(0)Oracle DATE 包含时分秒HGDB date 没有时分秒所以用 timestampTIMESTAMPtimestamp(p)p 对应秒/小数位CLOBtext直接映射成 text不要映射成 varcharBLOBbytea二进制大对象RAW(n)bytea原始字节数组NCHAR/NVARCHAR2varchar(n)HGDB 没有单独的 nchar统一用 varcharROWIDvarchar(18)如果抽取结果里带 ROWID 就放弃或者映射为 varchar这个映射有一个容易踩的坑Oracle 的NUMBER不带精度时在 HGDB 里最好用numeric而不是decimal两者语义接近但 HGDB 的numeric与 PostgreSQL 一样存储上能保留完整精度。另外DATE映射成timestamp后插入时如果源数据只有日期没有时间HGDB 会把时分秒置为 00:00:00不影响业务但如果你在 HGDB 端和数据仓库做对比按天分组时要注意一边是 date 类型一边是 timestamp。我通常把映射规则集中放在schema_mapper.py里每接一个新表前先跑一次预检看有没有不在映射表的类型有就当场补一条规则。3.3 分批拉取与游标优化数据抽取不是一条SELECT * FROM big_table一次性拉完。那样会占用大量 Oracle 临时表空间同时 HGDB 端写入时也会因为单个事务过大而拖慢。常见做法是每次只取 5000 行用主键范围做过滤条件。def extract_table(conn_oracle, conn_hgdb, table_info): table_name table_info[table_name] pk_column table_info.get(pk) if not pk_column: # 没有主键的表走通用查询但会警告 sql fSELECT /* PARALLEL(4) */ * FROM {table_name} cursor conn_oracle.cursor() cursor.arraysize table_info.get(fetch_size, 5000) cursor.execute(sql) else: # 有主键按主键范围分批 lower, upper get_min_max_pk(conn_oracle, table_name, pk_column) step table_info.get(step_size, 100000) cursor conn_oracle.cursor() cursor.arraysize table_info.get(fetch_size, 5000) start lower while start upper: end start step cursor.execute( fSELECT * FROM {table_name} WHERE {pk_column} :1 AND {pk_column} :2, [start, end] ) write_batch(conn_hgdb, table_info, cursor) start end这里cursor.arraysize是 Oracle 客户端预取的行数调大可以减少往返次数但会占内存5000 到 10000 之间最合适。step_size是主键步长比如主键是数值型每隔 10 万取一批如果主键是 UUID 之类的字符串就不能用范围法得用分页或时间戳切片。write_batch内部会把查询结果变成参数化 INSERT或者拼成 COPY 格式。比较推荐使用 psycopg2 的copy_expert批量写入比一条条 INSERT 快一个数量级。写完后要注意每个表做完后更新断点状态否则中途挂了会重头再来。4. 参数怎么调从批大小到断点续传4.1 关键参数表谁的改动对性能影响最大给出一份我压测后比较靠谱的参数表具体值需要你按源库和目标库的 CPU、内存情况再微调。参数推荐值影响fetch_size5000太小则网络往返多太大则 Python 进程内存飙升batch_size5000每次写 HGDB 的行数跟 COPY 块大小相关step_size100000主键分片大小过大导致单次查询太久parallel_workers4同时抽取几个大表注意 Oracle CPU 配额copy_batch_size10000PSQL COPY 每批写入的字节数建议按表行均宽计算retry_count3网络抖动时重试次数retry_interval5重试间隔秒数这些参数写在config.yml里每次跑之前读一遍。你会发现改parallel_workers比改fetch_size效果更明显但前提是目标 HGDB 服务器的 max_connections 够用。我第一次抽取时没注意并行数一台 HGDB 上开了 8 个并发进程直接把连接池打满Oracle 侧也在疯狂全表扫描最后把两边的业务都拖慢了。从那以后我每次调参先看监控再动手。4.2 断点续传机制用记录水位表记录表和主键位置抽取过程中最怕的是运行到第 3 个小时突然断网然后从头再来。工具里是这么处理的在 HGDB 目标库中建一张_extract_state表每抽完一批就 UPDATE 一下这张表记录当前表和已抽取到的位置。CREATE TABLE IF NOT EXISTS hgdb_admin._extract_state ( table_name VARCHAR(128) PRIMARY KEY, last_position VARCHAR(256), updated_at TIMESTAMP DEFAULT NOW(), status CHAR(10) DEFAULT running ); -- 每批抽取完成后执行 INSERT INTO hgdb_admin._extract_state (table_name, last_position) VALUES (CUSTOMERS, 100000) ON CONFLICT (table_name) DO UPDATE SET last_position 100000, updated_at NOW();查询状态时很简单如果last_position大于某个值说明这批已经处理过直接从下个分段开始。这个机制比单纯记录“抽了多少行”更可靠因为主键位置不受表中数据删除影响。last_position建议保存主键的最大值不要保存行号因为行号在 Oracle 的物理存储上不可靠。对于字符串主键把这个值原样存进去下次用字符串匹配。另外状态表本身要放在 HGDB 侧而不是 Oracle 侧因为工具写目标库时顺带更新状态不需要跟源库交互减少两边的事务耦合。4.3 大表抽取策略按分区或主键范围并行如果一张表有 5 亿行靠单个进程按主键顺序拉总会到凌晨还没跑完。这时候把「按表并行」改成「按主键区间并行」会更科学。工具里预留了--range-start和--range-end两个参数让你能够手动切成几段放到多个终端同时跑。# 终端1抽主键 1~1亿 python extractor.py --config config.yml --table ORDERS --range-start 1 --range-end 100000000 # 终端2抽主键 1亿~2亿 python extractor.py --config config.yml --table ORDERS --range-start 100000001 --range-end 200000000这里的关键是保证主键是均匀分布的数值型。Oracle 很多表的主键是序列生成的分布均匀切分效果很好。如果主键是拼接的 VARCHAR例如2024 || lpad(seq)可以用 SUBSTRING 提取前 4 位年份再分片但这样容易产生全表扫描不建议直接用范围。还需要注意两点多个进程同时写同一张 HGDB 表需要关闭目标表上的主键约束和唯一索引否则每个进程执行插入时都会检查约束性能陡降。抽取完后再重建约束并做VACUUM ANALYZE。5. 避坑指南字符集、LOB、自增列和权限5.1 现象中文全部变成问号原因源库 Oracle 的NLS_LANG设置是AMERICAN_AMERICA.US7ASCII而 HGDB 库字符集是UTF8两边字符集不一致抽取时按默认 ASCII 传输中文直接变成??。解决先查源库字符集再在连接时指定编码。若 Oracle 侧是UTF8或AL32UTF8连接字符串里加上encodingUTF8让 cx_Oracle 按 UTF-8 解码。HGDB 端psycopg2.connect()默认也用 UTF-8只要保证两端都是 UTF-8 基本不会出乱码。5.2 现象CLOB 字段抽取出来只有 4000 字符原因fetch_size设置太小或者用游标遍历时把 CLOB 当成字符串直接读取Oracle 客户端默认只取前 4000 个字节。解决对于 CLOB 字段改成在 SQL 中直接转成字符串DBMS_LOB.SUBSTR(col, 4000, 1)分段读取但对于超过 4000 字符的内容要在 HGDB 端用text类型然后分多次读取拼接。更靠谱的做法是不要全量拉 CLOB 到 Python 内存而是使用cursor.fetchone()一行行读取或者用DBMS_LOB.GETLENGTH先得到长度。5.3 现象HGDB 中自增列和序列值比实际数据小原因Oracle 表中如果有自增列例如用IDENTITY或序列抽取时没有同步序列到 HGDB。导致后续业务插入新数据时主键冲突。解决在 HGDB 中把序列值设为当前表最大主键加一。常见做法是用setval修改序列值SELECT setval(orders_id_seq, (SELECT max(id) FROM orders), true);这一步必须在数据全部抽取完成后执行而且要在开启 HGDB 的INSERT权限之前。否则一旦有业务先插入你就只能报错让开发配合清理重复数据了。现在工具包中加入了--sync-seq参数跑完大表自动执行这个 SQL。5.4 现象运行中途提示 “ORA-00942: table or view does not exist”原因你的 Oracle 登录账号只有业务表权限但抽取工具要读取all_tab_columns、all_constraints等系统视图或要访问DBA_*信息权限不足。解决给账号授予SELECT_CATALOG_ROLE或者SELECT ANY DICTIONARY。另外如果你的账号不是表 owner读取表数据时也要有SELECT权限。我在预检脚本里专门加了一段授权检测一旦发现权限缺失就提前报错而不是等抽取开始再中断。5.5 现象TIMESTAMP 精度丢失毫秒被截断原因Oracle 的TIMESTAMP(6)毫秒精度较高HGDB 默认也可用timestamp(6)但抽取过程如果通过 SQL 时间函数格式化就会丢失精度。解决映射时保留精确到微秒的类型并在 Python 代码中不要用字符串格式化直接以 Pythondatetime对象传递。另外注意源库SESSION TIME ZONE和目标库时区是否一致如果两边时区不一致建议在连接参数中统一指定 UTC否则SYSTIMESTAMP相关字段会在入库时偏移 8 小时。6. 进阶技巧校验数据一致性并让抽取速度再翻倍最后分享一个我自己的习惯动作每张表抽取完成不要急着继续先跑一轮校验。校验分两层第一层是行数对比查 Oracle 和目标表的总行数第二层是业务字段的校验简单点用 SUM 配合 MD5 聚合但这种多一张表的大字段比较麻烦。最实用的方法是抽取时由工具对每行计算 MD5存到目标表的一个临时校验列结束后统一比较。-- 抽取完成后在 HGDB 端执行 SELECT COUNT(*) AS total_rows, SUM(HASH_MD5(row_data::text)) AS checksum_value FROM customers;然后在 Oracle 端跑同样的聚合 SQL比对两个结果是否一致。若不一致就可以按主键范围二分排查。这个技巧帮我抓到了不少边界问题比如浮点数精度差异、空字符串和 NULL 的转换差异。再说到加速。抽取到 HGDB 的时候如果允许短暂的不在用状态可以先关闭表上的主键、唯一约束、非空约束等数据灌完再一次性建约束。这样做速度能提升 50% 以上。同时把 HGDB 的wal_level设为minimal或者将表所在表空间临时移动也能减少日志写盘。不过这些操作是 DBA 层面的需要提前在变更窗口内申请。从那以后我每次做 Oracle 到瀚高数据库的抽取都会强制走一遍「预检、小表试跑、断点续传参数确认、数据校验」四步流程也算给后面接手的同事少留点坑。希望帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。