当前位置: 首页 > news >正文

PostgreSQL快速入门

本文是写者准备的笔记,希望通过分享可以帮助没有接触过postgreSQL的人快速入门,对其底层原理有一个大概了解。但若您没有接触过数据结构及数据库,看起来可能会比较吃力,建议要先了解数据结构和数据库(如:MySQL,Oracle等),不过您也可以先收藏。

PostgreSQL jsonb:底层原理 + 常用操作 + 索引策略

原理(为什么 jsonbjson 快)
jsonb 并不是把 JSON 当文本存起来,而是把 JSON 解析、规范化后保存成二进制内部结构(token 列表 + 类型标记 + 指针/偏移),因此可以直接按键/值访问、做结构化检索和索引;同时重复键会被合并(以最后一个为准),键的顺序不保留。这个二进制/规范化的存储方式是 jsonb 性能优势的根源。 ([PostgreSQL][1])

常用运算符(语义要能背清楚)

  • ->:返回 jsonb 值(对象/数组)。
  • ->>:返回文本(text 值)。
  • @>:是否包含(containment),比如 data @> '{"role":"admin"}'
  • ? / ?| / ?&:键存在判断。
  • #> / #>>:路径访问(嵌套字段)。
    理解这些运算符,能帮你判断哪种索引会生效(比如 @> 最容易命中 GIN)。

索引原理(为什么用 GIN?)

  • GIN(倒排索引) 的思想:把每个“键/词/lexeme”当做词典项,索引里为每个词保留一个“posting list”(行的引用集合)。查找时先定位词,再去定位对应的行——非常适合“包含/存在/多值”检索(例如 jsonbtsvector、数组)。
  • 优点:查找(AND/包含)速度快;缺点:写入/更新开销大(需要维护 posting lists)。 ([PostgreSQL][2])

jsonb_path_ops vs jsonb_ops(重点)

  • jsonb_ops(默认)为每个键和值都生成单独的索引项,功能全面(可支持存在/包含/键值查找等),但索引更大。
  • jsonb_path_ops 更紧凑,只支持部分操作(最常用于 @> 包含查询),因此索引更小、查询 @> 更快,但不支持所有 jsonb 操作。面试中要能说出:如果你只做包含检查,jsonb_path_ops 更好;如果你需要键/值的更多灵活查找,选 jsonb_ops。 ([PostgreSQL][1])

表达式索引
如果常查 data->>'status' = 'active',比起给整个 jsonb 建 GIN,更轻量的方式是为 (data->>'status')btree 表达式索引:

CREATE INDEX idx_users_status ON users ((info->>'status'));

这是 CPU+I/O / 空间折衷的常见做法 —— 面试里常被要求解释“为什么不用整列 GIN”。

写/读权衡与维护

  • GIN 查快、写慢;GiST/BTREE 写快一点。
  • 大量写入场景可以:用批量插入 + 临时禁用索引/后建索引,或使用部分索引(只索引活跃数据)来降低开销。 ([PostgreSQL][2])

2) 全文检索(PostgreSQL FTS)— 原理及讲解

基本原理(词项化 → 词干化 → 向量化)

  • 文本通过 to_tsvector(config, text) 被转换成一组词项(lexemes),同时移除停用词、做词干化(stemming)、规范化(小写、去标点)。这就是 tsvector 的内容:词项 + 出现位置等元信息。查询端把用户查询变成 tsqueryplainto_tsquery / to_tsquery / websearch_to_tsquery)。匹配用 @@ 运算符。Postgres 提供 ts_rank/ts_rank_cd 用于相关度评分。 ([PostgreSQL][3])

索引 & 实现要点

  • tsvector 通常用 GIN 索引(倒排索引非常适合检索词项)。在高并发写场景请注意 GIN 的写成本。
  • 实战建议:把 tsvector 作为**持久列(generated/stored)**保存(避免每次查询运行 to_tsvector),并在其上建 GIN 索引,这样查询速度和稳定性最好。 ([PostgreSQL][4])

排名/短语/前缀匹配

  • ts_rank 可按词频、位置给予评分。
  • to_tsquery('foo & bar') 支持布尔组合;to_tsquery('foo:*') 支持词前缀。选择合适的字典(english/simple 等)会影响词干化与停用词。

3) 索引类型内部机理(btree / GIN / GiST / SP-GiST / BRIN)——要点 + 原理

B-tree(经典)

  • 基于平衡 B 树页结构(有序分支),适用于等值/范围/排序查询(= / < / > / ORDER BY)。典型、用途最广。

GIN(倒排索引)——“词到行”

  • 内部维护“词 ➜ posting list”;查询时合并 posting lists。非常高效做多值/包含查询(jsonbtsvector、数组)。但插入/更新需要维护 posting list,开销大(尤其 posting list 很长时)。维护时可通过增加 maintenance_work_mem 加速索引构建。 ([PostgreSQL][2])

GiST(广义搜索树)——“可以把很多问题变成范围/相似性问题”

  • GiST 不是具体的数据结构,而是“索引框架”——允许扩展通过定义“内涵/分割/距离”的方式索引复杂类型(例如几何的 bounding-box)。GiST 对空间索引(PostGIS)非常重要,也支持 KNN(最近邻)查询(利用 <-> 等运算符实现)。GiST 更新通常比 GIN 更轻(更适合动态数据)。 ([PostgreSQL][2], [Crunchy Data][5])

SP-GiST(空间分割)

  • 适合把空间分区(quadtrees、k-d 树思想)用到数据库索引,对某些大规模点集合在查询速度和索引大小上有优势(但比 GiST 更特殊)。

BRIN(块范围索引)

  • 适合超大表且数据按某规律局部性强(按时间、按空间范围排序的表)。BRIN 占空间小、维护便宜,但适用场景有限。

4) PostGIS(空间)—— 数据存储、SRID、精度与函数原理

geometry vs geography(要能清楚回答“选哪个”)

  • geometry:平面笛卡尔数学模型,适合投影坐标系(比如 UTM),单位取决于 SRID(SRID=3857/xxx 单位通常为米或投影单位)。计算快、功能多、适用于本地/区域性数据。
  • geography:大地测量模型(球面/椭球面),以经纬度存储但在距离计算时使用球面或大地线算法,查询距离以为单位,适合全球/跨地区距离计算,但函数选择更少、计算更慢。你在面试中要能给出“什么时候选 geography、什么时候选 geometry”的理由(如果数据区域小并且你能处理投影,优先用 geometry + 正确投影;如果跨洲/跨经度线/你要省去投影学习成本,选 geography)。 ([PostGIS][6], [PostGIS][7])

SRID 与 ST_Transform(原理)

  • SRID 指定了坐标参考系(CRS)。ST_Transform(geom, SRID) 是把几何从一个 CRS 投影/转换到另一个 CRS(内部调用 PROJ 库做数学变换)。要做精确的面积/距离计量,通常把数据投影到合适的“等距/等面积/局部 UTM”投影再计算。

5) 常用 PostGIS 函数及其“索引/性能”原理(重点)

ST_DWithin(a,b,d) —— 布尔:两者是否在 d 单位内。

  • 非常重要ST_DWithin 在实现上会自动做“bounding-box(包围盒)”预过滤(因此可以利用 GiST/GiST-like 空间索引),随后对候选集合执行精确几何距离计算(避免对全表做精确计算)。所以在 WHERE 中用 ST_DWithin 是索引友好的模式(比直接 ST_Distance(...) < d 更常推荐)。 ([PostGIS][8], [地理信息系统问答社区][9])

ST_Distance(a,b) —— 返回精确距离(geometry 的单位取决 SRID;geography 返回米/按 spheroid 计算)。

  • 如果需要排序最近邻,通常 先用 ST_DWithin 过滤(利用索引)再对结果做 ST_Distance 排序。或者使用 KNN 运算符 <-> 做索引辅助的最近邻排序(下节详细)。 ([PostGIS][8])

ST_Intersects, ST_Within —— 空间关系测试:这些函数也会被 GiST 索引的预过滤所加速(通常包含 && 包围盒操作做第一步),因此把空间约束放在 WHERE 子句中能让索引生效。


6) 最近邻(KNN)搜索:<-> 运算符与索引机制(原理)

<-> 是做什么的?

  • <-> 返回两几何的 2D 距离,并且在 ORDER BY 中使用时会触发数据库的 KNN(最近邻)算法,让查询使用 GiST 索引做“best-first”/优先队列搜索,从而高效返回最近的 N 个要素(而不是扫描全表再排序)。对 geometry 来说这能用 GiST 索引;对 geography,在较新版本的 PostGIS/Postgres 也能得到合适的行为(文档指出对 geography 返回大地球面距离的 KNN 行为)。面试中要能说出:KNN 用 <->,ST_DWithin 用来过滤并结合索引,两者各有用途。 ([PostGIS][10])

7) “查某点 5 公里范围内的商家”——一套标准答案(含执行计划意向)

假设你用 geography(Point,4326) 存经纬度(单位米),表名 shops

建表 & 索引

CREATE TABLE shops (id serial PRIMARY KEY,name text,location geography(Point,4326),props jsonb,document_tsv tsvector   -- 可选:存好全文索引向量
);-- 空间索引(GiST)
CREATE INDEX idx_shops_location_gist ON shops USING GIST (location);-- JSONB 包含查询用 GIN(如果你频繁按 props 查询)
CREATE INDEX idx_shops_props_gin ON shops USING GIN (props jsonb_path_ops);-- 全文搜索向量
CREATE INDEX idx_shops_tsv ON shops USING GIN (document_tsv);

查询:5km 内并按距离排序(索引友好)

WITH ref AS (SELECT ST_SetSRID(ST_MakePoint(116.4, 39.9), 4326)::geography AS geom
)
SELECT s.id, s.name,ST_Distance(s.location, r.geom) AS dist_m
FROM shops s, ref r
WHERE ST_DWithin(s.location, r.geom, 5000)   -- 先用索引过滤
ORDER BY dist_m
LIMIT 100;

为什么会走索引(执行计划层面)

  • ST_DWithin 会被内联成先做包围盒(bounding box)比较,从空间索引(GiST)里快速找出候选集合(通常由 Bitmap Index Scan + Bitmap Heap Scan 实现),然后对候选集做精确距离计算并返回。这样避免对全表用 ST_Distance 做昂贵计算。你可以用 EXPLAIN ANALYZE 看到 Bitmap Index Scan / Bitmap Heap Scan 等节点。 ([PostGIS][8], [地理信息系统问答社区][9])

如果想直接做最近邻(KNN)并让索引做排序

-- KNN,用 <-> 让 GiST 执行最近邻(更快的 N 最近)
SELECT s.id, s.name, s.location
FROM shops s
ORDER BY s.location <-> ST_SetSRID(ST_MakePoint(116.4,39.9),4326)::geometry
LIMIT 10;

(注意这里用 <-> 时通常对 geometry 列更普遍;对 geography 的 KNN 行为要看 PostGIS/Postgres 版本与实现细节。) ([PostGIS][10])


8) 性能优化要点(面试常问要点)

  1. 索引优先:空间索引(GiST / SP-GiST) + JSONB 的 GIN(或 jsonb_path_ops) + tsvector 的 GIN。
  2. 先过滤再排序ST_DWithin 过滤 → ST_Distance 排序;或使用 <-> 做 KNN。 ([PostGIS][8])
  3. 表达式/部分索引:把常查字段做表达式索引((props->>'category'))或用部分索引(WHERE props @> '{"active":true}')降低索引大小。
  4. 聚簇(CLUSTER)与物理局部性:对热点数据 CLUSTER 到空间索引上能提高范围查询局部性(注意这是一次性操作,后续更新会使其失效,需要定期维护)。
  5. VACUUM/ANALYZE:保持统计信息准确,查询规划器才能正确评估索引选择。
  6. 大表策略:分区(按区域/时间)、预计算网格/桶、或缩小精度用于快速过滤(例如把点映射到 tile-id,先按 tile 匹配再精确过滤)。
  7. 索引维护参数:构建大 GIN 索引时可临时增加 maintenance_work_mem 提升性能。 ([PostgreSQL][2])

9) 常见面试追问(短答模板)

  • Q:geometrygeography 何时用哪个?
    A:小范围/需要复杂空间运算/需要更快:geometry + 合适投影;跨大陆/直接以米为单位查询且不想处理投影:geography。 ([PostGIS][6])

  • Q:为什么 ST_DWithinST_Distance < d 更快?
    A:ST_DWithin 内部会做包围盒预过滤并能利用空间索引,减少精确距离计算的行数。 ([PostGIS][8], [地理信息系统问答社区][9])

  • Q:什么时候为 jsonb 建 GIN,什么时候用表达式 btree?
    A:做包含/多键匹配/全文类似查询用 GIN;常按某个固定字段查询(稳定的等值)用表达式 btree 更小更快。

  • Q:全文搜索的排名函数如何选?
    A:ts_rank/ts_rank_cd,若需位置重要性考虑 setweight() 为标题/正文等字段设置不同权重。


10) 练习题

  1. 建一个 shops 表,插入 10k 点(随机经纬度在某市范围),分别建立 GiST、GIN、tsvector 索引;对以下三种查询用 EXPLAIN ANALYZE 比较执行计划:

    • ST_DWithin 的半径搜索。
    • ORDER BY location <-> ref 的 KNN。
    • props @> '{"category":"restaurant"}' AND document_tsv @@ plainto_tsquery('pizza') AND ST_DWithin(...)(复合索引命中测试)。
  2. 把某 JSON 字段 props->>'status' 建表达式索引,观察更新/查询表现差异。

  3. to_tsvector(title||' '||body) 存为列并建 GIN,比较与直接表达式索引 to_tsvector(...) 的性能差别。


11) 可运行的示例 SQL(把上面的关键点合并,直接复制到 psql 中跑)

(下面是完整脚本:建表、插索引、插入少量示例、常用查询)

-- 创建扩展(如未安装)
CREATE EXTENSION IF NOT EXISTS postgis;
CREATE EXTENSION IF NOT EXISTS pg_trgm;  -- 若需要 trgm
CREATE EXTENSION IF NOT EXISTS fuzzystrmatch; -- 可选-- 建表
CREATE TABLE shops (id serial PRIMARY KEY,name text NOT NULL,props jsonb,location geography(Point,4326),document_tsv tsvector
);-- 索引
CREATE INDEX idx_shops_location_gist ON shops USING GIST (location);
CREATE INDEX idx_shops_props_gin ON shops USING GIN (props jsonb_path_ops);
CREATE INDEX idx_shops_tsv ON shops USING GIN (document_tsv);-- 示例数据(少量)
INSERT INTO shops (name, props, location, document_tsv) VALUES
('Pizza Place', '{"category":"restaurant","active":true}'::jsonb,ST_SetSRID(ST_MakePoint(116.401,39.905),4326)::geography,to_tsvector('english','Pizza Place best pizza in town')),
('Coffee Corner', '{"category":"cafe","active":true}'::jsonb,ST_SetSRID(ST_MakePoint(116.405,39.907),4326)::geography,to_tsvector('english','Coffee and pastries'));-- 查询:5km 内
WITH ref AS (SELECT ST_SetSRID(ST_MakePoint(116.4,39.9),4326)::geography AS geom
)
SELECT s.id, s.name, ST_Distance(s.location, r.geom) AS dist_m
FROM shops s, ref r
WHERE ST_DWithin(s.location, r.geom, 5000)
ORDER BY dist_m
LIMIT 100;-- KNN 最近邻 (示例)
SELECT id, name
FROM shops
ORDER BY location <-> ST_SetSRID(ST_MakePoint(116.4,39.9),4326)::geometry
LIMIT 10;

结语

  • jsonb 的优势来自二进制/结构化存储,适配 GIN 索引(倒排索引思想)。([PostgreSQL][1])
  • 文本搜索通过 tsvector/tsquery 实现,典型地在 tsvector 上建 GIN。([PostgreSQL][3])
  • 空间索引由 GiST(或 SP-GiST)驱动;ST_DWithin 会使用包围盒预过滤从而触发索引;最近邻用 <->。面试中把索引如何被使用讲清楚,比只写 SQL 更加重要。([PostgreSQL][2], [PostGIS][8])

http://www.dtcms.com/a/351572.html

相关文章:

  • 会议室预约小程序主要功能及预约审批流程
  • Java大厂面试全解析:从Spring Boot到微服务架构实战
  • Hadoop MapReduce 任务/输入数据 分片 InputSplit 解析
  • ProfiNet转CAN/CANopen网关技术详解-三格电子
  • uniapp uview吸顶u-sticky 无效怎么办?
  • 利用Certbot生成ssl证书配置到nginx
  • Android之穿山甲广告接入
  • Flutter 项目命名规范 提升开发效率
  • 深度学习(三):PyTorch 损失函数:按任务分类的实用指南
  • Swift 解法详解 LeetCode 363:矩形区域不超过 K 的最大数值和
  • Unity游戏打包——Mac基本环境杂记
  • Android Glide生命周期管理:实现原理与最佳实践
  • ubuntu2204安装搜狗拼音输入法
  • 基于spark的招聘岗位需求分析可视化系统设计与实现
  • 《相关法律、法规知识(五)》
  • 【数据结构】串——模式匹配
  • 微服务-23.网关登录校验-自定义GlobalFilter
  • yggjs_rbutton React按钮组件v1.0.0 示例和教程
  • Java全栈开发面试实录:从基础到实战的深度探索
  • JVM 学习与提升路线总结:从入门到精通的系统化指南
  • 前端-如何将前端页面输出为PDF并打包的压缩包中
  • PDF转图片、图片转PDF(免费)
  • AI+drawio生成流程图探索
  • Python 操作 PPT 文件:从新手到高手的实战指南
  • 重构审计体验!批量生成报表项目底稿的凭证检查表
  • 计算机术语 / 数学术语中的 trivial 与 non-trivial
  • MD5校验算法
  • Node.js(3)—— fs模块
  • Docker:部署Java后端
  • 关于电脑连接手机热点没有网络的问题