ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

达梦数据库DMGEO空间数据迁移与实战指南

达梦数据库DMGEO空间数据迁移与实战指南 干了这么多年GIS后端最烦的不是算法难写而是项目要从Oracle迁到国产数据库时JAVA这边一堆代码没问题空间数据这块却总是第一个卡壳。前两年做某地自然资源项目甲方明确要求数据库国产化替换我第一反应就是空间字段怎么办SDO_GEOMETRY那一堆函数还保不保后来落地用了达梦数据库的DMGEO模块折腾了小两周总算把空间数据整条链路跑通了。这篇文章就把DMGEO从环境准备、建表入库、空间查询到索引优化、问题排查的完整实现过程写透给正要做达梦空间数据迁移或者从零上GIS项目的人一个可以直接参考的路线。达梦数据库在国产库里算是功能覆盖面很全的DMGEO这个空间数据模块就是专门干这个的。它能存点、线、面这些矢量几何对象能做相交、缓冲区、包含、距离计算这些空间分析也能建空间索引跑大数据量查询。说白了对标的就是Oracle Spatial、PostGIS这类空间扩展。适合谁看如果你正在做国产化迁移、需要把SDO_GEOMETRY换掉或者要在达梦上从零开发一套带电子地图、范围查询、区域统计的GIS系统这篇文章的实操部分可以直接抄作业。1. DMGEO到底是什么达梦空间数据模块的能力边界1.1 空间数据在达梦里的两条技术路线很多人一上来就搜DMGEO然后在达梦官方文档里又看到SPHEREEX瞬间懵了。这两者关系得先捋清楚不然建表时选错类型后面迁移就麻烦。达梦的空间能力大致有两条路线。一条是DMGEO它是达梦较早推出的空间数据组件提供ST_Geometry这一系列类型以及配套的存储过程、函数和空间索引能力跟PostGIS的用法的确很像。另一条是SPHEREEX你可以把它理解成Oracle Spatial兼容层提供SDO_GEOMETRY类型让从Oracle迁过来的应用基本不改SQL就能跑很多从Oracle转达梦的老项目能省不少事。我在实际项目里的选择是新系统优先用DMGEO的ST_*系列SQL风格清爽和PostGIS迁移路径也近老系统如果原来就是SDO_GEOMETRY满天飞那就老老实实走SPHEREEX兼容路线。这里有个容易踩的坑两条路线的函数名不能混用。你不能建表用SDO_GEOMETRY类型查询却写ST_Intersects然后抱怨函数不存在。到底用哪条线设计阶段就要定死我见过有项目两套混着用最后维护时SQL一团乱麻。1.2 DMGEO能做什么和PostGIS、Oracle Spatial的功能对照DMGEO的核心能力可以归纳成三类空间存储、空间查询、空间分析。空间存储就是支持ST_Point、ST_LineString、ST_Polygon、ST_GeometryCollection这些对象类型并且能认WKT、WKB这些通用格式坐标参考系SRID也一并存进去。空间查询主要指基于空间关系的过滤比如两个图层谁和谁相交、谁包含谁、距离在多少米内这类查询配合空间索引可以把扫描范围大大缩小。空间分析则是缓冲区生成、面积周长计算、合并求交这些偏计算的操作。我做个了功能对照表方便你评估迁移工作量能力DMGEO达梦Oracle SpatialPostGIS几何类型ST_Geometry系列SDO_Geometrygeometry系列几何构造ST_GeomFromTextSDO_GEOMETRY SDO_UTILST_GeomFromText相交判断ST_IntersectsSDO_RELATEST_Intersects缓冲区ST_BufferSDO_GEOM.SDO_BUFFERST_Buffer距离计算ST_DistanceSDO_GEOM.SDO_DISTANCEST_Distance面积/长度ST_Area / ST_LengthSDO_GEOM.SDO_AREAST_Area / ST_Length空间索引空间网格索引SDO_INDEXR树等GIST索引从上面能看到DMGEO的函数命名和PostGIS几乎是一个路数这对我这种从PostGIS转过来的人是相当友好的。另外达梦自带的管理工具里装上DMGEO之后也能直接看几何字段的可视化预览不像Navicat那样看到一长串二进制头大。2. 环境准备与第一个空间表从安装到点亮DMGEO2.1 安装达梦8并启用DMGEO/SPHEREEX达梦数据库的安装这里不展开讲但有两个跟空间模块强相关的点我必须提醒。第一如果你用的系统是OpenEuler 24这类比较新的发行版装达梦8老版本安装包时容易碰到gzip: stdin: invalid compressed data --crc error这类解压报错多半是安装介质下载不全或者解压工具版本太旧重新下载校验MD5或者换个unzip/gzip版本再解压基本能解决。第二DMGEO不是默认装MySQL那种装完就有达梦8安装完成后空间模块可能需要单独启用或注册有的版本会把DMGEO的初始化脚本放在安装目录的dmdbms目录下有的版本则依赖SPHEREEX安装包单独部署。我建议的做法是装完数据库后先用系统管理员账号登录达梦管理工具跑一遍空间模块相关的初始化脚本或检查包是否可用。最简单的验证方式是执行一条和DMGEO相关的系统查询如果函数不存在或类型不存在就需要回到安装介质里找扩展包。SPHEREEX更明显它一般有独立的安装步骤装完后才能用SDO_GEOMETRY这样的Oracle兼容类型。2.2 坐标参考系与几何类型选择做空间数据第一步不是建表而是想清楚坐标系。DMGEO里每个几何对象都带SRID也就是空间参考标识。国内GIS项目最常用两个4326是WGS84经纬度做互联网地图、GPS数据常用4490是CGCS2000经纬度国产生态下很多基础地理数据都用它。如果项目里的数据是平面坐标比如高斯克吕格投影的X/Y米制坐标那就得用对应的投影SRID不能拿着经纬度和投影坐标混着算算出来的距离、面积完全是错的。几何类型的选择也讲究。如果业务上只需要图形轮廓就用ST_Polygon或者ST_MultiPolygon如果做管网、道路这种线状要素用ST_LineStringPOI点就用ST_Point。这里特别提醒一张表里几何字段能存混合类型但建空间索引和做查询分析时混合类型往往会导致过滤效率下降性能调优也麻烦。所以设计表结构时我一般建议一个几何字段尽量只存一种几何类型除非你有充分的理由做GeometryCollection。-- 如果走SPHEREEX兼容路线 CREATE TABLE T_SCHOOL ( ID NUMBER PRIMARY KEY, NAME VARCHAR2(100), SHAPE SDO_GEOMETRY );2.3 建表、插数据、空间索引的最小完整示例下面这套SQL是我在达梦8上验证过的DMGEO最小可用示例你可以直接照着敲。首先是建表和插入点数据CREATE TABLE T_POI ( ID NUMBER PRIMARY KEY, NAME VARCHAR2(100), CATEGORY VARCHAR2(20), SHAPE ST_GEOMETRY ); INSERT INTO T_POI (ID, NAME, CATEGORY, SHAPE) VALUES (1, 人民小学, school, ST_GEOMFROMTEXT(POINT(116.397 39.908), 4326)); INSERT INTO T_POI (ID, NAME, CATEGORY, SHAPE) VALUES (2, 中心公园, park, ST_GEOMFROMTEXT(POINT(116.402 39.915), 4326));插入完成后建议先查询验证一下几何对象是否正常SELECT ID, NAME, ST_ASTEXT(SHAPE) FROM T_POI;如果这步能正常返回WKT字符串说明DMGEO已经被点亮了。接下来建空间索引这一步非常关键没有空间索引后续查询就是全表扫描数据量一大直接卡死。CREATE INDEX IDX_POI_SHAPE ON T_POI(SHAPE) INDEXTYPE IS ST_SPATIAL_INDEX;达梦空间索引的具体建法和版本有关有的版本也能在CREATE INDEX语句里指定空间索引类型。但核心思想是一致的给几何字段建的是空间索引不是普通B树索引。你如果拿普通索引去建几何字段大概率报错或者根本不起作用。3. 常用空间函数实战一个完整的GIS查询场景3.1 数据入库与格式转换真实项目中不会只靠INSERT一条条塞数据更多是从SHP、GeoJSON、Oracle迁移过来。SHP文件入库我常用的姿势是先用GDAL把SHP转成WKT或者WKB批量文本再拼INSERT语句或者用达梦的导入工具导入。如果你原来的库是Oracle Spatial的SDO_GEOMETRY迁到DMGEO时不能直接二进制搬必须把几何转成WKT再重新构造。我写过一个小脚本import oracledb import dmPython # 连接Oracle取WKT连接达梦写WKT # 这里只演示转换逻辑 oracle_rows cursor.execute(SELECT SDO_UTIL.TO_WKTGEOMETRY(shape) FROM t_old).fetchall() for row in oracle_rows: wkt row[0] dm_cursor.execute( INSERT INTO T_POI(ID, NAME, SHAPE) VALUES(:1, :2, ST_GEOMFROMTEXT(:3, 4326)), (id_value, name_value, wkt) )达梦的Python驱动dmPython用法和cx_Oracle很接近对从Oracle转过来的人基本零学习成本。一定要记住空间对象跨库迁移最稳的中间格式是WKT其次是WKB千万别直接复制二进制字段投影和内部编码对不上就是灾难。3.2 空间查询与空间分析常用函数我把实际项目里最常用的DMGEO函数列了一份清单每一类都带一个能用得上的SQL片段。相交判断是最常见的空间过滤。比如我要查某条路两边的地块本质就是找和这条路的缓冲区相交的所有多边形SELECT a.NAME FROM T_PARCEL a WHERE ST_INTERSECTS(a.SHAPE, ST_BUFFER(ST_GEOMFROMTEXT(LINESTRING(116.40 39.90, 116.42 39.92), 4326), 50)) 1;这里50的单位取决于坐标系如果SRID是4326这个50就是度而不是米必须注意。实际项目里做周边多少米这种需求通常会先把经纬度数据投影到米制坐标系再算或者使用专门的投影转换函数。距离计算和面积计算也是高频操作SELECT a.NAME, ST_DISTANCE(a.SHAPE, b.SHAPE) AS DISTANCE FROM T_POI a, T_POI b WHERE a.ID 1 AND b.ID 2;面积计算直接ST_AREA返回的面积单位同样和SRID强相关经纬度坐标算出来的是平方度毫无业务意义。做规划项目统计地块面积必须先确保数据在合适的投影坐标系里再让甲方签字确认面积口径。3.3 实战以小区周边3公里找学校为例写SQL拿一个具体业务来串一遍需求是给定一个小区中心点找到周边3公里范围内的所有学校并按距离从近到远排序。SELECT s.NAME, ST_DISTANCE(s.SHAPE, ST_GEOMFROMTEXT(POINT(116.405 39.905), 4326)) AS DIST FROM T_SCHOOL s WHERE ST_INTERSECTS( s.SHAPE, ST_BUFFER(ST_GEOMFROMTEXT(POINT(116.405 39.905), 4326), 0.03) -- 约3公里视纬度折算 ) 1 ORDER BY DIST;这里我故意用0.03度做示例就是想强化一个意识度坐标系下缓冲区的单位要会换算。按一度约111公里估算0.03度大约就是3.3公里但高纬度地区东西方向会缩水严谨的做法是先用投影函数或者把中心点转为米制坐标算缓冲区再转回原坐标系做相交。达梦DMGEO如果提供了投影转换相关的函数优先用投影函数没有的话就在外部代码里完成坐标转换再把WKT传进SQL。4. 空间索引与性能优化别让空间查询变成全表扫描4.1 空间索引是怎么工作的空间索引和普通B树索引思路完全不同。普通索引是一维值比较空间索引则是把二维空间切格子。DMGEO的空间索引会把几何对象落在哪些网格里记录下来查询时先快速筛出和目标区域有交集的格子再精算格子里的几何对象。这就是网格索引的基本思想。理解了这一点你就能明白为什么空间索引对数据分布很敏感。如果一批数据全部堆在同一个区域网格再小也很难分离最终回表精算的数据量巨大。反过来说如果数据是稀疏分布在整个城市的点要素一个合适的网格层级能快速把查询范围缩小到几个格子性能提升非常显著。我遇到过最夸张的案例一张几百万行的地类图斑表没有空间索引时根据范围过滤要跑十几秒建完空间索引后压到几十毫秒差距就在这里。4.2 建索引的参数和经验值关于空间索引的参数很多刚接触的人喜欢问网格设多大好。我的经验是网格尺寸最好接近你业务查询中最常出现的查询窗口大小或者接近数据的平均要素密度。如果网格太大一个格子里的要素太多过滤效果差网格太小索引本身占空间不说查询时要合并的格子数量也暴涨。实操上我一般这样定先对数据做一个简单的统计了解要素数量和空间分布范围用合适的网格参数建索引然后拿典型业务SQL跑一遍看执行计划。如果发现空间索引没被用上优先检查统计信息是否陈旧重新收集统计信息往往比疯狂调整索引参数更有效。有时候你在达梦的执行计划里看到CLUSTERBTR这一类普通B树聚集扫描路径而不是空间索引扫描路径那就要警觉了八成是SQL写法或者索引类型有问题空间查询退化成了全表过滤。顺带说一句达梦产品线里有DMDW数据仓库方向和DMDSC共享集群高可用方向这些不同组件和DMGEO定位不同别搞混了但它们的统计信息机制是相通的。4.3 慢查询排查与执行计划里的空间索引排查空间查询慢的思路和普通SQL差不多先看执行计划确认到底走没走空间索引。达梦管理工具里可以直接查看执行计划重点找这个几何字段的过滤条件是不是通过空间索引完成的。如果走了索引还是慢再往下查就是数据量问题或者几何复杂度问题。几何对象特别复杂比如一个多边形有几十万个顶点即使索引把对象筛出来了精确相交计算也要付出巨大代价。这类问题通常要靠简化几何或者分治处理来解决。还有一个常见坑是统计信息过期数据变更量大以后没重新收集统计信息优化器错误估算行数选了全表扫描解决办法很简单定期执行统计信息收集。5. 高频问题与排查实录从迁移报错到Navicat连接5.1 迁移时报错误号-3236这类失败怎么定位达梦迁移报错号是负号很多人一看负号就慌。以错误号-3236为例我遇到过类似场景多半不是SQL语法错而是对象映射问题。比如源库是Oracle Spatial的SDO_GEOMETRY目标库没装SPHEREEX兼容层迁移工具试图把SDO_GEOMETRY当成普通自定义类型转换直接卡死。解决办法很直接先把SPHEREEX装上或者把源数据在Oracle里就用SDO_UTIL.TO_WKTGEOMETRY转成WKT文本再导。在达梦官方的迁移工具或者自写的迁移脚本里空间字段最好单独处理不要和普通字段一样粗暴映射。我见过项目因为迁移工具不支持空间类型干脆在目标库里先把空间字段建成VARCHAR2存WKT应用层再统一转换成ST_GEOMETRY这样虽然绕了一圈但迁移稳定性极高。5.2 Navicat连接达梦查空间数据、备份还原后空间对象失效Navicat能连达梦但查空间字段时默认显示的是二进制或类型对象可读性很差。这不代表数据错了只是工具没做过空间可视化。需要直观确认几何内容时用ST_ASTEXT把几何转成WKT再看SELECT ID, NAME, ST_ASTEXT(SHAPE) AS WKT FROM T_POI;如果要在地图里看通常的做法是把WKT导出成GeoJSON或者直接用后端引擎渲染Navicat本身不是GIS工具不必苛求。连接失败的问题另说很多Navicat连接达梦突然连不上其实是连接池把连接占满了排查时先看达梦会话数和空闲连接别上来就怀疑数据库挂了。备份还原这块我吃过亏。达梦的DMP导出导入如果当前库装的是旧版本DMGEO备份文件拿回新环境还原后空间索引经常失效。因为索引的元数据和版本强相关跨小版本恢复后重建索引是常规操作我在还原后的例行脚本里永远放着这么一句ALTER INDEX IDX_POI_SHAPE REBUILD;5.3 环境类问题汇总连接池、dmp还原、开发账号申请整理一个速查表把这段时间我在社区群里被问最多的问题列出来现象常见原因处理建议达梦数据库突然连不上连接池连接耗尽或防火墙拦截查看达梦会话数检查监听状态连接池配置调整最大连接数Navicat连达梦报驱动问题未用达梦官方JDBC驱动换达梦自带驱动包注意和数据库大版本匹配装达梦报gzip crc error安装介质损坏或解压环境问题校验介质完整性更换解压工具重新下载dmp还原后空间查询报错空间索引状态失效或组件版本不一致重建空间索引核对DMGEO/SPHEREEX版本开发测试账号申请以后用不了授权范围未含空间对象操作权限用DBA账号给账号单独授权空间函数和表的执行权限连接池配置这个问题在Java项目里尤其常见hikrcp连接达梦时driverClassName要写达梦的JDBC驱动类名url里带上达梦的通信端口和库名。如果应用里还要接nacos这类注册配置中心适配达梦时记得把数据源和空间函数初始化放到同一个事务上下文避免初始化顺序错乱导致空间函数不可用。6. 一些实际操作体会做DMGEO项目这段时间我最深的体会是国产数据库的空间能力基本盘是够用的但需要把空间数据要特殊处理的思维刻进团队每个人脑子里。普通字段迁完就能跑空间字段涉及到坐标系、类型、索引、扩展包哪一环漏掉都会在后续爆雷。建议第一次做达梦空间项目的团队前期花半天时间把DMGEO的类型和函数列表通读一遍再拿一份小的真实数据跑通全流程绝对比边写边查效率高得多。最后分享一个小技巧所有空间表的几何字段命名、SRID、坐标系注册信息我都在项目交付文档里单独建一张配置表记录后续任何人接手都不用再靠猜省下的沟通成本远比想象中大。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进