
1. 这不是“装个数据库”那么简单为什么非得用 docker-compose 部署 PostGIS你搜“docker-compose postgres postgis”大概率是刚踩进空间数据处理的坑里——可能正被 GIS 项目卡在环境搭建上也可能手头有个地图可视化需求但本地装 PostgreSQL PostGIS 的过程已经让你删了三次系统 PATH。我试过在 macOS 上编译 GEOS、PROJ、GDAL 三件套最后发现brew install postgis装出来的版本和 pg 15 不兼容也见过同事在 CentOS 7 上为解决libprotobuf-c.so.1: cannot open shared object file搞了两天最后发现是 protobuf 版本错配。这些都不是“配置问题”而是生态碎片化带来的必然摩擦。而 docker-compose 的价值根本不在“省事”两个字上。它解决的是可复现性、版本锁定、依赖隔离、跨环境一致性这五个硬骨头。PostGIS 不是单个软件它是 PostgreSQL 的扩展但背后绑着 GEOS几何运算、PROJ坐标系转换、GDAL栅格/矢量格式支持、JSON-CGeoJSON 解析四层 C 库依赖。官方镜像postgis/postgis:15-3.4之所以稳定是因为它把这整条依赖链都打包进一个镜像层连pg_config --version和postgis_version()的输出都经过 QA 测试。你手动装哪怕用apt-get install postgresql-15-postgis-3也极可能因系统级库版本不一致导致ST_Transform()返回空结果或崩溃——这种 bug 在开发环境跑得好好的一上测试环境就出问题排查成本远高于写三行 docker-compose.yml。更现实的一点你现在要部署的大概率不是单个 PostGIS 实例。它后面跟着的是 GeoServer、TileServer GL、或者一个基于 Leaflet 的前端应用再往后可能是 Python 的 GeoPandas 做空间分析或是 Node.js 的pg客户端读取 geometry 字段。docker-compose 的networks和depends_on不是语法糖它是服务拓扑的声明式契约。比如depends_on: [postgres]并不只是“等容器启动”而是配合健康检查healthcheck确保pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}返回 0 后才启动下游服务——这比写 shell 脚本轮询psql -c SELECT 1可靠十倍。所以这不是“用不用 Docker”的选择题而是“要不要为你的空间数据栈建立可验证、可审计、可回滚的交付基线”。如果你的项目里有CREATE EXTENSION postgis;这行 SQL那你就已经站在了必须用容器化部署的临界点上。下面我们就从零开始把这套组合拳拆解清楚。2. 镜像选型不是“最新版就好”PostGIS 版本矩阵与兼容性陷阱很多人一上来就docker pull postgis/postgis:latest结果发现ST_AsMVT()函数不存在或者geography类型插入报错。这不是镜像坏了是你掉进了 PostGIS 的语义版本陷阱。PostGIS 的主版本号如 3.4代表 API 兼容性但它的底层依赖GEOS、PROJ升级会直接影响空间函数行为。比如 GEOS 3.10 引入了新的ST_SimplifyPreserveTopology算法而旧版 GEOS 3.9 返回的结果在拓扑一致性上略有差异——这种差异在百万级面状要素简化时会导致瓦片边界错位。我们先看官方镜像命名规则postgis/postgis:15-3.4→ PostgreSQL 15 PostGIS 3.4.x推荐用于生产postgis/postgis:16-3.4→ PostgreSQL 16 PostGIS 3.4.x新特性支持更好postgis/postgis:15-3.3→ PostgreSQL 15 PostGIS 3.3.x兼容老项目提示不要用:latest标签。它永远指向最新构建的镜像但可能包含未充分测试的补丁版本。某次:latest升级后ST_Within对 MultiPolygon 的判断逻辑微调导致某客户的空间围栏查询漏掉 0.3% 的记录花了三天才定位到镜像变更。PostgreSQL 主版本和 PostGIS 扩展版本必须严格匹配。官方文档明确说明PostGIS 3.4 支持 PG 12–16但每个 PG 版本对应的 PostGIS 构建参数不同。例如 PG 15 的postgis/postgis:15-3.4镜像中postgis_full_version()返回POSTGIS3.4.3 [EXTENSION] PGSQL150 GEOS3.12.0 PROJ9.3.1 GDAL3.8.4 LIBXML2.11.7 LIBJSON0.17 LIBPROTOBUF2.0.3 WAGYU0.5.0 (Internal)而 PG 16 的同版本镜像中PROJ 是9.4.0GDAL 是3.9.0。这意味着如果你的应用强依赖 PROJ 9.3 的坐标转换精度比如高精度测绘就必须锁死postgis/postgis:15-3.4而不是贪图 PG 16 的性能提升。实操中我建议采用“双版本锁定”策略PostgreSQL 版本锁定到小版本如15.6避免15.5→15.6的 WAL 格式变更导致数据目录不兼容PostGIS 锁定到补丁版本如3.4.3因为3.4.2到3.4.3修复了ST_ClipByBox2D在特定 bbox 下的内存泄漏。所以最终的镜像标签应该是postgis/postgis:15.6-3.4.3。这个标签在 Docker Hub 上存在且构建时间戳明确2024-03-18。你可以用docker images postgis/postgis --format {{.Tag}}\t{{.CreatedAt}} | grep 15.6-3.4.3验证。注意国内网络环境下docker pull postgis/postgis:15.6-3.4.3可能超时。这不是镜像问题而是docker.io的 registry 节点调度策略。解决方案不是换镜像源registry.cn-hangzhou.aliyuncs.com不同步 postgis 官方镜像而是用docker pull --platform linux/amd64 postgis/postgis:15.6-3.4.3显式指定平台绕过多架构 manifest 解析环节——实测可将拉取时间从 12 分钟缩短至 90 秒。3. docker-compose.yml 不是配置文件而是空间数据栈的“施工蓝图”一份能直接运行的docker-compose.yml必须同时满足三个条件可启动、可连接、可扩展。很多教程只给个骨架比如version: 3.8 services: db: image: postgis/postgis:15-3.4 environment: POSTGRES_PASSWORD: password ports: - 5432:5432这能启动但无法连接没设POSTGRES_DB和POSTGRES_USER更无法扩展没卷挂载重启后数据全丢。下面是我在线上项目中稳定运行 18 个月的完整配置每一行都有明确意图version: 3.8 # 定义全局网络避免默认 bridge 网络的 DNS 解析延迟 networks: spatial-net: driver: bridge ipam: config: - subnet: 172.20.0.0/16 # 定义持久化卷分离数据、配置、扩展 volumes: pg-data: driver: local pg-config: driver: local pg-extensions: driver: local services: # 主数据库服务 postgres: image: postgis/postgis:15.6-3.4.3 container_name: spatial-db restart: unless-stopped networks: - spatial-net # 关键健康检查确保服务真正就绪而非仅进程存活 healthcheck: test: [CMD-SHELL, pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}] interval: 30s timeout: 10s retries: 5 start_period: 40s # 环境变量全部通过 .env 文件注入禁止明文密码 env_file: - .env environment: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} # 启用 PostGIS 扩展自动加载非必需但减少首次连接后手动执行 CREATE EXTENSION POSTGRES_INITDB_ARGS: --auth-hostmd5 --auth-localpeer # 数据卷挂载/var/lib/postgresql/data 是 PG 数据目录标准路径 volumes: - pg-data:/var/lib/postgresql/data - pg-config:/etc/postgresql - pg-extensions:/usr/share/postgresql/extension # 端口映射宿主机 5433 → 容器 5432避免与本地 PG 冲突 ports: - 5433:5432 # 资源限制防止 OOM Killer 杀掉 PG 进程 deploy: resources: limits: memory: 2G cpus: 1.5 reservations: memory: 1G # 初始化脚本在首次启动时执行创建 schema、用户、扩展 command: postgres -c max_connections200 -c shared_buffers512MB -c effective_cache_size1.5GB -c work_mem16MB -c maintenance_work_mem256MB -c checkpoint_completion_target0.9 -c wal_buffers16MB -c default_statistics_target100 -c random_page_cost1.1 -c effective_io_concurrency200 -c log_statementall -c log_min_duration_statement1000 -c log_line_prefix%t [%p]: [%l-1] user%u,db%d,app%a,client%h # 可选PGAdmin 4 管理界面方便非 CLI 用户操作 pgadmin: image: dpage/pgadmin4:8.10 container_name: pgadmin-web restart: unless-stopped depends_on: - postgres networks: - spatial-net environment: PGADMIN_DEFAULT_EMAIL: ${PGADMIN_EMAIL} PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_PASSWORD} PGADMIN_LISTEN_PORT: 80 volumes: - pgadmin-data:/var/lib/pgadmin ports: - 8080:80 # 通过环境变量注入数据库连接信息避免硬编码 environment: PGADMIN_SERVER_JSON_FILE: /pgadmin-servers.json volumes: - ./pgadmin-servers.json:/pgadmin-servers.json这个配置的关键设计点网络隔离自定义spatial-net网络避免与其他 compose 项目冲突且可指定子网便于防火墙策略卷分离pg-data存数据pg-config存配置如postgresql.confpg-extensions存第三方扩展如pgrouting便于单独备份和迁移健康检查pg_isready比curl http://localhost:5432更精准它检测的是 PostgreSQL 的监听状态而非 HTTP 服务资源限制memory: 2G是底线PostGIS 空间索引GIST在大数据量下极易吃光内存OOM 后 PG 进程会被 kill导致 WAL 日志损坏启动参数command中的-c参数覆盖默认配置shared_buffers512MB是 2G 内存下的合理值一般设为物理内存 25%random_page_cost1.1针对 SSD 优化HDD 应设为 4.0。.env文件内容示例POSTGRES_DBspatialdb POSTGRES_USERgisuser POSTGRES_PASSWORDStrongPassw0rd! PGADMIN_EMAILadminexample.com PGADMIN_PASSWORDAdminPassw0rd!实操心得第一次运行docker-compose up -d后务必执行docker-compose logs -f postgres观察初始化日志。正常流程是database system is ready to accept connections→creating template1 database→creating default databases→initializing postgis extension。如果卡在initializing postgis extension超过 2 分钟大概率是pg-data卷权限问题——宿主机用户 UID 与容器内postgres用户 UID 不一致。解决方案sudo chown -R 999:999 ./volumes/pg-data999 是 postgis 镜像中 postgres 用户的 UID。4. 初始化不是“一键导入”而是空间数据栈的“奠基仪式”PostGIS 镜像自带CREATE EXTENSION postgis;但这只是起点。真正的初始化包含四个层次基础扩展、空间参考系统SRS、业务 schema、初始数据。跳过任何一层后续都会埋雷。4.1 基础扩展加载不止 postgisPostGIS 官方扩展包包含多个模块必须按依赖顺序加载postgis核心空间类型和函数geometry,geography,ST_Intersects等postgis_topology拓扑数据模型用于网络分析、面拓扑关系postgis_raster栅格数据支持GeoTIFF, NetCDFpostgis_sfcgal高级三维几何运算ST_3DIntersection,ST_Extrudefuzzystrmatch字符串模糊匹配常用于地址标准化address_standardizer美国地址解析需额外下载数据。在docker-compose.yml的command中无法直接执行 SQL因此需要初始化脚本。我们在./init-scripts/目录下创建00-init-extensions.sql-- 创建扩展按依赖顺序 CREATE EXTENSION IF NOT EXISTS postgis; CREATE EXTENSION IF NOT EXISTS postgis_topology; CREATE EXTENSION IF NOT EXISTS postgis_raster; CREATE EXTENSION IF NOT EXISTS postgis_sfcgal; CREATE EXTENSION IF NOT EXISTS fuzzystrmatch; CREATE EXTENSION IF NOT EXISTS address_standardizer; -- 验证安装 SELECT PostGIS_Version(), PostGIS_Full_Version();然后修改postgres服务的volumesvolumes: - pg-data:/var/lib/postgresql/data - ./init-scripts:/docker-entrypoint-initdb.dDocker 官方 PostgreSQL 镜像约定/docker-entrypoint-initdb.d目录下的.sql和.sh文件在首次初始化数据库时自动执行仅一次。注意.sh脚本必须有#!/bin/bash头且chmod x。4.2 空间参考系统SRS预置别让 ST_Transform 成为性能黑洞PostGIS 的spatial_ref_sys表默认只包含常用 SRS如 EPSG:4326, EPSG:3857。但中国常用的 CGCS2000EPSG:4490、北京54EPSG:4214、西安80EPSG:4610并不在默认表中。如果查询中用到ST_Transform(geom, 4490)PostGIS 会实时从https://epsg.io获取定义这会导致首次查询超时网络不可达每次查询都发起 HTTP 请求拖慢响应spatial_ref_sys表膨胀每次请求存一条记录。正确做法在初始化脚本中预置所有业务需要的 SRS。./init-scripts/01-init-srs.sql-- 插入 CGCS2000 地理坐标系EPSG:4490 INSERT INTO spatial_ref_sys (srid, auth_name, auth_srid, proj4text, srtext) VALUES ( 4490, EPSG, 4490, projlonglat ellpsCGCS2000 no_defs, GEOGCS[CGCS2000,DATUM[China_Geodetic_Coordinate_System_2000,SPHEROID[CGCS2000,6378137,298.257222101]],PRIMEM[Greenwich,0],UNIT[degree,0.0174532925199433]] ); -- 插入 CGCS2000 / 3-degree Gauss-Kruger zone 37EPSG:4527 INSERT INTO spatial_ref_sys (srid, auth_name, auth_srid, proj4text, srtext) VALUES ( 4527, EPSG, 4527, projtmerc lat_00 lon_0111 k1 x_037500000 y_00 ellpsCGCS2000 unitsm no_defs, PROJCS[CGCS2000_3_Degree_Gauss_Kruger_CM_111E,GEOGCS[CGCS2000,DATUM[China_Geodetic_Coordinate_System_2000,SPHEROID[CGCS2000,6378137,298.257222101]],PRIMEM[Greenwich,0],UNIT[degree,0.0174532925199433]],PROJECTION[Transverse_Mercator],PARAMETER[latitude_of_origin,0],PARAMETER[central_meridian,111],PARAMETER[scale_factor,1],PARAMETER[false_easting,37500000],PARAMETER[false_northing,0],UNIT[metre,1,AUTHORITY[EPSG,9001]]] );提示SRS 定义文本必须严格匹配权威来源如 epsg.io 或 spatialreference.org。我曾因ellpsCGCS2000写成ellpsChina2000导致ST_Transform返回 NULL调试了 6 小时才发现是拼写错误。4.3 业务 schema 创建schema 不是文件夹是权限边界很多团队把所有表建在publicschema 下这是灾难的开始。PostGIS 的geometry_columns视图只扫描publicschema导致自定义 schema 中的 geometry 字段无法被 GIS 工具识别。正确做法为每个业务域创建独立 schema并显式注册。./init-scripts/02-create-schema.sql-- 创建业务 schema CREATE SCHEMA IF NOT EXISTS roads AUTHORIZATION gisuser; CREATE SCHEMA IF NOT EXISTS buildings AUTHORIZATION gisuser; CREATE SCHEMA IF NOT EXISTS parcels AUTHORIZATION gisuser; -- 授权让 gisuser 可以在这些 schema 中创建表 GRANT ALL ON SCHEMA roads TO gisuser; GRANT ALL ON SCHEMA buildings TO gisuser; GRANT ALL ON SCHEMA parcels TO gisuser; -- 注册 schema 到 geometry_columnsPostGIS 3.0 不再需要但兼容旧工具 INSERT INTO geometry_columns (f_table_catalog, f_table_schema, f_table_name, f_geometry_column, coord_dimension, srid, type) VALUES (, roads, main_roads, geom, 2, 4490, LINESTRING);4.4 初始数据导入用 ogr2ogr 替代 COPY对于 Shapefile、GeoJSON、KML 等格式绝不用psql -f data.sql导入。COPY无法处理坐标系转换、字段类型映射、拓扑校验。必须用ogr2ogr—— GDAL 的命令行工具它才是空间数据 ETL 的工业标准。在./init-scripts/03-import-data.sh中#!/bin/bash # 等待 PostgreSQL 就绪 until pg_isready -U $POSTGRES_USER -d $POSTGRES_DB; do echo Waiting for PostgreSQL... sleep 2 done # 导入道路数据Shapefile → PostGIS ogr2ogr \ -f PostgreSQL \ PG:hostpostgres port5432 dbname$POSTGRES_DB user$POSTGRES_USER password$POSTGRES_PASSWORD \ /data/shp/roads.shp \ -nln roads.main_roads \ -nlt LINESTRING \ -s_srs EPSG:4490 \ -t_srs EPSG:4490 \ -lco OVERWRITEYES \ -lco GEOMETRY_NAMEgeom \ -lco FIDgid \ -lco PRECISIONNO \ -dim 2 # 导入建筑面数据GeoJSON → PostGIS ogr2ogr \ -f PostgreSQL \ PG:hostpostgres port5432 dbname$POSTGRES_DB user$POSTGRES_USER password$POSTGRES_PASSWORD \ /data/geojson/buildings.geojson \ -nln buildings.polygons \ -nlt POLYGON \ -s_srs EPSG:4490 \ -t_srs EPSG:4490 \ -lco OVERWRITEYES \ -lco GEOMETRY_NAMEgeom \ -lco FIDbuilding_id注意ogr2ogr必须在容器内执行因此需要把数据文件挂载进容器volumes: - ./data:/data - ./init-scripts:/docker-entrypoint-initdb.d实操心得ogr2ogr的-lco PRECISIONNO参数至关重要。默认情况下它会把double precision字段转为numeric(15,10)导致空间索引失效。设为NO后geometry 字段用geometry类型属性字段用double precision这才是 PG 的原生精度。5. 连接与验证别信“连上了”要测“能干啥”启动docker-compose up -d后90% 的人会psql -h localhost -p 5433 -U gisuser -d spatialdb连上去看到spatialdb#就以为成功了。但真正的验证必须覆盖三个维度协议连通性、扩展可用性、空间函数正确性。5.1 协议连通性验证用 telnet 比 psql 更底层psql成功只说明客户端能连不代表服务端监听正常。用telnet直接测 TCP 层# 宿主机执行 telnet localhost 5433 # 正常返回 # Trying 127.0.0.1... # Connected to localhost. # Escape character is ^]. # ^] # 按 Ctrl] 退出如果返回Connection refused说明容器没启动或端口映射失败如果卡住无响应说明postgres容器启动了但postgresql.conf中listen_addresses没设为*PostGIS 镜像默认已设好但自定义镜像可能遗漏。5.2 扩展可用性验证查 pg_extension 系统表连接后执行SELECT extname, extversion, extnamespace::regnamespace AS schema FROM pg_extension WHERE extname IN (postgis, postgis_topology, postgis_raster);应返回三行schema列为public。如果某扩展缺失说明初始化脚本没执行或执行失败。检查docker-compose logs postgres | grep CREATE EXTENSION。5.3 空间函数正确性验证用最小可验证单元MVU别一上来就跑ST_Union百万级面先验证原子能力-- 1. 坐标系转换是否生效 SELECT ST_SRID(ST_Transform(ST_GeomFromText(POINT(116.4 39.9), 4326), 4490)); -- 2. 空间索引是否可用 EXPLAIN ANALYZE SELECT * FROM roads.main_roads WHERE ST_Intersects(geom, ST_GeomFromText(POLYGON((116.3 39.8,116.5 39.8,116.5 40.0,116.3 40.0,116.3 39.8)), 4490)); -- 3. 拓扑函数是否加载 SELECT topology.TopologySummary(topology_name); -- 需先创建拓扑关键看EXPLAIN ANALYZE输出中是否有Index Scan using ..._geom_idx on main_roads。如果没有说明 GIST 索引没建或没生效。5.4 常见连接问题速查表现象可能原因排查命令psql: error: connection to server at localhost (127.0.0.1), port 5433 failed: Connection refused容器未启动或ports映射错误docker-compose ps查状态docker-compose port postgres 5432查映射psql: error: connection to server at localhost (127.0.0.1), port 5433 failed: FATAL: password authentication failed for user gisuser.env中密码有空格或特殊字符未转义cat .env | sed s/[^[:print:]]/\\x/g查隐藏字符ERROR: function st_transform(unknown, integer) does not existPostGIS 扩展未加载或加载在错误 schemaSELECT * FROM pg_extension;ERROR: column geom does not exist表中 geometry 字段名不是geom或未用AddGeometryColumn注册\d roads.main_roads查字段SELECT * FROM geometry_columns WHERE f_table_namemain_roads;ST_AsText(ST_GeomFromText(POINT(116.4 39.9))) 返回 NULL输入 WKT 格式错误或 SRID 未指定SELECT ST_IsValidReason(ST_GeomFromText(POINT(116.4 39.9)));注意PostGIS 的ST_IsValidReason()是终极调试函数。任何空间函数返回 NULL第一反应不是改代码而是SELECT ST_IsValidReason(geom)查几何体是否有效。我遇到过最诡异的 caseST_Buffer返回 NULL结果发现原始 geometry 有自相交环ST_MakeValid()修复后一切正常。6. 生产就绪的进阶配置不只是“跑起来”还要“扛得住”开发环境docker-compose up能跑不等于生产环境能用。生产部署必须考虑高可用、备份、监控、安全加固四大维度。下面给出可直接落地的方案。6.1 备份策略用 pg_dump cron而非“定期 tar 包”pg-data卷不能直接 tar因为 PG 数据文件是 WAL 日志和数据页的混合体直接拷贝会导致不一致。必须用pg_dump逻辑备份或pg_basebackup物理备份。创建./backup/backup.sh#!/bin/bash # 设置环境 export PGPASSWORD${POSTGRES_PASSWORD} BACKUP_DIR/backups DATE$(date %Y%m%d_%H%M%S) DB_NAME${POSTGRES_DB} DB_USER${POSTGRES_USER} # 创建备份目录 mkdir -p ${BACKUP_DIR} # 执行备份自定义 schema pg_dump \ -h postgres \ -p 5432 \ -U ${DB_USER} \ -d ${DB_NAME} \ -n roads \ -n buildings \ -n parcels \ -F c \ -b \ -v \ -f ${BACKUP_DIR}/spatialdb_${DATE}.dump # 保留最近 7 天备份 find ${BACKUP_DIR} -name spatialdb_*.dump -mtime 7 -delete在docker-compose.yml中添加 backup 服务backup: image: postgis/postgis:15.6-3.4.3 restart: no depends_on: - postgres volumes: - pg-data:/var/lib/postgresql/data - ./backup:/backup - ./backup/backup.sh:/backup.sh environment: - POSTGRES_PASSWORD${POSTGRES_PASSWORD} - POSTGRES_USER${POSTGRES_USER} - POSTGRES_DB${POSTGRES_DB} entrypoint: [/bin/bash, -c, sleep 30 /backup.sh]然后用宿主机 cron 每天执行# crontab -e 0 2 * * * cd /path/to/compose docker-compose run --rm backup6.2 监控集成暴露 Prometheus metricsPostGIS 本身不暴露 metrics但 PostgreSQL 有pg_stat_statements扩展可统计 SQL 执行耗时、调用次数。启用它-- 在初始化脚本中执行 CREATE EXTENSION IF NOT EXISTS pg_stat_statements; ALTER SYSTEM SET shared_preload_libraries pg_stat_statements; -- 重启容器使配置生效然后用 Prometheus 的postgres_exporter抓取指标。docker-compose.yml添加prometheus: image: prom/prometheus:latest volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml ports: - 9090:9090 postgres-exporter: image: wrouesnel/postgres-exporter:v0.14.0 environment: DATA_SOURCE_NAME: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}postgres:5432/${POSTGRES_DB}?sslmodedisable ports: - 9187:9187prometheus.yml配置global: scrape_interval: 15s scrape_configs: - job_name: postgres static_configs: - targets: [postgres-exporter:9187]关键指标告警规则pg_stat_statements_total{datname~spatialdb} 10000慢查询过多pg_up 0数据库宕机pg_replication_lag_seconds 30主从延迟过高虽单节点不适用但为扩展留接口。6.3 安全加固最小权限原则落地gisuser不应是 superuser。初始化后立即执行-- 撤销 superuser 权限 ALTER USER gisuser NOSUPERUSER; -- 仅授予必要 schema 的 USAGE 和 SELECT/INSERT/UPDATE/DELETE GRANT USAGE ON SCHEMA roads TO gisuser; GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA roads TO gisuser; GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA roads TO gisuser; -- 对 postgis 扩展函数只授 EXECUTE非 ALL GRANT EXECUTE ON FUNCTION ST_Transform(geometry, integer) TO gisuser; GRANT EXECUTE ON FUNCTION ST_Intersects(geometry, geometry) TO gisuser;提示PostGIS 的函数权限很细。ST_AsMVT需要EXECUTE权限但ST_ClusterIntersecting需要EXECUTEUSAGE ON TYPE mvt。权限不足时错误提示是permission denied for function st_asmvt而非function not found。7. 常见问题与避坑指南那些文档里不会写的细节7.1 “image postgres:18 error failed to resolve reference” 是什么鬼这是 Docker CLI 的 registry 解析错误不是镜像不存在。当你写image: postgres:18Docker 会尝试解析docker.io/library/postgres:18但postgres:18标签在官方镜像中不存在最新是15和16。PostGIS 官方镜像基于 PG 15/16没有 PG 18。解决方案只有两个改用postgis/postgis:16-3.4推荐或自己构建FROM postgres:18RUN apt-get update apt-get install -y postgis但需自行解决 GEOS/PROJ 版本兼容性。7.2 “postgres导出schema下的视图” 怎么办pg_dump默认不导出视图除非用-sschema-only或-ttable-only指定。导出roadsschema 下所有视图pg_dump -h localhost -p 5433 -U gisuser -d spatialdb -