☰
PostGIS 3.5.0 安装部署与空间查询实战指南
2026/10/9 11:55:00 网站建设 项目流程

简介:PostGIS 3.5.0 安装包(postgis-bundle-pg17-3.5.0x64.zip)为 64 位 Windows 系统上的 PostgreSQL 17 数据库提供完整的空间数据扩展能力,面向 GIS 开发者、数据库管理员及空间数据分析人员,解决在关系型存储中管理点、线、面等空间对象的难题。压缩包内共 1317 个文件,以结构化查询脚本、动态链接库、文本数据文件、栅格影像和地理空间参考文件为主,还包含扩展控制文件与配置文档,可用于部署空间索引、地理编码、地址标准化、点云存储和路径规划分析。资源包整体大小约 130.69MB,已有 193 人浏览学习,目录组织规范,便于按需检索。获取后可在 PostgreSQL 17 中直接启用扩展,通过标准 SQL 完成距离面积计算、空间连接、聚合统计和网络分析,并配合地理编码与路由模块开展真实 GIS 项目,大幅降低环境搭建与二次开发成本。

1. 为什么是 PostGIS:从解压一个 3.5.0 bundle 开始

PostGIS 是 PostgreSQL 生态里最被低估的空间组件。很多人做 GIS 时先在桌面软件里画图、导 shp,等数据量涨到百万级、需要多人并发查空间数据时,才发现数据库层的空间能力才是命门。这份 postgis-bundle-pg17-3.5.0x64.zip 是给 PostgreSQL 17 准备的 64 位空间扩展包,里面不只有 PostGIS 3.5.0 核心,还带了点云扩展、地址标准化、geocoder 和 pgRouting 路径规划。它解决的核心问题很具体:让数据库直接存点、线、面,用标准 SQL 算距离、面积、缓冲区,再配合空间索引扛住高并发查询。适合 GIS 开发、测绘数据处理、地图服务后端,也适合想给业务库补空间能力的后端工程师。

2. bundle 包解析:文件清单背后的组件体系

拿到 zip 先别急着解压安装。Windows 上的 PostgreSQL 扩展包不像普通安装程序那样一键装完,它的文件组织方式直接决定你的安装路径和排错方向。我第一次处理这类 bundle 时走了不少弯路,以为把文件解压覆盖到 PostgreSQL 目录就完事,结果 CREATE EXTENSION 一直报找不到控制文件。后来把包内文件与 PG 目录逐个对照,才弄明白这套体系的运转逻辑。

2.1 文件名先读出关键信息:pg17 与 3.5.0 的配套关系

先看文件名postgis-bundle-pg17-3.5.0x64.zip,这一串不是随便起的,它交代了三件事。

开头的pg17表示这套扩展是编译给 PostgreSQL 17 大版本用的。PostGIS 的二进制扩展依赖 PostgreSQL 提供的头文件和 ABI,扩展组件在一个主版本内可以通用,但不能跨大版本安装。把 pg17 的包强行塞给 PostgreSQL 16 或 18,后患无穷,轻则加载失败,重则把服务搞崩。所以装之前先确认 PG 的主版本,这一步省不掉。

中间的3.5.0是 PostGIS 版本号。3.5.0 意味着它携带了一套特定的函数库和 SQL 脚本,不同小版本的函数集合会有差异,比如某些新增函数只在 3.4 以上存在。版本号同时也是排错时的参照物,后续postgis_full_version()输出的信息会与你下载的包版本互相印证。

结尾的x64是明牌:这是 64 位构建。Windows 上 PostgreSQL 有 32 位和 64 位两个体系,扩展库必须与 PostgreSQL 服务进程的位数完全一致,混用的结果一般是服务启动失败或函数无法加载。这类位数不匹配的问题在论坛里出现的频率极高,属于典型的低级翻车点,后面第 5 章我会专门列一条排查路径。

2.2 解压清单拆解:从批处理脚本到各扩展控制文件

解压这份 zip 之后,第一眼看到的文件有点零散,但把它们归类之后,结构其实非常清晰。我先把包内典型的文件按功能列出来:

文件名类型作用
makepostgisdb_using_extensions.bat批处理脚本一键创建扩展库的 Windows 脚本
loaders.cache配置文件GDAL/加载器组件的缓存文件
fonts.conf配置文件字体渲染相关的默认配置
im-multipress.conf配置文件栅格/multi 格式导入相关的默认配置
postgis.control扩展控制文件PostGIS 主扩展的注册描述文件
postgis_tiger_geocoder.control扩展控制文件TIGER geocoder 扩展的注册描述文件
address_standardizer.control扩展控制文件地址标准化扩展的注册描述文件
pointcloud.control扩展控制文件PointCloud 基础扩展的注册描述文件
pointcloud_postgis.control扩展控制文件PointCloud 与 PostGIS 集成扩展的注册描述文件
pgrouting.control扩展控制文件pgRouting 路径规划扩展的注册描述文件

这几个.control文件就是整个 bundle 的骨架。postgis.control对应核心空间扩展,提供 geometry/geography 类型、空间索引和大部分空间函数;pointcloud.control和pointcloud_postgis.control负责点云数据的存取和空间关系分析;address_standardizer.control做地址解析和标准化,postgis_tiger_geocoder.control是美国人口普查 TIGER 数据的地址匹配工具;pgrouting.control则是专门做路径规划和网络分析的扩展。国内 GIS 项目真正天天用的通常是postgis和pgrouting,其余的是按场景按需启用。

makepostgisdb_using_extensions.bat这个名字值得展开说。它对应的是一套"用扩展方式建空间库"的做法:新建一个数据库,然后逐个执行 CREATE EXTENSION 命令。这个 bat 脚本把多个扩展的启用动作封装到了一起,适合被反复初始化新库的场景。我一般不会直接双击它,而是打开看一遍里面的扩展列表,确认哪些扩展是当前项目需要的,然后手动拆分执行,这样更可控。

2.3 扩展加载机制:.control、SQL 脚本和共享库的分工

PostgreSQL 的扩展机制可以拆成三层:.control文件是扩展的元数据描述,它告诉数据库这个扩展叫什么、默认装到哪个 schema、是否需要超级用户权限、对应的 SQL 脚本文件名是什么。真正的函数和类型定义写在 SQL 脚本里,比如postgis--3.5.0.sql;而在 SQL 层注册的 C 函数实现,则编译成了 DLL 共享库,放在 PostgreSQL 的 lib 目录下。

理解这三层分工对排错极有帮助。报"control file not found"时,问题在.control文件没进入正确的share/extension目录。报"function does not exist"时,八成是 SQL 脚本没执行或 schema 路径有问题。报"could not load library xxx.dll"时,则是共享库缺失、位数不匹配或服务未重启。

所以安装 bundle 的本质就一句话:把.control和 SQL 脚本放到 PostgreSQL 的share/extension目录,把 DLL 放到lib目录,然后执行 CREATE EXTENSION。第 3 章我会给出具体的落位命令和验证流程。

3. 安装部署:从 zip 到能跑空间查询的数据库

解压 PostGIS bundle 到真正能用,中间隔着几个容易踩空的步骤。这一章按我习惯的顺序走:先查环境,再落文件,然后建库建扩展,最后用版本函数和一张最小空间表收尾验证。

3.1 前置检查:PostgreSQL 版本、架构与最小权限

先把 PostgreSQL 的本体信息确认清楚。没有装 PostgreSQL 17 的,先装好 17 的 x64 版本,再回来处理扩展。已装的,在bin目录下打开命令行跑一遍:

# 进入 PostgreSQL 的 bin 目录,然后执行 pg_config --version pg_config --bindir pg_config --pkglibdir pg_config --sharedir

pg_config --version输出的是 PostgreSQL 版本字符串,比如 PostgreSQL 17.x。--bindir是程序目录,--pkglibdir是共享库目录,--sharedir是文档和扩展脚本目录。这些路径后面落文件时都要用。注意,如果机器上装了多个 PostgreSQL 实例,务必确认当前 PATH 指向的是你准备用的那一个,这属于常见乌龙。

权限方面,创建扩展通常需要超级用户权限,或者至少具备CREATE权限并属于扩展的所有者。Windows 上还要确认 PostgreSQL 服务账户对扩展目录有读权限,否则服务启动时加载 DLL 会有隐蔽的权限问题。简单起见,我的习惯是:初始化测试环境时直接用超级用户操作,生产环境再单独建一个专用账号并只授予必要权限。

3.2 解压与文件落位:扩展目录和共享库目录

把 zip 解压到一个临时目录,比如D:\postgis_bundle。接下来要做的是把包内的.control、.sql文件合并到--sharedir对应的extension子目录,把 DLL 合并到--pkglibdir对应的目录。

Windows 下我一般用robocopy做目录合并,它比资源管理器可靠,也不会因为同名文件反复弹窗:

# 假设 PG 17 安装在 C:\pgsql17,解压包在 D:\postgis_bundle robocopy D:\postgis_bundle C:\pgsql17 /E /XO

/E表示复制所有子目录和文件,/XO表示只覆盖旧文件,避免覆盖掉 PG 自带的同名文件。如果你不想覆盖任何已有文件,可以先跑一遍robocopy ... /L列出将要复制的文件列表,再决定是否执行。

这一步做完,别急着建库。先去C:\pgsql17\share\extension扫一眼,确认postgis.control、pgrouting.control这些文件已经落位。Windows 的命令行可以这样快速核对:

dir C:\pgsql17\share\extension\*.control

看到.control文件清单与 2.2 节的表格对应上,再往下走。

3.3 创建空间数据库:psql 命令行与批处理脚本两条路线

PostGIS 的启用方式是建一个新库,然后执行 CREATE EXTENSION。包内的makepostgisdb_using_extensions.bat做的就是这件事,它会按脚本里写好的顺序创建数据库、执行扩展脚本。我建议先手动走一遍,理解每一步在干什么,再决定要不要用脚本自动化。

使用psql的完整流程如下:

-- 连接到默认的 postgres 库 CREATE DATABASE gisdb; \c gisdb -- 依次启用核心扩展 CREATE EXTENSION postgis; CREATE EXTENSION address_standardizer; CREATE EXTENSION postgis_tiger_geocoder; CREATE EXTENSION pointcloud; CREATE EXTENSION pointcloud_postgis; CREATE EXTENSION pgrouting;

逻辑说明:CREATE EXTENSION postgis把基础空间类型和函数装进数据库,这一条是必选项。pointcloud和pointcloud_postgis是点云支持,做激光雷达或倾斜摄影数据管理时才需要。pgrouting是独立的路径规划引擎,和 PostGIS 主扩展互不依赖,单独启用即可。postgis_tiger_geocoder依赖address_standardizer,所以先启用后者再做 geocoder,顺序反了会报依赖缺失。

参数说明:花括号里的扩展名必须与.control文件名严格一致,比如pointcloud_postgis就是一个整体名字,不能拆开。此外,CREATE EXTENSION支持SCHEMA参数,例如CREATE EXTENSION postgis SCHEMA public;,明确 schema 可以避免后续search_path混乱。对于postgis_tiger_geocoder,它内部会创建多个函数和表,建议单独用一个 schema 来装,避免与业务表混在一起。

批处理脚本方式也可以直接用,它做的事情与上面的 SQL 序列等价。区别在于脚本会读取预设的数据库名和连接参数,适合一键初始化测试库。生产环境我更推荐手动拆分执行:一是可以按需裁剪扩展,二是失败时能看到准确的错误上下文。

3.4 验证安装:版本函数与第一张空间表

扩展装完,用版本函数做一次体检:

SELECT postgis_version(); SELECT postgis_full_version(); SELECT pgr_version();

postgis_version()返回简短版本号,postgis_full_version()输出完整构建信息,包括 PROJ、GDAL、GEOS 等底层库的版本,这串信息在论坛报错或写精度相关代码时非常有用。pgr_version()只有启用 pgRouting 之后才存在,如果这条报函数不存在,说明扩展没有装成功。

版本没问题后,建一张最小空间表验证写路径和读路径:

CREATE TABLE poi ( id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, name text, geom geometry(Point, 4326) ); INSERT INTO poi (name, geom) VALUES ('某设施', ST_SetSRID(ST_Point(116.40, 39.90), 4326)); SELECT name, ST_AsText(geom) FROM poi;

这段验证 SQL 里,geometry(Point, 4326)声明该列只接受 4326 坐标系下的点类型。ST_SetSRID(ST_Point(...), 4326)先构造坐标点再指定 SRID,避免出现坐标系未知的几何对象。ST_AsText把内部二进制格式转成 WKT 文本,便于肉眼确认数据成功落库。到这里,PostGIS 基本就能跑起来了,下面的章节进入实际业务使用和排错环节。

4. 空间数据实操:建表、导入与三类典型查询

安装只是起点,真正的工作从设计空间表、导入坐标数据、写空间 SQL 开始。这一章用一套模拟的 POI 和地块数据,演示最常见的建表、转换和查询操作。所有坐标都是虚构示例,重点在看函数怎么用、参数怎么设、结果怎么查。

4.1 创建空间表:geometry 类型、SRID 与坐标类型选择

PostGIS 表设计和普通 PostgreSQL 表最大的差别是数据列采用geometry或geography类型。geography使用球面计算,直接算出的距离单位是米;geometry是平面几何计算,速度快,但单位取决于坐标系。国内项目最常见的是用geometry类型配合 4326 或 3857,少部分高精度计量场景用投影坐标系。

以一块基础地块表为例:

CREATE TABLE parcels ( id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, district varchar(32), area_note text, geom geometry(Polygon, 4326) ); CREATE INDEX parcels_geom_idx ON parcels USING GIST (geom);

逻辑说明:geometry(Polygon, 4326)限制了该列只能存 4326 坐标系下的多边形。如果数据源包含点、线、面混合,不要加类型约束,直接写geometry即可,代价是类型检查变弱。最后一行创建 GIST 空间索引,这是空间查询性能的命门,第 5 章会专门展开。

参数说明:4326是 WGS84 经纬度的 EPSG 代码,适合全球范围的定位和可视化;如果业务只在国内某个局部区域做高精度面积计算,建议使用对应的高斯投影或 UTM 投影 SRID。注意引用geometry(Polygon, 4326)时,类型名称为geometry,括号里的约束在 PostGIS 中称为典型类型修饰符,查询该列时会自动校验 SRID。

4.2 从文本到空间列:WKT、ST_GeomFromText 与坐标转换

业务数据往往来自 CSV、Excel 或第三方接口,常见格式是一个点的经纬度两列,或一整段 WKT 文本。PostGIS 处理这类场景的标准函数是ST_GeomFromText和ST_SetSRID。写入一段多边形示例:

INSERT INTO parcels (district, geom) VALUES ('A区', ST_GeomFromText('POLYGON((116.40 39.90, 116.41 39.90, 116.41 39.91, 116.40 39.91, 116.40 39.90))', 4326));

ST_GeomFromText第一个参数是 WKT 文本,第二个参数是 SRID。WKT 里坐标对用空格分隔,多个点用逗号分隔,多边形首尾坐标必须闭合。如果文本坐标已经带 SRID,也可以用ST_SetSRID(ST_GeomFromText('...'), 4326)二次赋值,但要保证数据本身确实是这个坐标系,否则后面所有计算都会出错。

导入投影坐标系数据时,需要先声明原始 SRID,再转换到目标 SRID:

-- 假设源数据是 3857 Web 墨卡托坐标 UPDATE parcels SET geom = ST_Transform(ST_SetSRID(geom, 3857), 4326) WHERE ST_SRID(geom) <> 4326;

ST_Transform做真正的坐标变换,而不是简单改 SRID 标签。ST_SetSRID只修改元数据标签,不改变坐标。

4.3 典型空间操作:缓冲区、距离计算与空间连接

三组最常用的空间操作是缓冲区、距离计算和空间连接。缓冲区的业务含义是"画一个以某点为中心、半径 X 的圆":

-- 以某设施为中心,半径 500 米做缓冲区 SELECT ST_Buffer(geom, 500) AS buffer_geom FROM poi WHERE name = '某设施';

注意,在 4326 坐标系下,ST_Buffer的距离单位是度而不是米,ST_Buffer(geom, 500)画出来是一个跨度 500 度的怪东西。正确做法是先把坐标系转成以米为单位的投影坐标系,比如 3857,再缓冲区:

SELECT ST_Buffer(ST_Transform(geom, 3857), 500) AS buffer_geom FROM poi WHERE name = '某设施';

距离计算使用ST_Distance,同样受坐标系影响。如果两个点在相同投影坐标系下,结果以该投影坐标系的单位为准;4326 下结果为度。为了拿准确的米数,建议ST_Distance时把两侧都先ST_Transform到 3857 或使用geography类型:

SELECT ST_Distance( ST_Transform(a.geom, 3857), ST_Transform(b.geom, 3857) ) AS dist_m FROM poi a, poi b WHERE a.id < b.id;

a.id < b.id是一种老式的自连接去重写法,保证每对点只算一次距离。

空间连接典型场景是按网格或行政区统计落点数量:

SELECT g.grid_id, count(p.id) FROM grid g LEFT JOIN poi p ON ST_Intersects(g.geom, p.geom) GROUP BY g.grid_id;

ST_Intersects判断两个几何对象是否有交集。配合USING GIST索引,这类连接在百万量级数据下依然能保持亚秒级响应。这一章的函数足够覆盖日常 70% 的空间查询了,下一章来聊聊那些让我反复翻车的安装与用法问题。

5. 避坑指南:从安装到使用的五个高频翻车点

PostGIS 的功能大家都会用,真正拉开差距的是遇到问题时的排查速度。这里整理五条我亲测踩过的高频问题,全部按照现象、原因、解决的顺序写,替换掉你脑补的"重新装一遍"方案。

5.1 现象:CREATE EXTENSION 报控制文件不存在

现象:在新建的数据库里执行CREATE EXTENSION postgis;,报错提示could not open extension control file "C:/.../extension/postgis.control": No such file or directory。

原因:.control和对应的 SQL 文件没有放进 PostgreSQL 的share/extension目录。常见于解压后直接双击执行批处理脚本,但脚本里的路径与实际 PG 安装路径不一致;或者把 zip 内的子目录结构解压错了,.control文件仍套在多层子目录里。

解决:回到 3.2 节,用pg_config --sharedir拿到真实的 shared 目录,把.control和postgis--*.sql一次性复制到%sharedir%\extension。如果手动复制,建议用dir核对一遍文件是否真的到位,再重新执行CREATE EXTENSION。

5.2 现象:扩展创建成功,但函数调用报函数不存在

现象:CREATE EXTENSION postgis;成功,没有报错。但执行SELECT ST_Buffer(geom, 10);或SELECT postgis_version();时提示function postgis_version() does not exist。

原因:当前连接的search_path没有包含 PostGIS 扩展实际安装的 schema。如果你的扩展装进了public,当前用户又在public之前声明了其他 schema,PostgreSQL 就找不到这个函数。

解决:先查扩展实际安装位置:

SELECT extnamespace::regnamespace AS installed_schema, extname FROM pg_extension WHERE extname = 'postgis';

再在数据库级别固定搜索路径,避免每个连接都要手动 SET:

ALTER DATABASE gisdb SET search_path = public, pgrouting, tiger, tiger_data;

这样后续连接默认从public开始找函数,tiger和tiger_data是 postgis_tiger_geocoder 需要的 schema。注意search_path里没有pg_catalog时,PostgreSQL 会隐式先搜系统目录,一般不会出问题。

5.3 现象:空间查询慢到没法用,全表扫描却查不出问题

现象:一张几十万行的 POI 表,按范围过滤或按距离排序时,查询要几秒甚至几十秒。EXPLAIN ANALYZE显示 Seq Scan,没有走索引。

原因:表中根本不存在 GIST 索引,或者索引列的表达式与查询谓词使用的函数不一致。最常见的是WHERE ST_Intersects(geom, ...)里的geom列没有建立索引,或者索引建在了ST_Transform(geom, 3857)上,但查询时直接用了geom,导致索引完全命中不了。

解决:为原始几何列建立 GIST 索引:

CREATE INDEX idx_parcels_geom ON parcels USING GIST (geom);

建完索引后重新执行EXPLAIN ANALYZE SELECT ... WHERE ST_Intersects(geom, ST_GeomFromText(...)),确认计划中显示Index Scan using idx_parcels_geom。如果查询在函数内部才做坐标转换,比如ST_Intersects(ST_Transform(geom, 3857), target3857_geom),可以考虑把转换结果落到普通列再建索引,或者直接用 columns 的投影坐标版本存一份。

5.4 现象:64 位系统、版本全对,仍报无法加载动态库

现象:PostgreSQL 是 64 位,PostGIS 是 x64,扩展文件都放进去了,但启动服务或首次调用空间函数时报错could not load library "C:/.../lib/postgis-3.dll": The specified module could not be found。

原因:这类报错最常出现在 Windows 上,一是 DLL 依赖的 Visual C++ 运行库缺失,二是因为 PostgreSQL 服务一直在运行,解压时 DLL 被占用或没覆盖成功,三是服务未重启,旧进程还持有老版本动态库。这个报错属于典型的"看着像玄学,实际是运行库和进程状态"问题。

解决:按顺序做三件事。第一,装一遍对应版本的 Visual C++ Redistributable。第二,停掉 PostgreSQL 服务,确认lib目录下的postgis-3.dll和postgis-3.5.dll等文件存在且时间戳是刚解压的。第三,重启服务,再执行SELECT postgis_full_version();验证。

5.5 现象:pgRouting 的函数一个都查不到

现象:已经执行过CREATE EXTENSION postgis;,但没有执行CREATE EXTENSION pgrouting;,或者执行成功后在查询pgr_dijkstra时提示函数不存在。

原因:pgRouting 是独立于 PostGIS 主扩展的模块,它的函数不随postgis自动加载。另外,pgr_dijkstra这类函数要求图数据满足起点节点、终点节点和边权重的特定结构,有时候函数存在但查询报列名不匹配,容易被误判为"没装扩展"。

解决:显式启用扩展:

CREATE EXTENSION pgrouting; SELECT pgr_version();

启用后,pgr_version()应返回 3.x 版本号。随后为路径分析准备一张带source、target、cost列的网络表,并执行pgr_createTopology生成拓扑关系。如果函数存在但报列名不存在,检查网络表的列名是否为start_id或source,pgRouting 的接口参数名是固定给定的,不能随意改名。

6. 进阶收尾:空间索引、CLUSTER 与一条自查命令

PostGIS 用熟之后,性能问题和环境排查会成为日常工作的主题。这一章收在两个具体动作上:GIST 索引的生命周期管理,以及一条命令完成环境体检。

6.1 GIST 索引:空间查询性能的命门

GIST 索引不是建完就完事的。大量增删改之后,索引会膨胀,查询计划可能退化成 Seq Scan。定期重建索引是我维护空间库的固定动作:

REINDEX INDEX idx_parcels_geom;

如果一张表的空间数据几乎不再变化,可以做一次物理排序,把空间上相近的数据在磁盘上也排到一起。CLUSTER命令可以让查询更加高效:

CLUSTER parcels USING idx_parcels_geom;

注意CLUSTER会重写整张表并获取锁,务必在维护窗口执行。我一般选在每周日凌晨跑一次,配合ANALYZE更新统计信息。

6.2 一条命令自查整个 PostGIS 环境

排查环境问题时,我习惯执行一条组合查询,把版本、扩展清单和关键配置一次性拉出来:

SELECT postgis_full_version(); SELECT extname, extversion FROM pg_extension WHERE extname IN ('postgis','pgrouting','pointcloud','address_standardizer') ORDER BY extname; SELECT setting FROM pg_settings WHERE name = 'search_path';

第一行确认 PostGIS 核心库及其底层依赖是否完整;第二行确认扩展是否全部注册;第三行看当前连接的 schema 搜索路径。三行输出放在一起,大部分环境问题都能定位到具体方向。

从那以后,我每次配完新的空间库,都强制自己走一遍"版本函数 → 扩展清单 → EXPLAIN 验证"这三个动作,再小心的环节出过问题,后面就再也不给故障留机会。希望这套检查习惯能帮你在后面的项目里少走几步弯路。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询