{"name":"PostgreSQL","id":"中间件-数据库-PostgreSQL","content":"# PostgreSQL\n\n## 事务\n\n### MVCC\n\n相比于 MySQL，PostgreSQL 的 MVCC 不是把旧版本放到 undo log，而是把多个版本直接存在表的 heap（行里），每个行版本包含创建/删除事务信息（xmin/xmax 等）\n\n#### 内部结构\n\n每个 heap tuple（行版本）头部包含用于 MVCC 的关键信息（简化表达）：\n\n- xmin：创建该版本的事务 ID（XID）。\n- xmax：使该版本失效的事务 ID（若被删除或被更新时设置）。\n- ctid：物理行标识（行在表中的位置）。\n- cmin/cmax：同一事务内的命令序号（决定同一事务内多次操作可见性的细节）。\n- infomask / hint bits：记录该 XID 的提交/回滚状态（便于后续扫描快速判断，减少访问 pg_xact）。\n\n另外还有 visibility map（标记某页是否“all-visible”或“all-frozen”），用于加速 VACUUM 和索引优化。\n\n#### 可见性规则\n\n当事务（或语句，见隔离级别差异）开始读数据时，会创建一个快照，快照里会包含：xmin（低界）、xmax（高界）以及一个活跃事务 ID 列表（在 xmin 到 xmax 范围内的活跃事务）\n\n快照定义了“谁在我看数据时还没完成”，只有在创建时间点之前完成的创建/删除会被视作可见/不可见\n\n#### 更新删除细节\n\n- UPDATE：Postgres 不在原地修改行，而是写入一个新的 heap tuple（新版本），并把旧版本的 xmax 设置为当前事务 ID（表示旧版本被本事务删除/无效化）。新版本的 xmin 设置为当前事务 ID。\n- DELETE：相当于把原版本的 xmax 设置为当前事务 ID（没有产生新有效版本）。直到 VACUUM 回收这些死版本，物理空间不会被完全回收（除非 VACUUM FULL 或行被 HOT 合并回收）。\n\nHOT（Heap Only Tuple）优化：如果 UPDATE 只修改非索引列，且页中有足够空间插入新版本，Postgres 可以将新版本放在同一页并利用 HOT 链接，避免更新索引（避免索引膨胀），性能和空间效率都高\n\n#### VACUUM\n\n为什么要 VACUUM？ 因为旧的 tuple 版本占空间并且持有 XID 元信息，如果不清理会导致表膨胀（bloat），并最终因 XID 回绕（wraparound）导致数据丢失风险\n\nInnoDB 把旧版本存在 undo log 中；Postgres 把旧版本存在表 heap。结果是：Postgres 依赖 VACUUM 回收死版本\n\n### 锁机制\n\n#### 行级锁\n\n- FOR UPDATE：锁住行，其他事务不能修改。\n- FOR SHARE：共享锁，其他事务可以读，不能修改。\n\n#### 表级锁\n\n支持 ACCESS SHARE / ROW EXCLUSIVE / ACCESS EXCLUSIVE 等多种模式\n\n#### Advisory Lock\n\nPostgreSQL 还支持 用户自定义锁（Advisory Lock），可以在应用层用来控制逻辑\n\n## 索引机制\n\n\nPostgreSQL 的索引是独立于表的数据结构，索引不会覆盖表中的所有数据（不像 MySQL 的 InnoDB 聚簇索引）\n\n### 索引类型\n\n- B-Tree 索引：默认索引类型。适合等值查询、范围查询\n- Hash 索引：只适合 = 查询\n- GIN 索引：适合全文搜索，类似于搜索引擎的倒排索引\n- GiST 索引：用途：模糊查询、相似度查询、地理空间（PostGIS）、全文搜索\n- BRIN 索引：适合大表（TB 级别）+ 按时间或顺序存储的数据（如日志）\n- SP-GiST 索引：用于非平衡数据结构（四叉树、k-d 树）。常用于多维数据（空间、地理）\n\n### 索引特性\n\n```sql\n# 部分索引\nCREATE INDEX idx_active_user ON users(last_login) WHERE active = true;\n# 表达式索引\nCREATE INDEX idx_lower_name ON users(LOWER(name));\n# 覆盖索引，可以再索引里包含额外的列\nCREATE INDEX idx_orders_user ON orders(user_id) INCLUDE(total_amount);\n# 并发创建索引 不阻塞读写\nCREATE INDEX CONCURRENTLY\n```\n\n### 索引扫描方式\n\n- Index Scan：逐条查索引 + 回表。\n- Bitmap Index Scan：先批量查索引，生成位图，再批量回表（适合大范围查询）。\n- Index Only Scan：索引覆盖了查询所需的列，无需回表\n","metadata":"tags: ['数据库']","hasMoreCommit":false,"totalCommits":4,"commitList":[{"date":"2025-11-16T21:30:56+08:00","author":"MY","message":"docs: 统一并精简文档标签","hash":"21362e9d7aeb62e05364cd5e7f3a3c24d7e293c7"},{"date":"2025-11-14T14:35:29+08:00","author":"MY","message":"docs(database): 更新 PostgreSQL 文档格式","hash":"312316c0c3431e80331497316a6611ba46649088"},{"date":"2025-11-06T16:05:53+08:00","author":"MY","message":"docs: 为多个文档添加标签元数据","hash":"dc09327da481a60e0d70a640956def97c47a6e7e"},{"date":"2025-09-28T18:13:46+08:00","author":"MY","message":"docs(database): 添加 PostgreSQL 文档链接","hash":"0bd8c61df15146950f682cdf4a9e4e198c60f2aa"}],"createTime":"2025-09-28T18:13:46+08:00"}