ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

PostGIS 3.3.6 源码编译安装全指南:从依赖配置到数据库扩展与排错

PostGIS 3.3.6 源码编译安装全指南:从依赖配置到数据库扩展与排错

简介:这是PostGIS 3.3.6的源码安装包,面向需要在PostgreSQL中处理地理空间数据的开发者和GIS从业者,用于解决空间对象存储、空间查询、坐标转换与地理分析等场景问题。整个资源以gz压缩包形式发布,大小约16.98MB,包含2000个文件;其中457个SQL脚本与PostGIS扩展初始化相关,269个C文件和127个头文件属于核心代码,164个WKT文件提供空间对象文本样例,另有po多语言翻译、expected回归测试结果、shp/dbf等矢量数据文件,便于对照源码理解实现细节。目前已有69人学习或下载该压缩包。通过这份源码,可以完整查看PostGIS对OpenGIS规范的实现方式,研究空间数据类型、空间索引、空间函数、空间关系判断、坐标参考系统转换等底层逻辑;同时它也能作为离线编译安装的基础,适合需要学习扩展开发或搭建本地GIS数据库环境的技术人员。

1. postgis-3.3.6.tar.gz 是什么:散装源码包与编译安装的真实需求

拿到postgis-3.3.6.tar.gz这个源码包的时候,多数人已经在发行版仓库里碰过壁了:仓库自带的 PostGIS 版本太旧、和当前 PostgreSQL 不匹配、或者干脆缺了某个扩展模块。这个 tar.gz 不是一键安装包,而是 PostGIS 3.3.6 的完整源代码,需要自己 configure 和 make。它能解决的核心问题是:在不受发行版约束的前提下,把 PostGIS 装到对应版本的 PostgreSQL 上,并且拿到想要的 GIS 功能集。适合三类人:正在做空间数据平台选型的后端工程师,需要特定 PG 版本匹配的 DBA,以及在离线环境或内网部署空间数据库的运维。如果你只是想点两下鼠标装完就用,这个包并不是最省事的选项,但它是唯一能精确控制版本和编译参数的路径。

2. 编译前的依赖准备:PostGIS 3.3.6 需要哪些底库与版本匹配

2.1 依赖清单:GEOS、Proj、GDAL 缺一不可

PostGIS 不是独立运行的程序,它是嵌入 PostgreSQL 的一组扩展库。编译它之前,必须先准备好底层空间运算库。常见做法是装齐这四个:GEOS(几何运算核心)、Proj(坐标投影)、GDAL(栅格和多种格式读写)、LibXML2(GML/KML 解析)。其中 GEOS 管的是ST_Intersects、ST_Buffer这类空间分析算子,Proj 管的是坐标系转换,GDAL 管的是栅格和矢量文件导入。缺了任何一个,configure 都会提前中断。

我一般会在编译前用系统包管理器装这些依赖。Debian/Ubuntu 系的命令是:

sudo apt-get install -y \ libgeos-dev \ libproj-dev \ libgdal-dev \ libxml2-dev \ postgresql-server-dev-14

这段命令的关键是最后的postgresql-server-dev-14,它的版本号必须和你的 PostgreSQL 主版本一致。如果你用的是 PostgreSQL 15,就把 14 改成 15。这个包提供pg_config工具,PostGIS 编译时必须靠它确定头文件路径。缺少它,configure 会直接报PostgreSQL backend headers are not found。装完依赖后,可以用pkg-config验证:

pkg-config --modversion geos pkg-config --modversion proj pkg-config --modversion gdal

如果有版本号输出,说明底库已经就绪。如果没有,回到上一步检查包名是否装对。

2.2 PostgreSQL 版本匹配:3.3.6 能配合的 PG 版本

PostGIS 3.3.6 不是所有 PostgreSQL 版本都支持。它官方适配的版本区间和当时发布的 PostgreSQL 版本有关,但实践里更常见的问题是:你手里的 PG 版本太新或太旧,导致扩展控制文件的requires条件不满足。比如 PostgreSQL 16 刚发布时,早期 PostGIS 3.3 版本还没有适配它,编译能过,但CREATE EXTENSION postgis会报语义错误。这种情况只有升级 PostGIS 到 3.4 以上,或者换用兼容的 PG 版本。

建议先确认你的 PG 主版本,然后对照 PostGIS 的 release notes。常见部署里 PostgreSQL 13、14、15 用 3.3.6 都是稳妥的。如果拿不准,直接在 PostgreSQL 中执行:

SHOW server_version;

然后用查询到的版本号去查找对应分支。不要盲目猜,这是 postgis 安装失败 里最容易忽略的一类原因——不是编译器问题,而是 PG 和 PostGIS 的版本契约没谈拢。

2.3 检查环境:用 pg_config 验证编译路径

在解压源码之前,先确认pg_config指向的 PostgreSQL 实例是不是你要装的那个。很多机器上装了多个 PG 版本,PATH里先遇到的pg_config可能不是目标版本。执行:

which pg_config pg_config --version pg_config --includedir-server

includedir-server是 PostgreSQL 服务端头文件所在目录,如果这个目录不存在,说明你没装postgresql-server-dev包。另外要注意,configure 时--with-pgconfig参数必须指向目标 PG 的pg_config,而不是随便一个。我遇到过一台机器同时有 PostgreSQL 12 和 14,默认pg_config指向 12,结果 PostGIS 编译完装到了 12 的扩展目录,后续在 14 的数据库里执行CREATE EXTENSION一直找不到文件。先检查这个,能省下后面一整轮排查。

3. 解压 configure 与编译:从 tar.gz 到可安装文件的关键步骤

3.1 解压与进入源码目录

拿到postgis-3.3.6.tar.gz后,先做完整性校验,再解压。官方习惯是附一个SHA256SUMS文件,你可以这样核对:

sha256sum postgis-3.3.6.tar.gz

如果校验值和你下载源给出的不一致,说明文件损坏或被篡改,解压编译大概率会翻车。确认没问题后:

tar -xzf postgis-3.3.6.tar.gz cd postgis-3.3.6

解压后会看到configure、Makefile.in、postgis等目录。postgis目录是核心代码,extensions里是postgis、postgis_topology等扩展的 SQL 定义。这里要养成一个习惯:编译之前先扫一眼README里的系统要求,尤其是版本号。PostGIS 每次发布都会更新依赖的最低版本,3.3.6 对 GEOS、Proj、GDAL 都有明确要求。

3.2 configure 参数:--with-pgconfig 等必调项

进入源码目录后,最关键的步骤是配置编译参数。PostGIS 的configure不是无脑./configure --prefix=/usr/local就完事,它需要知道 PostgreSQL 的位置、GEOS/Proj/GDAL 的位置。常见做法是显式指定:

./configure \ --with-pgconfig=/usr/lib/postgresql/14/bin/pg_config \ --with-geosconfig=/usr/bin/geos-config \ --with-gdalconfig=/usr/bin/gdal-config \ --with-projdir=/usr/share/proj \ --prefix=/usr/local

--with-pgconfig指向 PostgreSQL 的pg_config可执行文件,这个路径决定 PostGIS 把扩展安装到哪个 PG 实例。--with-geosconfig和--with-gdalconfig分别指定 GEOS 和 GDAL 的配置文件路径。--with-projdir指定 Proj 数据目录,通常和proj安装位置一致。如果这些路径漏配,configure 会尝试在默认路径里找,找不到就报错。参数结束后,仔细看屏幕输出的摘要,比如PostgreSQL version: 14、GEOS version: 3.11,确认每一项都正确。

如果只想使用默认的 PostgreSQL 安装,可以只写./configure --prefix=/usr/local,但那样在一个多版本 PostgreSQL 共存的环境里非常危险。我一般会每次都写清楚--with-pgconfig,避免装错实例。

3.3 make 与 make install:并行编译与权限处理

配置成功后,进入编译环节。这里可以用多核并行加速:

make -j4

-j4表示用 4 个并行任务,如果你的服务器 CPU 核数多,可以适当加大。但要注意,如果编译过程中报错,不要只看最后几行。PostGIS 是由 C 和 C++ 混合编写的,错误提示往往不在底部,而是混在大量输出里。建议先重新单独执行一次make(不带-j),拿到干净的错误信息再定位。编译时间取决于机器,通常在 5 到 20 分钟之间。

编译没有报错后,用make install安装:

sudo make install

这里为什么要sudo?因为扩展文件要写入 PostgreSQL 的安装目录,比如/usr/lib/postgresql/14/lib和/usr/share/postgresql/14/extension。普通用户没有写权限。如果之前指定过--prefix,安装路径会在make install时打印出来,比如/usr/lib/postgresql/14/lib/postgis-3.3.so。看到这个文件生成,说明核心库安装成功。

4. 数据库端启用扩展:CREATE EXTENSION 的流程与 PostgreSQL 侧配置

4.1 创建扩展前置动作:PostGIS 在 PG 中的安装路径

编译安装完成后,需要到 PostgreSQL 里建立扩展。但这一步之前要确认好数据库。PostGIS 是数据库级别的扩展,不是集群级别的——你需要对每个要用空间功能的库单独执行CREATE EXTENSION。常见做法是先在 PostgreSQL 中切换到目标数据库:

sudo -u postgres psql -d mygisdb

mygisdb是你准备存储空间数据的库。如果没有,先创建:

CREATE DATABASE mygisdb;

连接到数据库后,查看 PostGIS 扩展是否已经出现在可用列表里:

SELECT * FROM pg_available_extensions WHERE name LIKE 'postgis%';

这条 SQL 会列出 PostgreSQL 已经扫描到的 PostGIS 扩展及其版本号。如果这里能看到3.3.6,说明扩展文件安装路径正确;如果空表,说明make install的路径和当前数据库的pg_config路径不对应,回到第 3 章的路径检查。

4.2 CREATE EXTENSION postgis 的权限要求

执行创建扩展的命令很简单:

CREATE EXTENSION postgis;

但这行命令有隐性要求:执行者必须是超级用户或者被授予了CREATEROLE和CREATE权限。PostGIS 安装时会向pg_extension表写入控制信息,并创建一组新的 SQL 函数、数据类型和类型转换。如果没有超级用户权限,通常会报permission denied to create extension。有时候 DBA 只给了ALL PRIVILEGES也没用,因为CREATE EXTENSION要求数据库所有者权限,这个很玄学。

我的建议是,在初始部署时直接用postgres超级用户执行创建扩展,完成后再把权限收窄给业务用户。不要在业务库第一次建扩展时就尝试用普通角色,否则会被权限问题缠住。创建完可以验证:

SELECT postgis_version();

输出应该包含3.3.6。如果这个函数报错,说明扩展没有完整安装,检查第 3 章的编译参数。

4.3 常用 GUC 参数与首次空间查询

PostGIS 3.3.6 安装后,不需要像别的插件那样重启 PostgreSQL,但需要确保search_path包含public或postgis架构,否则ST_*函数可能找不到。常见做法是在连接时设置:

SET search_path = public, postgis, topology;

如果你把扩展安装到了单独的 schema(比如CREATE EXTENSION postgis SCHEMA postgis;),这里就要把postgis放在search_path里。我习惯用第二个参数指定 schema,这样业务表和空间函数分开,权限管理更清晰。

第一个空间查询可以这样跑:

SELECT ST_GeomFromText('POINT(116.4 39.9)', 4326);

如果返回类型是geometry,说明空间类型和函数已经正常工作。接着验证空间索引:

CREATE TABLE buildings ( id serial PRIMARY KEY, geom geometry(Point, 4326) ); CREATE INDEX buildings_geom_idx ON buildings USING GIST (geom);

GIST 索引是 PostGIS 空间查询加速的根基,建立之后ST_DWithin、ST_Intersects这类查询才有性能保证。没有这个索引,小数据量看不出问题,大数据量直接把人卡到崩溃。

5. 避坑:PostGIS 3.3.6 安装失败的典型场景与排查路径

5.1 现象:configure 报错找不到 GEOS

现象:执行./configure时,输出里出现configure: error: could not find geos-config,或者提示cannot find libgeos_c.h。

原因:GEOS 的开发包没安装,或者装了但geos-config不在PATH里。PostGIS 不是直接找 GEOS 的库文件,而是通过geos-config这个工具获取编译参数。发行版通常会把它放在/usr/bin/geos-config。如果只是装了libgeos-dev但PATH不对,configure 还是会找不到。

解决:先确认包是否安装,which geos-config或dpkg -L libgeos-dev | grep geos-config。如果没装,sudo apt-get install -y libgeos-dev。如果装了但路径不在默认位置,在 configure 命令里显式加上--with-geosconfig=/usr/local/bin/geos-config。不要因为报错提示去网上乱找补丁,绝大多数情况就是路径问题。

5.2 现象:make 过程中出现语法错误

现象:make跑到一半,输出大量error: xxx was not declared in this scope,甚至直接卡在某个头文件上。

原因:PostGIS 3.3.6 对 C++ 标准有要求,如果默认编译器太旧(比如 GCC 4.8),部分模板和多态代码会编译不过。另外,-j4并行编译时内存不足也会导致中间文件错乱,报出一些莫名其妙的语法错误。

解决:先检查编译器版本,gcc --version和g++ --version。如果版本低于 7,升级 GCC。如果编译器版本正常,试试不带-j重新编一次,看是否还有同样的错误。我遇到过一次服务器内存只有 1GB,make -j8直接把内存撑爆,重复编译时产生了残缺的.o文件。这种情况先make clean,再用make -j2编译,就能过去。这属于血泪经验,别高估服务器的承受能力。

5.3 现象:CREATE EXTENSION 提示文件不存在

现象:执行CREATE EXTENSION postgis;时,PostgreSQL 报错extension "postgis" is not available,但pg_available_extensions里能看到名称。

原因:扩展控制文件(.control)存在,但对应的 SQL 文件或动态库文件没有安装。PostgreSQL 在CREATE EXTENSION时会读取/usr/share/postgresql/<版本>/extension/postgis.control,然后寻找同目录下postgis--3.3.6.sql和postgis.so。如果make install时pg_config指向了另一个 PostgreSQL 版本,这些文件就会落到错误目录。

解决:检查你自己数据库的配置路径:

psql -c 'SHOW data_directory;'

然后看扩展目录是否正确。最常见做法是用--with-pgconfig强制指定版本后重新编译安装。不要手动画葫芦去复制文件,那样版本容易错乱,以后升级会踩更多坑。

5.4 现象:升级时旧版本残留导致冲突

现象:之前装过 PostGIS 2.5 或 3.0,后来在同一个库里执行CREATE EXTENSION postgis报错extension "postgis" already exists,或者升级报错type "geometry" already exists。

原因:旧版本已经在数据库里初始化了数据类型和函数,新版本的 SQL 脚本会尝试再次创建同名对象,数据库不允许重复定义。

解决:生产环境不能随意DROP EXTENSION,因为会删除所有空间数据。正确路径是用ALTER EXTENSION postgis UPDATE TO '3.3.6';。但如果直接更新失败,说明旧版本缺少升级脚本。PostGIS 官方提供的升级路径是从旧版本一层一层升上去,比如2.5到3.0到3.3。如果你是从 2.5 直接版跳到 3.3,需要先安装中间版本,再继续升。这个步骤不能跳,跳了就会因函数签名不匹配而翻车。

5.5 现象:空间索引没生效,查询奇慢无比

现象:POSTGIS 安装成功,数据也能查,但ST_DWithin执行时间很长,EXPLAIN显示Seq Scan而不是Index Scan。

原因:PostGIS 的 GIST 索引是建立在几何列上的,如果表的geom字段没有建立索引,或者查询条件里对geom做了函数封装,索引就失效。最常见的模糊操作是WHERE ST_Intersects(ST_Transform(geom, 4326), target_geom),对列做函数计算,GIST 无法利用。

解决:对几何列建立 GIST 索引,并尽量在查询时保持列裸露。如果你经常做ST_Transform,考虑直接使用统一的坐标系存储,或者创建表达式索引:

CREATE INDEX buildings_geom_transformed_idx ON buildings USING GIST (ST_Transform(geom, 4326));

但这样索引体积会膨胀,写入变慢。我一般优先保证源数据坐标系统一,把转换放在应用层,不让索引背锅。

6. 验证与进阶:用 PostGIS 3.3.6 跑通真实空间查询并做一次调优

安装完成只是起点,我离开前会做一轮完整验证,确保不是"能建扩展但用不起来"。下面这段 SQL 模拟了一个 5 公里范围内的兴趣点查询:

EXPLAIN ANALYZE SELECT b.id FROM buildings b WHERE ST_DWithin( b.geom, ST_SetSRID(ST_MakePoint(116.4, 39.9), 4326), 0.05 );

执行计划里如果出现Bitmap Heap Scan和Index Scan using buildings_geom_idx,说明 GIST 正确参与。0.05的单位是度,约等于 5 公里,这个数值在高纬度地区会有偏差,但用于索引验证足够。如果出现Seq Scan,回第 5.5 节查索引。验证通过后,我会用一个进阶技巧:调整work_mem来降低 GIST 索引扫描的 I/O。对于大范围空间查询,PostGIS 会批量读取索引页,默认的work_mem太低会导致额外排序。常见做法是在会话或事务级调整:

SET LOCAL work_mem = '256MB';

在生产环境直接改全局work_mem风险很大,所以只在慢查询会话里临时调整。这个参数不是玄学,空间索引的访问模式天然比其他 B-tree 更吃内存,给足它,效果立竿见影。

我的习惯是每次编译 PostGIS 后都保留当时用的 configure 参数到一份postgis_build.sh脚本,放在/usr/local/src里。下次遇到问题,直接跑一遍脚本重新编译,省去回忆参数的时间。有时候版本依赖会真的把人逼疯,但参数写下来,至少能排除一半的变量。postgis-3.3.6.tar.gz这个包本身不复杂,复杂的是它嵌在 PostgreSQL 生态里的那些隐性约束。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

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