
简介PostGIS 3.3.6 是面向 PostgreSQL 数据库的开源空间扩展安装包适合 GIS 开发工程师、数据分析师以及需要处理空间数据的后端开发者。该版本在 PostgreSQL 基础上补充了空间数据类型、R 树索引、空间函数与操作符、CRS 坐标转换及 GeoJSON/Shapefile 导入导出能力可直接用于构建轻量级空间数据服务。压缩包约 16.98MB共含 2000 个文件以 sql、c/h 源码、po 多语言翻译、wkt 空间对象示例、expected 测试预期文件为主并附带 configure、makefile 及补充文档便于从源码编译、运行回归测试并深入查看内核实现。整体目录以源码、翻译、测试数据与测试脚本为核心结构清晰适合二次开发与问题排查。目前已有 66 人学习适合希望从源码层掌握空间数据库扩展机制、为 PostgreSQL 增加 GIS 能力的读者下载研究。1. 从 postgis-3.3.6.tar.gz 说起为什么还要自己编译很多人看到 tarball 的第一反应是用 apt 或者 yum 装 postgis 不香吗确实在多数发行版仓库里PostGIS 已经作为独立包提供一条命令能省去大量时间。但当你需要精确控制 PostgreSQL 扩展的版本或者要在 PostgreSQL 16 上使用还没有进入发行版仓库的 PostGIS 3.3.6又或者你的生产环境与开发环境必须保持字节级一致时源码编译就成了绕不开的路。postgis-3.3.6.tar.gz 是 PostGIS 3.3.6 的官方源码包它不是能直接运行的二进制而是一堆 C 代码、SQL 脚本和配置文件需要经过 configure、make、make install 三个动作再在数据库里执行CREATE EXTENSION postgis才能真正生效。这个标题对 IT 从业者的意义在于它指向一个经典的带依赖编译流程。PostGIS 不是独立软件它寄生在 PostgreSQL 之上依赖 GEOS、GDAL、Proj 等一堆地理计算库。版本匹配稍有偏差编译期报错还算友好运行时的诡异崩溃才让人头疼。所以下面的内容从解压开始一直讲到验证扩展版本把每一步的关键参数、失败信号和验证方法都摊开。无论你是 DBA、后端工程师还是部署运维按这个流程走完就能在自己的环境里得到一份可复现的 PostGIS 3.3.6。2. 编译前的准备依赖、环境变量与 PostgreSQL 版本匹配2.1 依赖清单与版本硬约束PostGIS 3.3.6 的核心依赖有四个依赖库最低版本作用GEOS3.6提供几何算法运算如相交、缓冲、面积计算GDAL2.0栅格数据读写和格式转换处理后缀为ST_FromGDALRaster的函数Proj4.9坐标系投影转换支持ST_Transformlibxml22.5用于ST_GeomFromGML/ST_GeomFromKML等 XML 解析除了这四个还需要 PostgreSQL 的开发头文件也就是postgresql-server-dev或对应版本的libpq-dev。如果你的 PostgreSQL 是从源码编译安装的那么 PostgreSQL 的源码目录里已经包含了所需头文件但需要保证pg_config能正确指向目标版本的安装路径。这里有一个常见误区系统里可能同时存在多个 PostgreSQL 版本例如/usr/lib/postgresql/15/bin/pg_config和/usr/local/pgsql/bin/pg_config。PostGIS 的 configure 脚本通过pg_config获取 PostgreSQL 的 include 目录和 library 目录如果 PATH 里先找到的是 PostgreSQL 15而你想给 PostgreSQL 16 编译扩展结果就是 make 时头文件错乱或者安装到了错误目录。所以第一步必须确认pg_config指向谁。在 Ubuntu 22.04 这类环境里我通常会先用发行版的包管理安装依赖避免自己编译 GEOS/GDAL 时引入更多变数# Ubuntu / Debian 系注意 postgresql-server-dev-16 里的版本号要与目标 PostgreSQL 匹配 sudo apt update sudo apt install -y build-essential postgresql-server-dev-16 libgeos-dev libgdal-dev libproj-dev libxml2-dev libjson-c-dev # CentOS / RHEL 系包名随发行版略有不同 sudo yum install -y gcc make postgresql16-devel geos-devel gdal-devel proj-devel libxml2-devel json-c-devel这里postgresql-server-dev-16会同时安装pg_config和服务器头文件。libjson-c-dev是为了让ST_GeomFromGeoJSON能工作虽然它不是 PostGIS 官方文档中最显眼的依赖但缺失时 configure 会提示找不到 json-c导致 GeoJSON 相关函数不可用。2.2 用 configure 检查环境的最小命令解压之后先别急着 configure先跑一段环境检查。我一般用下面的命令组合tar -zxvf postgis-3.3.6.tar.gz cd postgis-3.3.6 # 确认 pg_config 位置和版本 which pg_config pg_config --version pg_config --includedir-server # 快速检查关键依赖是否存在 pkg-config --modversion geos pkg-config --modversion gdal pkg-config --modversion projpg_config --includedir-server输出的是 PostgreSQL 服务端头文件目录PostGIS 编译时必须能找到postgres.h和fmgr.h。如果该目录不存在说明你没装服务器开发包后边的 configure 会直接报错。pkg-config命令可以一次看三个库的版本注意 Proj 在较新版本中库名可能是proj老一点的是libproj如果系统里是手动编译安装的pkg-config可能找不到这时需要设置PKG_CONFIG_PATH指向.pc文件所在目录。我习惯把这几条检查写进脚本里做成一个可复用的环境探测片段。对于大型集群可以在每台机器上先跑一遍输出保存为日志避免编译到一半才发现某台机器缺库。2.3 常见依赖缺失的报错与处理编译前的坑集中在三个报错上。第一个是checking for geos_c.h... no对应 GEOS 开发包未安装。在 Ubuntu/Debian 上执行apt-get install libgeos-dev在 CentOS/RHEL 上执行yum install geos-devel。第二个是could not find PostgreSQL server header files这个报错基本是pg_config路径不对。解决方式是在执行 configure 前显式指定export PATH/usr/pgsql-16/bin:$PATH或者用 configure 的参数--with-pgconfig/usr/pgsql-16/bin/pg_config。第三个是ERROR: could not find cunit或者checking for CUnit... no。这不一定致命因为 CUnit 只是用来跑单元测试的如果你不需要make check可以在 configure 时加上--without-cunit跳过检查。依赖问题最好在 configure 阶段全部暴露不要等到 make 到一半才看到fatal error: geos_c.h: No such file or directory。所以建议每次 configure 前都清空缓存rm -f config.log config.status再用make distclean清理上一次的编译痕迹。3. 编译安装 postgis-3.3.6 的完整步骤与关键参数3.1 解压、configure、make、make install 的标准流程这是最核心的一步。前提是你已经把前一章的依赖都准备好了。在源码目录下执行# 假设你已经在 postgis-3.3.6 目录下 # 1. 配置安装前缀和必要参数 ./configure --prefix/usr/local/pgsql --with-pgconfig/usr/local/pgsql/bin/pg_config --with-geosconfig/usr/local/bin/geos-config # 2. 编译这里使用 4 核并行可按机器配置调整 make -j4 # 3. 安装到系统目录 sudo make install这里解释三个参数。--prefix是安装根目录它决定 PostGIS 的 SQL 脚本和共享库最终放在哪里。如果不指定默认会跟随pg_config --configure里记录的 PostgreSQL 安装前缀通常没问题但如果 PostgreSQL 是由发行版包管理器安装的它的前缀可能是/usr而 PostGIS 的共享库会装在/usr/lib/postgresql/16/lib这样也符合管理员的预期。--with-pgconfig用于精确指定 PostgreSQL 的配置工具路径这在多版本共存的环境里是必须的。--with-geosconfig指向geos-config路径防止系统 PATH 里多个 GEOS 时挑错。3.1.1 configure 的可选参数除了上面三个还有几个参数值得了解参数作用--with-gdalconfig指定 GDAL 的gdal-config用于定位栅格支持--without-raster禁用栅格功能纯矢量场景可以减小体积--without-topology禁用拓扑扩展--without-address-standardizer忽略地址标准化器依赖外部库--with-jsondir指定 JSON-C 头文件位置ST_GeomFromGeoJSON需要它如果你只需要矢量 GIS比如国内常用的geometry类型和ST_AsGeoJSON那么--without-raster可以让你少编一个 GDAL 相关的模块但注意这样会让PostGIS_Raster相关的函数不可用。取舍要看业务是否需要栅格分析不要盲目关闭。同理--without-topology会去掉拓扑扩展如果不需要CreateTopology这类功能可以节省一笔编译时间。对于大多数互联网业务默认全开反而更省心因为后续若要开启新功能不需要重新编译。3.2 扩展安装CREATE EXTENSION 与升级路径make install只是把文件拷贝到了 PostgreSQL 的扩展目录还没有把函数注册到数据库里。PostGIS 的安装方式是通过 SQL 扩展机制。在任意一个数据库里执行-- 进入目标数据库 CREATE EXTENSION IF NOT EXISTS postgis; -- 如果需要拓扑功能 CREATE EXTENSION IF NOT EXISTS postgis_topology; -- 如果需要栅格 CREATE EXTENSION IF NOT EXISTS postgis_raster;注意postgis扩展是全功能的包括几何处理、空间索引、坐标转换等核心能力。postgis_topology和postgis_raster是额外组件如果之前 configure 时用了--without-raster那么postgis_raster创建时会报extension postgis_raster has no installation script。如果你是从旧版本升级比如从 PostGIS 3.2 升到 3.3.6需要执行ALTER EXTENSION postgis UPDATE TO 3.3.6;这会自动运行postgis--3.2--3.3.6.sql里面的改动脚本。升级前记得备份因为某些函数签名可能发生变化比如ST_TileEnvelope的默认边界在 3.3 中调整过升级后可能与旧业务对不上。另外升级前最好用pg_dump只导出扩展定义和依赖扩展的表结构测试恢复到一个临时库再执行ALTER EXTENSION。如果临时库升级后查询结果与旧版一致再动生产库。3.3 验证安装PostGIS 版本函数与空间数据库创建扩展创建完成后第一时间验证版本和功能-- 查看 PostGIS 版本包括编译期库版本 SELECT postgis_full_version(); -- 查看当前数据库里挂载的扩展信息 SELECT extname, extversion FROM pg_extension WHERE extname LIKE postgis%; -- 创建一个空间表试水 CREATE TABLE sample_geo (id serial PRIMARY KEY, geom geometry(Point, 4326)); INSERT INTO sample_geo (geom) VALUES (ST_SetSRID(ST_MakePoint(116.391, 39.907), 4326)); SELECT ST_Distance(geom, ST_SetSRID(ST_MakePoint(121.473, 31.230), 4326))/1000 AS dist_km FROM sample_geo;postgis_full_version()输出格式类似POSTGIS3.3.6 [EXTENSION] ... GEOS3.11.2-CAPI-1.15.2 PROJ9.1.1 LIBXML2.9.13。这里可以看出编译时链接的库版本。如果 GEOS 版本低于 3.6说明依赖没配对即使编译成功运行时也可能在处理某些几何运算符时崩溃。最后一行的ST_Distance算的是北京到上海的球面距离这个查询能正确返回大约 1067 公里就证明你的 PostGIS 3.3.6 基本可用。如果你的 PostgreSQL 是通过源码方式安装的可能还需要执行ldconfig让动态链接器找到新装的 PostGIS 共享库。否则在CREATE EXTENSION时会报could not load library /usr/local/pgsql/lib/postgis-3.so。此时运行sudo ldconfig /usr/local/pgsql/lib可以解决。4. 实战配置PostGIS 3.3.6 的常用参数与性能优化4.1 影响空间查询的 GUC 参数PostGIS 在运行时有一批自定义的 GUC 参数它们控制着函数行为。以下几个是生产环境最常调的-- 允许 GDAL 读取 GeoTIFF 和 PNG 栅格驱动 SET postgis.gdal_enabled_drivers GTiff PNG; -- 关闭 Proj 网络下载避免外部请求 SET postgis.proj_enable_network off; -- 最大并行 worker 数 SET postgis.max_parallel_workers_per_query 2;第一个参数gdal_enabled_drivers是白名单机制默认只有DISABLE_ALL也就是说默认无法用ST_GDALRaster读取外部栅格文件。你需要显式列出允许的驱动名多个驱动用空格分隔。这里设置GTiff PNG是允许读取 GeoTIFF 和 PNG。注意一旦设为ALL虽然方便但如果数据库在公网可能有安全风险因为栅格驱动会触发外部文件读取。proj_enable_network是 Proj 9 的新特性可以联网下载某些偏导数网格但生产环境最好关闭避免网络抖动拖慢查询。ST_Transform在涉及某些全球范围的坐标系时若本地缺少对应网格加上这个参数后会自动尝试从网络获取有时会让首次查询变慢数秒。所以除非必要否则保持默认的 off。max_parallel_workers_per_query不是只给 PostGIS 用的它控制一条查询可以调用的并行 worker 数量。PostGIS 3.3 中ST_Intersects、ST_DWithin等函数支持并行但并行成本预估依赖于统计信息如果表刚导入数据没有 analyze并行计划可能不会生成。还有一个参数容易被忽略postgis.backend。在 PostGIS 3.3 中几何计算后端有两个geos和sfcgal。默认是geosSFCGAL 提供更多三维函数比如ST_Volume、ST_ConvexHull的特定算法。如果你的源码编译时配置了--with-sfcgal可以这样切换SET postgis.backend sfcgal;注意 SFCGAL 后端不是所有函数都支持切换后调用那些未实现函数时会直接报错所以建议只在专门的三维查询会话里使用。4.2 空间索引与 analyze 的配合空间索引是 PostGIS 查询的加速器。建索引的 SQL 是CREATE INDEX idx_sample_geo_geom ON sample_geo USING GIST (geom);GIST 索引基于 R-Tree适合范围查询和距离查询。但索引不会自动维护统计信息。要发挥、ST_DWithin的索引加速效果必须收集统计信息ANALYZE sample_geo;对于特别大的表PostGIS 还能生成空间统计直方图帮助规划器估算 selectivity。这是通过函数postgis_stats完成的SELECT postgis_stats(sample_geo, geom, 4);这里的 4 是直方图桶数。如果查询计划仍不走索引可以打开EXPLAIN ANALYZE观察EXPLAIN (ANALYZE, BUFFERS) SELECT id FROM sample_geo WHERE geom ST_MakeEnvelope(115.0, 39.0, 117.0, 41.0, 4326);如果看到Seq Scan检查索引是否命中表达式。PostGIS 是一个表达式敏感的扩展geom SRID4326;POLYGON(...)这种字符串常量形式可以走索引但是ST_MakeEnvelope如果前面没有把常量包装成 immutable 函数也可能计划器认为成本高而不走。常见做法是显式写出ST_SetSRID(ST_MakeEnvelope(...),4326)。实践中我见过不少案例是因为没有ANALYZE导致 GIST 索引选择性估算为 0规划器放弃索引。所以建完索引后第一件事就是ANALYZE。4.3 与 PostgreSQL 16 的兼容性排查PostGIS 3.3.6 发布时间早于 PostgreSQL 16 GA。如果你是在 PostgreSQL 16 上编译可能会遇到两个问题。第一个是CREATE EXTENSION postgis时报could not load library ... version mismatch这说明 PostGIS 的 C 共享库是针对 PostgreSQL 15 编译的而当前服务器是 16。解决方法是重新编译 PostGIS确保pg_config指向 16 的 bin 目录。第二个问题是make check中内存泄漏测试无法通过这通常是 CUnit 版本太老导致不影响生产。但这种检查必须在项目交付时记录到 FAQ 中避免后续维护者误以为是代码缺陷。有一个更隐蔽的坑PostgreSQL 16 调整了numeric函数的内部行为而 PostGIS 的ST_Area在求大范围多边形时可能用到numeric导致结果精度与旧版本有微小偏差。这不算 bug但如果你做面积对比测试应该以postgis_full_version()输出的 GEOS 版本为准去参考差异。比如在从一个库迁移到另一个库时如果两端的 PostGIS patch 版本不同面积计算结果可能在小数点后 9 位有差异这是正常的并不表示迁移出错。5. 进阶用并行编译和快速验证提升安装效率5.1 并行 make 与 make check 的取舍在编译时make -j4可以大幅缩短时间但如果不注意依赖的先后可能因为头文件尚未生成导致编译失败。PostGIS 的 Makefile 对并行支持得比较好但建议在一次make distclean之后先执行make -j1来验证基础编译没有隐藏的依赖错误再做并行编译。如果机器是 8 核可以用make -j8编译速度比-j4快不了太多因为瓶颈通常在 GEOS/GDAL 的链接阶段。make check是运行回归测试它需要 CUnit 库和一个可用的 PostgreSQL 实例。如果你想快速出结果可以跳过make check但跳过之后某些函数在运行时可能会暴露出边界条件问题。我一般会在编译前用make check跑一遍大约耗时十几分钟主要看regress输出中的FAILED行。如果出现FAILED: regress_index之类的条目先不要急着make install回头检查是不是 PostgreSQL 版本太新导致测试脚本兼容性问题。make check的失败不一定代表扩展不可用但至少能帮你提前暴露一些隐患。5.2 定制安装路径与 pg_config 的坑如果你不想污染系统目录可以指定--prefix到自己的软件目录比如./configure --prefix/opt/postgis-3.3.6 --with-pgconfig/opt/pgsql16/bin/pg_config make -j8 sudo make install但注意PostGIS 在make install时会把共享库.so文件和 SQL 脚本拷贝到$(pg_config --pkglibdir)和$(pg_config --sharedir)/extension这两个目录可能不受--prefix控制。这会让一个非标准的安装变得混乱一部分文件在/opt/postgis-3.3.6一部分跑到了 PostgreSQL 的标准扩展目录。所以最稳妥的方式是让--prefix与pg_config所属 PostgreSQL 的--prefix一致或者干脆用动态装载路径在 PostgreSQL 的shared_preload_libraries前设置LD_LIBRARY_PATH。另一个坑是pg_config指向的 PostgreSQL 版本与实际运行的 postmaster 不一致。比如你编译时用的 PostgreSQL 16 头文件但服务器上跑的是 PostgreSQL 15加载时必然报错。检查方法是# 对比两个版本 $(pg_config --bindir)/postgres --version psql -c SELECT version();如果二者不一致说明 PATH 顺序有误需要调整。这种问题在容器化部署里尤其常见构建镜像时系统路径里有多个 PostgreSQL一定要显式传--with-pgconfig。5.3 快速验证扩展版本的技巧安装完成后除了postgis_full_version()还有几个轻量级验证技巧# 从 shell 直接查看扩展文件 ls -l $(pg_config --pkglibdir)/postgis-3.so cat $(pg_config --sharedir)/extension/postgis.controlpostgis.control文件里包含default_version 3.3.6这是扩展的默认版本号。如果你看到它但数据库里CREATE EXTENSION postgis还是安装不到 3.3.6那很可能是 PostgreSQL 的扩展目录被操作系统包管理器覆盖需要用--with-extensiondir参数重新指定目录。最后说一个日常排查问题的小技巧当你执行CREATE EXTENSION postgis失败时可以在 psql 里打开 debug 输出SET client_min_messages debug; CREATE EXTENSION postgis;这样能打印出扩展脚本的执行过程定位到具体哪一行 SQL 失败。这些日志对判断是权限问题、依赖缺失还是初始化脚本冲突很有帮助。配合postgis_full_version()的输出基本能在五分钟内定位 90% 的安装故障。我把这两条命令固化到安装脚本末尾每次部署完直接跑一遍确认无误后再交给业务方联调。本文还有配套的精品资源点击获取