<?xml version="1.0" encoding="utf-8"?>
<ul>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。连接与查看psql -h 127.0.0.1 -U postgres -d shoppsql -h pg.internal -U app_user -d app -f schema.sqlSELECT version();SELECT current_database(), current_user;SHOW data_directory;SHOW shared_buffers;SHOW work_mem;psql\l        数据库列表\c db     切换数据库\dt       表列表\d table  表结构\di       索引\du       用户角色\x        扩展显示\timing   计时\q        退出建表模板CREATE TABLE orders (    id bigint GENERATED ALWAYS AS IDENTITY,    order_no text NOT NULL,    user_id bigint NOT NULL,    status text NOT NULL DEFAULT 'CREATED',    amount numeric(12,2) NOT NULL CHECK (amount &gt;= 0),    created_at timestamptz NOT NULL DEFAULT now(),    CONSTRAINT pk_orders PRIMARY KEY (id),    CONSTRAINT uq_orders_order_no UNIQUE (order_no));常用索引CREATE INDEX idx_orders_user_createdON orders(user_id, created_at DESC);CREATE INDEX idx_orders_openON orders(created_at)WHERE status IN ('CREATED', 'PAID');CREATE INDEX idx_events_payloadON events USING GIN (payload jsonb_path_ops);CREATE INDEX idx_events_created_brinON events USING BRIN (created_at);执行计划EXPLAIN ANALYZESELECT ...;EXPLAIN (ANALYZE, BUFFERS)SELECT ...;重点：扫描节点rows vs actual rowsloopsSort / Hash 内存Shared Hit / ReadRows Removed by FilterSQL 常用INSERT INTO users(name, city)VALUES ('Alice', 'Shanghai')ON CONFLICT (email) DO NOTHING;UPDATE ordersSET status = 'PAID', paid_at = now()WHERE order_no = 'O001'RETURNING id, amount;SELECT DISTINCT ON (user_id)    user_id, id, amount, created_atFROM ordersORDER BY user_id, created_at DESC;事务BEGIN;UPDATE accounts SET balance = balance - 100 WHERE id = 1;UPDATE accounts SET balance = balance + 100 WHERE id = 2;COMMIT;超时：SET statement_timeout = '30s';SET lock_timeout = '3s';SET idle_in_transaction_session_timeout = '10min';活动与锁SELECT pid, state, wait_event_type, wait_event, queryFROM pg_stat_activityWHERE state &lt;&gt; 'idle';SELECT blocked.pid AS blocked_pid,       blocked.query AS blocked_query,       blocking.pid AS blocking_pid,       blocking.query AS blocking_queryFROM pg_stat_activity blockedJOIN pg_locks bl ON bl.pid = blocked.pid AND NOT bl.grantedJOIN pg_locks ul ON ul.granted AND ul.locktype = bl.locktype AND ul.database IS NOT DISTINCT FROM bl.database AND ul.relation IS NOT DISTINCT FROM bl.relationJOIN pg_stat_activity blocking ON blocking.pid = ul.pidWHERE blocked.wait_event_type = 'Lock';VACUUM 与统计ANALYZE orders;VACUUM ANALYZE orders;SELECT relname, n_live_tup, n_dead_tup,       last_autovacuum, last_autoanalyzeFROM pg_stat_user_tablesORDER BY n_dead_tup DESC;复制与 WALSELECT client_addr, state, sync_state,       pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS replay_bytesFROM pg_stat_replication;SELECT slot_name, slot_type, active, restart_lsnFROM pg_replication_slots;SELECT archived_count, failed_countFROM pg_stat_archiver;备份恢复pg_dump -Fc -d shop -f shop.dumppg_restore -d shop_restore shop.dumppg_basebackup -h primary -U replicator -D standby -Fp -Xs -P -R常见错误            错误      方向                  duplicate key      唯一约束冲突              deadlock detected      加锁顺序或重试              too many connections      连接池              could not serialize access      串行化重试              canceling statement due to timeout      statement_timeout              terminating connection due to administrator command      管理操作              database is not accepting commands      事务 ID 回卷保护      本章小结本速查手册汇总 PostgreSQL 连接、建表、索引、执行计划、事务、锁、清理、复制和备份常用命令。生产使用时以当前版本文档为准。</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。从会写 SQL 到掌握 PostgreSQL，需要建立内核机制、性能方法、高可用架构和团队治理四层能力。33.1 能力阶梯            阶段      目标                  入门      安装、SQL、类型、约束              进阶      索引、执行计划、事务              高级      MVCC、WAL、VACUUM、复制              专家      高可用、性能治理、备份恢复              大师      架构选型、容量规划、规范建设      33.2 深入内核推荐阅读方向：  Parser / Planner / Executor；  Buffer Manager；  WAL 与检查点；  MVCC 可见性；  Lock Manager；  Autovacuum；  复制协议；  索引实现。官方文档、release notes 和源码测试用例是最好的材料。33.3 建立实验环境常做实验：  构造长事务观察膨胀；  对比索引执行计划；  观察隔离级别现象；  模拟锁等待和死锁；  搭建流复制和逻辑复制；  演练 PITR；  压测连接池；  测试 autovacuum 参数。33.4 性能方法论定义 SLO采集基线定位 SQL分析计划修复索引统计验证并发记录参数和收益沉淀规范避免碎片化调参，每次变更都要可回滚、可观测。33.5 团队治理必备规范：  schema migration 流程；  SQL review；  索引评审；  慢查询治理；  备份恢复演练；  权限审批；  容量规划；  变更窗口；  值班 runbook。33.6 持续学习关注：  官方文档；  邮件列表；  release notes；  社区会议；  扩展生态；  云数据库特性；  内核源码。新特性要验证与现有备份、扩展、HA 方案的兼容性。本章小结大师之路是把数据库机制、业务负载和团队流程打通。持续实验、记录、复盘，并沉淀规范，才能让 PostgreSQL 在生产中长期稳定运行。思考题  你最需要补齐哪一层能力？  如何设计自己的内核实验？  团队最缺哪个数据库规范？  如何量化一次优化收益？  下一次演练选什么场景？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。本章整理 PostgreSQL 高频面试题，并给出体现工程判断的回答框架。32.1 MVCC 是什么？回答要点：  每行有 xmin / xmax；  事务根据快照判断可见性；  读写互不阻塞；  UPDATE 生成新版本；  旧版本由 VACUUM 清理。加分项：解释长事务和复制槽如何阻止清理，导致表膨胀。32.2 VACUUM 的作用？回收死元组空间、冻结事务 ID、维护可见性映射、支持 Index Only Scan。普通 VACUUM 不归还磁盘空间；VACUUM FULL 会重写表并加重锁。32.3 为什么索引不生效？排查顺序：  索引类型与操作符是否匹配；  列是否被函数包装；  类型是否一致；  是否违背复合索引左前缀；  统计信息是否过期；  返回比例是否很高；  优化器成本判断。32.4 EXPLAIN ANALYZE 看什么？看 Seq / Index / Bitmap Scan、rows 与 actual rows、loops、Sort / Hash 内存、Buffers、Rows Removed。不要只看总耗时。32.5 隔离级别如何选择？默认 Read Committed 适合多数业务；报表一致性用 Repeatable Read；强一致并发约束用 Serializable，并接受重试成本。32.6 WAL 的作用？崩溃恢复、PITR、物理复制、归档。提交策略 synchronous_commit 决定性能与丢数据窗口。32.7 复制槽为什么危险？它会保留未被消费的 WAL。下游失效后可能把主库磁盘占满，并引发数据库保护性停止写入。必须监控 restart_lsn 和保留量。32.8 逻辑复制与物理复制区别？物理复制传 WAL 数据块变化，一致性强；逻辑复制解析逻辑变更，可按表选择和跨版本，但不复制 DDL、序列状态，且可能有冲突。32.9 如何处理表膨胀？先治因：长事务、autovacuum、复制槽、高频无效更新。严重时评估 pg_repack 或窗口内重建，VACUUM FULL 是最后选择。32.10 B-Tree 和 GIN 如何选择？B-Tree 适合等值范围排序；GIN 适合 JSONB 包含、数组、全文。GIN 查询强但写入维护成本高。32.11 JSONB 如何优化？包含查询建 GIN；高频字段提取生成列；需要范围排序时用表达式或普通列；控制文档大小和更新频率。32.12 为什么需要连接池？PostgreSQL 每连接一个进程，过多连接带来内存、调度和锁竞争。连接池复用进程，控制并发，但要理解事务级模式限制。32.13 分区表注意事项？分区键要匹配高频过滤；唯一约束包含分区键；提前创建分区；控制分区数量；查询验证分区剪枝。32.14 如何做性能治理？用 pg_stat_statements 建立慢 SQL 台账，用 EXPLAIN ANALYZE 定位，用索引和统计优化，用连接池和超时保护，最后压测和固化规范。32.15 生产高可用方案？主从复制 + 仲裁 + 自动切换 + 连接路由 + 监控告警 + 演练。明确 RPO/RTO，避免脑裂，旧主恢复前不得继续写。本章小结PostgreSQL 面试重点集中在 MVCC、VACUUM、索引、执行计划、WAL、复制、连接池和故障排查。回答时先讲机制，再讲风险和验证方式。思考题  如何完整解释一次 UPDATE 的 MVCC 过程？  表膨胀的治理路径是什么？  逻辑复制升级要注意什么？  JSONB 查询慢如何系统排查？  如何设计数据库的 RPO/RTO？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。本章设计一个电商交易与订单分析系统，覆盖表结构、索引、事务、分区、备份和监控。31.1 业务流程用户下单 -&gt; 创建订单 -&gt; 支付 -&gt; 扣库存 -&gt; 发货 -&gt; 售后核心要求：  订单号唯一；  金额精确；  状态流转合法；  库存不能超卖；  支付必须幂等；  历史订单可归档。31.2 表设计CREATE TYPE order_status AS ENUM (    'CREATED', 'PAID', 'SHIPPED', 'CANCELLED', 'REFUNDED');CREATE TABLE orders (    id bigint GENERATED ALWAYS AS IDENTITY,    order_no text NOT NULL,    user_id bigint NOT NULL,    status order_status NOT NULL DEFAULT 'CREATED',    amount numeric(12,2) NOT NULL CHECK (amount &gt;= 0),    paid_at timestamptz,    created_at timestamptz NOT NULL DEFAULT now(),    created_date date GENERATED ALWAYS AS (created_at::date) STORED,    CONSTRAINT pk_orders PRIMARY KEY (created_date, id),    CONSTRAINT uq_orders_order_no UNIQUE (order_no))PARTITION BY RANGE (created_date);31.3 索引CREATE INDEX idx_orders_user_createdON orders(user_id, created_at DESC);CREATE INDEX idx_orders_status_createdON orders(status, created_at)WHERE status IN ('CREATED', 'PAID');按订单号查询必须确保唯一索引可用；按用户查询使用复合索引；后台任务使用部分索引。31.4 状态流转UPDATE ordersSET status = 'PAID', paid_at = now()WHERE order_no = 'O202608250001'  AND status = 'CREATED'RETURNING id, amount;如果返回 0 行，说明状态已变化或订单不存在，不能直接重试扣款。31.5 库存扣减UPDATE sku_stockSET available = available - 1WHERE sku_id = 1001  AND available &gt;= 1RETURNING version;高并发热点 SKU 可考虑排队、预占、批量合并或应用层限流。31.6 支付幂等INSERT INTO payment_requests(payment_no, order_no, amount)VALUES ('P001', 'O001', 99.00)ON CONFLICT (payment_no)DO UPDATE SET request_time = excluded.request_timeWHERE payment_requests.status = 'INIT'RETURNING id, status;支付回调必须以支付平台流水为准，并记录请求日志。31.7 分区维护CREATE TABLE orders_2026_09 PARTITION OF ordersFOR VALUES FROM ('2026-09-01') TO ('2026-10-01');定时任务提前创建未来分区，并归档过期分区。31.8 监控            指标      目标                  下单成功率      核心告警              死锁      立即告警              慢 SQL      持续治理              复制延迟      RPO 相关              死元组      autovacuum 健康度              备份成功率      每日检查      31.9 上线清单1. 约束和索引 review2. 事务边界 review3. 压测4. HA 演练5. 备份恢复演练6. 权限最小化7. 监控告警8. 回滚预案本章小结电商系统要把唯一性、状态流、幂等、库存一致性和归档策略放在模型层解决。PostgreSQL 的约束、事务、分区和索引能力能提供较强兜底，但仍需业务重试和监控闭环。思考题  订单表为什么按时间分区？  部分索引适合哪些后台查询？  状态更新为什么要带状态条件？  支付幂等如何设计？  热点库存有哪些优化方向？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。PostgreSQL 和 MySQL 都是成熟开源关系型数据库。选型要看事务模型、查询复杂度、生态、团队运维能力和成本，而不是只看单项 benchmark。30.1 架构差异            维度      PostgreSQL      MySQL                  进程模型      多进程      多线程              存储引擎      统一存储层      Server + 存储引擎              主流引擎      内核存储      InnoDB              扩展方式      扩展生态强      插件生态与云分支              连接模型      进程连接，依赖连接池      线程连接      30.2 SQL 能力PostgreSQL 优势：  窗口函数与 CTE 能力成熟；  GROUPING SETS / ROLLUP / CUBE；  DISTINCT ON；  LATERAL；  丰富类型和操作符；  JSONB 与 GiST/GIN；  自定义类型和扩展。MySQL 优势：  生态广泛；  复制和运维经验普及；  常见 Web 业务上手快；  云服务支持成熟；  简单高并发 OLTP 场景表现稳定。30.3 MVCC 差异            维度      PostgreSQL      MySQL InnoDB                  旧版本位置      表内死元组      Undo Log              清理机制      VACUUM      purge              长事务影响      表膨胀、事务 ID 风险      undo 膨胀              典型运维点      autovacuum、表年龄      purge、history list      30.4 复制差异PostgreSQL：  物理流复制；  逻辑复制；  发布订阅按库表选择；  支持跨版本升级。MySQL：  binlog 复制成熟；  GTID 和半同步广泛使用；  组复制与集群方案多；  主从切换生态丰富。30.5 JSON 对比PostgreSQL JSONB：SELECT * FROM eventsWHERE payload @&gt; '{"type":"pay"}';支持二进制结构、GIN、生成列和丰富操作符。MySQL JSON：SELECT * FROM eventsWHERE JSON_EXTRACT(payload, '$.type') = 'pay';也支持函数索引和生成列，但语义和索引行为与 PostgreSQL 不同。30.6 分区与 DDL两者都支持分区，但分区键约束、分区管理和 DDL 锁行为不同。PostgreSQL 分区表唯一键必须包含分区键；MySQL 分区也有主键约束要求。迁移时必须重新验证 schema。30.7 迁移注意1. 类型映射2. SQL 方言3. 序列和自增4. 空字符串与 NULL5. 时区处理6. 大小写敏感7. 复制拓扑8. 备份窗口9. 应用驱动10. 回滚方案本章小结PostgreSQL 更适合复杂查询、丰富类型、扩展能力要求高的系统；MySQL 在高并发简单 OLTP 和既有生态中依然很强。选型应以团队经验和业务负载为准。思考题  两者的 MVCC 清理机制有什么差异？  JSONB 和 MySQL JSON 各有什么特点？  哪些 SQL 迁移风险最高？  连接模型如何影响部署？  如何设计公平对比测试？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。备份是最后的安全网。生产方案必须能恢复，并且恢复能力被演练验证过。29.1 备份类型            类型      说明                  逻辑备份      SQL 或归档文本              物理备份      数据目录和 WAL              基础备份 + WAL      支持 PITR              快照      存储层一致性快照      29.2 pg_dumppg_dump -h localhost -U postgres -Fc -d shop -f shop.dump恢复：createdb shop_restorepg_restore -d shop_restore shop.dump适合小库和单库迁移，大库恢复较慢。29.3 pg_dumpallpg_dumpall -f cluster.sql包含角色、表空间和数据库定义。全局对象需要定期备份。29.4 PITR要素：基础备份连续 WAL 归档恢复目标时间配置和密码等外部资源归档配置：archive_mode = onarchive_command = 'test ! -f /archive/%f &amp;&amp; cp %p /archive/%f'29.5 备份工具常见工具：            工具      用途                  pgBackRest      物理备份和仓库管理              Barman      备份与恢复              wal-g      WAL 和对象存储              pg_dump      逻辑导出      选择标准：  全量 / 增量能力；  对象存储支持；  压缩加密；  并行恢复；  与云环境兼容；  团队熟悉度。29.6 恢复流程1. 确认故障和恢复目标2. 准备新实例3. 恢复基础备份4. 配置 restore_command5. 设置 recovery_target6. 启动恢复7. 校验数据8. 切换应用9. 保留故障现场29.7 校验SELECT count(*) FROM orders;SELECT max(created_at) FROM orders;SELECT pg_current_wal_lsn();还应校验：  关键表行数；  业务指纹；  索引完整性；  应用登录；  复制状态。29.8 RPO 与保留策略全量：每周差异 / 增量：每日WAL：连续归档异地：按灾备等级RPO 取决于 WAL 归档连续性，而不是备份文件本身。本章小结逻辑备份适合小规模和迁移，物理备份加 WAL 是生产恢复主流方案。备份必须加密、异地保存、容量监控，并定期做真实恢复演练。思考题  pg_dump 和物理备份有什么区别？  PITR 为什么需要连续 WAL？  RPO 如何确定？  恢复后如何校验？  哪些全局对象容易被漏备份？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。数据库安全包括账号权限、网络认证、数据加密、审计、SQL 注入防护和敏感数据治理。28.1 角色与权限CREATE ROLE app_user LOGIN PASSWORD 'strong-password';CREATE ROLE analyst_ro NOLOGIN;GRANT CONNECT ON DATABASE shop TO app_user, analyst_ro;GRANT USAGE ON SCHEMA app TO app_user, analyst_ro;GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA app TO app_user;GRANT SELECT ON ALL TABLES IN SCHEMA app TO analyst_ro;默认权限：ALTER DEFAULT PRIVILEGES IN SCHEMA appGRANT SELECT ON TABLES TO analyst_ro;28.2 行级安全ALTER TABLE tenants ENABLE ROW LEVEL SECURITY;CREATE POLICY tenant_isolation ON tenantsUSING (tenant_id = current_setting('app.current_tenant')::bigint);设置会话变量：SET app.current_tenant = '1001';RLS 由数据库强制隔离，适合多租户，但测试必须覆盖绕过场景和超级用户行为。28.3 认证与 TLShostssl shop app_user 10.0.0.0/8 scram-sha-256密码加密：SHOW password_encryption;建议：  使用 SCRAM-SHA-256；  密钥集中管理；  定期轮转；  最小网络暴露；  不允许任意公网访问。28.4 SQL 注入错误：SELECT * FROM users WHERE email = '$email';正确使用绑定参数：PREPARE get_user(text) ASSELECT * FROM users WHERE email = $1;应用层使用 ORM 或驱动参数绑定，动态表名和排序字段必须白名单校验。28.5 审计可用扩展或日志方案：log_connections = onlog_disconnections = onlog_statement = 'ddl'log_min_duration_statement = 500核心审计需求：  登录成功失败；  权限变更；  DDL；  敏感表访问；  管理员操作；  连接来源。28.6 敏感数据CREATE EXTENSION IF NOT EXISTS pgcrypto;SELECT crypt('password', gen_salt('bf', 12));原则：  密码只存哈希；  手机号和证件号脱敏展示；  需要检索的敏感值可存 HMAC；  密钥不进数据库连接串；  备份也要加密。28.7 Schema 安全REVOKE CREATE ON SCHEMA public FROM PUBLIC;避免普通用户在 public 创建恶意对象，并控制 search_path：ALTER ROLE app_user SET search_path = app, public;本章小结安全治理要坚持最小权限、强认证、网络收敛、参数绑定和审计留痕。多租户可使用 RLS，敏感数据要考虑展示、存储、检索和备份四个层面。思考题  为什么应用账号要与管理员分离？  RLS 的适用条件是什么？  如何防止 SQL 注入？  敏感字段如何安全检索？  审计日志至少保留哪些事件？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。PostgreSQL 监控要覆盖可用性、连接、事务、锁、复制、WAL、VACUUM、缓存和 SQL 成本。27.1 核心指标            分类      指标                  服务      存活、可读写、连接数              连接      active、idle、idle in transaction              事务      xact_commit、xact_rollback              锁      等待数、最长等待              复制      LSN 延迟、状态              WAL      生成量、归档失败              清理      死元组、表年龄              缓存      命中率、临时文件              SQL      慢查询、错误率      27.2 连接与活动SELECT state, wait_event_type, count(*)FROM pg_stat_activityGROUP BY state, wait_event_type;长事务：SELECT pid, now() - xact_start AS duration, state, queryFROM pg_stat_activityWHERE xact_start IS NOT NULLORDER BY xact_start;27.3 数据库统计SELECT    datname,    xact_commit,    xact_rollback,    blks_read,    blks_hit,    temp_files,    pg_size_pretty(temp_bytes) AS temp_size,    deadlocksFROM pg_stat_database;27.4 表与索引SELECT    relname,    seq_scan,    idx_scan,    n_live_tup,    n_dead_tup,    last_autovacuum,    last_autoanalyzeFROM pg_stat_user_tablesORDER BY n_dead_tup DESC;27.5 复制与 WALSELECT client_addr, state, sync_state,       pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS replay_bytesFROM pg_stat_replication;SELECT archived_count, failed_countFROM pg_stat_archiver;27.6 常见故障            现象      排查                  连接耗尽      pg_stat_activity、连接池              CPU 高      pg_stat_statements、执行计划              IO 高      buffers、临时文件、检查点              锁等待      pg_locks、等待事件              磁盘涨      WAL、复制槽、膨胀              备库延迟      网络、回放、大事务              SQL 变慢      统计、索引、数据增长      27.7 连接耗尽查看：SHOW max_connections;SELECT count(*), state FROM pg_stat_activity GROUP BY state;处理：  终止异常连接；  排查泄漏；  调整池大小；  治理长事务；  必要时临时提升 max_connections 并重启。27.8 磁盘增长顺序：  检查数据目录大文件；  检查 WAL 目录；  检查复制槽；  检查归档失败；  检查表膨胀；  检查日志文件；  扩容或清理。27.9 日志配置logging_collector = onlog_min_duration_statement = 500log_lock_waits = onlog_checkpoints = onlog_autovacuum_min_duration = 0日志要集中采集，并保留足够回溯时间。本章小结监控的目的是发现趋势和定位因果。PostgreSQL 统计视图非常丰富，应结合系统指标、日志和 SQL 执行计划建立完整观测面。思考题  idle in transaction 为什么危险？  如何发现表膨胀和清理滞后？  复制延迟应监控哪个位置？  磁盘增长如何快速定位？  生产日志应开启哪些项？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。性能调优是系统化工程：先定义目标和基线，再定位瓶颈，最后通过压测验证配置和 SQL 改动。26.1 调优流程确定 SLO  -&gt; 建立基线  -&gt; 采集资源和 SQL  -&gt; 定位瓶颈  -&gt; 一次改一个变量  -&gt; 压测  -&gt; 固化配置和规范26.2 参数分层            层      参数                  连接      max_connections、superuser_reserved_connections              内存      shared_buffers、work_mem、maintenance_work_mem              WAL      max_wal_size、checkpoint_timeout、synchronous_commit              查询      default_statistics_target、random_page_cost              自动清理      autovacuum_*              日志      log_min_duration_statement      26.3 连接池连接不是越多越好。应用应使用 PgBouncer 或应用内连接池。max_connections = 200PgBouncer pool = 50-100，按业务拆池事务级或会话级模式按语义选择事务级 pooling 不支持依赖连接状态的特性，如 prepared statement 的部分行为、SET SESSION、 advisory lock 语义。26.4 内存参数ALTER SYSTEM SET shared_buffers = '4GB';ALTER SYSTEM SET effective_cache_size = '12GB';ALTER SYSTEM SET work_mem = '32MB';ALTER SYSTEM SET maintenance_work_mem = '512MB';SELECT pg_reload_conf();ALTER SYSTEM 写入自动配置文件，仍要纳入版本管理。26.5 IO 调优关注：  数据盘类型；  WAL 与数据是否分离；  full_page_writes；  检查点分布；  bgwriter；  表和索引布局；  临时文件位置。查看 IO 等待：SELECT wait_event_type, wait_event, count(*)FROM pg_stat_activityWHERE state &lt;&gt; 'idle'GROUP BY 1, 2;26.6 SQL 调优优先级：  修复缺索引；  修复统计信息；  减少扫描列和行；  优化排序聚合；  控制返回结果；  消除 N+1；  使用绑定参数；  缓存稳定结果。26.7 批量作业大作业分批：WITH batch AS (    SELECT id    FROM tasks    WHERE status = 'NEW'    ORDER BY id    LIMIT 1000    FOR UPDATE SKIP LOCKED)UPDATE tasks tSET status = 'RUNNING'FROM batchWHERE t.id = batch.idRETURNING t.id;26.8 基准测试pgbench -i -s 10 testdbpgbench -c 20 -j 4 -T 120 testdb测试前记录：  数据规模；  参数；  硬件；  查询集；  并发模型；  系统指标。本章小结性能调优不能脱离负载模型。先治理 SQL 和索引，再谨慎调整内存、WAL 和 autovacuum，并用连接池和压测验证并发能力。思考题  为什么 max_connections 不是越大越好？  事务级连接池有什么限制？  work_mem 调大的风险是什么？  批量任务为什么分批提交？  如何建立可信压测基线？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。本章用两个业务场景把类型、索引和扩展组合起来：事件 JSONB 分析和门店地理位置服务。25.1 JSONB 事件表CREATE TABLE app_events (    id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,    event_time timestamptz NOT NULL DEFAULT now(),    event_name text NOT NULL,    user_id bigint NOT NULL,    payload jsonb NOT NULL);写入：INSERT INTO app_events(event_time, event_name, user_id, payload)VALUES (    now(),    'pay',    1001,    '{"city":"Shanghai","platform":"App","amount":99.50}'::jsonb);25.2 GIN 索引CREATE INDEX idx_events_payloadON app_events USING GIN (payload);高效查询：SELECT *FROM app_eventsWHERE payload @&gt; '{"event_source":"app"}';文本取值后比较会弱化索引：SELECT *FROM app_eventsWHERE payload -&gt;&gt; 'city' = 'Shanghai';可做表达式索引：CREATE INDEX idx_events_cityON app_events((payload -&gt;&gt; 'city'));25.3 提升高频字段ALTER TABLE app_eventsADD COLUMN city textGENERATED ALWAYS AS (payload -&gt;&gt; 'city') STORED;CREATE INDEX idx_events_city_timeON app_events(city, event_time);查询：SELECT city, count(*)FROM app_eventsWHERE event_time &gt;= now() - interval '1 day'GROUP BY city;25.4 JSONB 更新UPDATE app_eventsSET payload = payload || '{"status":"done"}'::jsonbWHERE id = 1;删除键：UPDATE app_eventsSET payload = payload - 'trace_id'WHERE id = 1;高频更新 JSONB 会产生死元组，应关注 VACUUM。25.5 GIS 数据CREATE EXTENSION IF NOT EXISTS postgis;CREATE TABLE shops (    id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,    name text NOT NULL,    city text NOT NULL,    location geography(Point, 4326) NOT NULL,    service_range int NOT NULL DEFAULT 3000);插入：INSERT INTO shops(name, city, location)VALUES (    'Cloud Coffee',    'Shanghai',    ST_SetSRID(ST_MakePoint(121.47, 31.23), 4326)::geography);25.6 空间索引CREATE INDEX idx_shops_locationON shops USING GIST (location);附近门店：SELECT    id,    name,    ST_Distance(location, ST_SetSRID(ST_MakePoint(121.47, 31.23), 4326)::geography) AS distanceFROM shopsWHERE ST_DWithin(    location,    ST_SetSRID(ST_MakePoint(121.47, 31.23), 4326)::geography,    1000)ORDER BY distanceLIMIT 20;25.7 地理边界SELECT s.nameFROM shops sJOIN city_boundary b ON ST_Contains(b.geom, s.location::geometry);边界数据要确认坐标系、精度和来源授权。25.8 性能要点            场景      优化                  JSON 包含查询      GIN              JSON 高频字段      生成列 + B-Tree              附近查询      GiST + 范围过滤              边界包含      边界简化、空间索引              大范围统计      预聚合或分区      本章小结JSONB 适合灵活事件结构，高频字段应提升为普通列或生成列；GIS 应使用 geography 存 WGS84 点位，并用 GiST 支撑距离和包含查询。思考题  @&gt; 和 -&gt;&gt; 查询的索引行为有什么不同？  生成列什么时候适合 JSONB？  geography 和 geometry 有什么差异？  ST_DWithin 为什么要配合索引？  JSONB 高频更新会带来什么运维问题？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。PostgreSQL 的扩展能力是它成为“数据库平台”的重要原因。扩展可以增加索引、类型、函数、外部表和调度能力。24.1 扩展机制CREATE EXTENSION IF NOT EXISTS pg_stat_statements;查看：SELECT extname, extversionFROM pg_extension;可用扩展：SELECT name, default_version, installed_versionFROM pg_available_extensionsWHERE name IN ('postgis', 'pgvector', 'timescaledb');24.2 常用扩展            扩展      用途                  pg_stat_statements      SQL 性能统计              postgis      GIS              pgvector      向量检索              timescaledb      时序              pg_trgm      相似度和模糊匹配              pgcrypto      加密函数              hstore      键值类型              postgres_fdw      PostgreSQL 外部表              pg_partman      分区管理              pg_repack      在线重整      24.3 pg_stat_statementsCREATE EXTENSION IF NOT EXISTS pg_stat_statements;查询：SELECT    calls,    round(total_exec_time::numeric, 2) AS total_ms,    round(mean_exec_time::numeric, 2) AS mean_ms,    rows,    shared_blks_read,    queryFROM pg_stat_statementsORDER BY total_exec_time DESCLIMIT 20;重置：SELECT pg_stat_statements_reset();24.4 pg_trgmCREATE EXTENSION IF NOT EXISTS pg_trgm;CREATE INDEX idx_products_name_trgmON products USING GIN (name gin_trgm_ops);相似查询：SELECT name, similarity(name, 'postgres') AS scoreFROM productsWHERE name % 'postgres'ORDER BY score DESC;适合模糊搜索，复杂中文语义仍需专门分词或搜索引擎。24.5 FDWCREATE EXTENSION IF NOT EXISTS postgres_fdw;CREATE SERVER remote_pgFOREIGN DATA WRAPPER postgres_fdwOPTIONS (host 'remote.internal', dbname 'shop', port '5432');映射用户和表：CREATE USER MAPPING FOR app_userSERVER remote_pgOPTIONS (user 'reader', password 'strong-password');IMPORT FOREIGN SCHEMA publicFROM SERVER remote_pgINTO remote;FDW 下推能力与版本和 SQL 形态相关，必须检查执行计划。24.6 pgvectorCREATE EXTENSION IF NOT EXISTS vector;CREATE TABLE docs (    id bigint PRIMARY KEY,    content text,    embedding vector(1536));索引：CREATE INDEX idx_docs_embeddingON docs USING hnsw (embedding vector_cosine_ops);查询：SELECT id, contentFROM docsORDER BY embedding &lt;=&gt; '[0.1, 0.2, ...]'::vectorLIMIT 10;24.7 扩展治理  只安装可信扩展；  固定扩展版本；  评估备份兼容性；  确认升级路径；  观察内核崩溃风险；  记录扩展依赖；  云环境确认支持情况。本章小结扩展生态让 PostgreSQL 覆盖 GIS、向量、时序、模糊检索和数据集成等场景。但扩展会改变运维边界，引入前要验证版本、性能、稳定性和备份恢复。思考题  pg_stat_statements 能解决什么问题？  pg_trgm 适合哪些模糊查询？  FDW 查询为什么必须看执行计划？  pgvector 的索引方式有什么取舍？  扩展升级要注意什么？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。高可用不只是“有一个备库”，还包括故障检测、切换决策、数据一致性、连接路由、演练和回切。23.1 常见拓扑一主一备：App -&gt; LB / HA entry        |     Primary        |     Standby一主两备：Primary -&gt; Sync Standby        -&gt; Async Standby同城双中心：DC A: Primary + WitnessDC B: Sync/Async Standby异地灾备：同城主集群 -&gt; 异地异步备库23.2 RPO 与 RTO            指标      含义                  RPO      最多可丢多少数据              RTO      最多可接受多久不可用      同步复制降低 RPO，但增加延迟；异步复制性能更好，但故障可能丢最近 WAL。23.3 故障检测应检测：  进程存活；  端口健康；  只读或读写状态；  复制延迟；  WAL 保留；  磁盘空间；  事务成功率。不要只 ping 端口。备库端口可用不代表可提升。23.4 切换决策切换前确认：1. 主库是否真故障2. 是否发生脑裂3. 备库 LSN 是否追平4. 复制模式是什么5. 应用是否已停止写旧主6. 新主是否可读写防脑裂方式包括仲裁节点、STONITH、网络隔离策略和分布式锁，具体取决于 HA 方案。23.5 连接路由应用应通过稳定入口访问：            方式      特点                  VIP      网络层切换，配置简单              负载均衡      探活和读写分离              DNS      传播延迟              代理      灵活，但新增组件      读写分离时，写请求必须到主库；强一致读也应到主库或等待回放。23.6 HA 工具常见方案：            工具      特点                  Patroni + etcd      常用分布式 HA              repmgr      复制管理和切换              pg_auto_failover      自动故障转移              云托管服务      平台托管 HA      选择标准：  故障检测策略；  一致性保证；  恢复流程；  运维复杂度；  与监控集成；  团队掌握程度。23.7 备库冲突查询可能取消回放：ERROR: canceling statement due to conflict with recovery相关参数：SHOW hot_standby_feedback;SHOW max_standby_streaming_delay;hot_standby_feedback 可减少取消，但会让主库保留旧版本，增加膨胀风险。23.8 演练定期演练：  kill 主库进程；  网络分区；  同步备库宕机；  复制断开；  WAL 归档失败；  应用连接切换；  旧主重建；  回切。本章小结高可用架构要明确 RPO/RTO、复制模式、仲裁机制、路由策略和故障恢复。自动切换必须防脑裂，且所有方案都要经过真实演练。思考题  同步复制如何影响写入延迟？  RPO=0 是否等价于不会宕机？  如何防止脑裂？  备库查询冲突如何处理？  HA 演练应覆盖哪些场景？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。逻辑复制发布逻辑变更，而不是物理页变化。它支持按库表选择、跨版本升级和异构消费，但语义限制更多。22.1 架构Publisher  logical decoding -&gt; publication          |Replication protocol          |Subscriber  apply worker -&gt; subscription22.2 发布发布端配置：wal_level = logicalmax_replication_slots = 10max_wal_senders = 10创建发布：CREATE PUBLICATION sales_pubFOR TABLE orders, order_items;只发布部分操作：ALTER PUBLICATION sales_pub SET (publish = 'insert, update');22.3 订阅CREATE SUBSCRIPTION sales_subCONNECTION 'host=publisher.internal port=5432 dbname=shop user=replicator password=strong-password sslmode=require'PUBLICATION sales_pub;初始数据会按发布表复制，之后应用增量逻辑变更。22.4 冲突处理订阅端遇到主键冲突或约束错误时，apply worker 会停止：ERROR: duplicate key value violates unique constraint处理方式：  查看订阅端错误；  删除或修正冲突行；  恢复 apply；  记录根因；  必要时重建订阅。22.5 支持与限制常见限制：            操作      逻辑复制行为                  INSERT      支持              UPDATE      需要副本标识              DELETE      需要副本标识              DDL      不会自动发布              序列      不自动同步状态              大对象      版本和支持有限              TRUNCATE      取决于发布配置和版本      表必须有主键或唯一索引作为副本标识：ALTER TABLE orders REPLICA IDENTITY USING INDEX uq_orders_order_no;22.6 监控发布端：SELECT * FROM pg_stat_replication;SELECT slot_name, plugin, database, activeFROM pg_replication_slots;订阅端：SELECT * FROM pg_stat_subscription;关注：  apply 延迟；  复制槽保留 WAL；  冲突错误；  worker 状态；  表结构一致。22.7 跨版本升级旧版本 Publisher -&gt; 新版本 Subscriber流程：  新库建表和索引；  建发布和订阅；  等初始同步；  追平增量；  短停写；  校验数据；  切换应用；  同步序列；  删除订阅。22.8 多写入与分区订阅逻辑复制天然不防止多写入冲突。双向复制需要冲突解决策略和业务分区，不能只靠数据库默认行为。本章小结逻辑复制适合选择性表复制、跨版本升级和下游同步。它不复制 DDL 和序列状态，且依赖副本标识。生产必须监控复制槽、apply 延迟和冲突。思考题  物理复制和逻辑复制有什么区别？  REPLICA IDENTITY 有什么作用？  订阅端冲突如何处理？  为什么序列要单独处理？  逻辑复制升级的切换步骤是什么？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。物理复制传输 WAL，使备库拥有与主库一致的物理数据块。它是高可用、读写分离和灾备的基础。21.1 架构Primary  walsender -&gt; WAL stream                  |Standby  walreceiver -&gt; write / flush / replay备库回放 WAL，达到物理一致。21.2 基础配置主库：listen_addresses = '*'wal_level = replicamax_wal_senders = 10创建复制用户：CREATE ROLE replicator WITH REPLICATION PASSWORD 'strong-password';pg_hba.conf：hostssl replication replicator 10.0.0.0/8 scram-sha-25621.3 初始化备库使用 pg_basebackup：pg_basebackup \  -h primary.internal \  -U replicator \  -D /var/lib/postgresql/16/standby \  -Fp -Xs -P -R-R 会写入 standby.signal 和连接信息。21.4 复制状态SELECT    pid,    application_name,    client_addr,    state,    sync_state,    sent_lsn,    write_lsn,    flush_lsn,    replay_lsn,    write_lag,    flush_lag,    replay_lagFROM pg_stat_replication;备库：SELECT status, sender_host, received_lsn, latest_end_lsnFROM pg_stat_wal_receiver;21.5 同步复制synchronous_standby_names = 'FIRST 1 (standby01)'模式：            synchronous_commit      等待点                  remote_write      备库写入              remote_flush      备库刷盘              remote_apply      备库回放      同步复制降低失败丢数据风险，但增加提交延迟。21.6 延迟复制recovery_min_apply_delay = 5min适合防误删和保留恢复窗口，但备库持续延迟，不适合常备读一致性查询。21.7 复制槽SELECT pg_create_physical_replication_slot('standby01');复制槽保留 WAL，防止备库需要的历史日志被删除。必须监控：SELECT slot_name, active, restart_lsn,       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retainedFROM pg_replication_slots;21.8 级联复制Primary -&gt; Standby01 -&gt; Standby02下游备库从 Standby01 接收 WAL，减少主库 walsender 压力。注意整体拓扑和延迟监控。21.9 故障切换切换要做：  确认主库状态；  停止应用写入；  等待或评估 LSN 差异；  提升备库；  修改连接入口；  重建旧主库；  验证数据和复制。提升备库：SELECT pg_promote();本章小结物理复制基于 WAL，数据一致性强，适合 HA 和灾备。生产必须监控 LSN 延迟、复制槽保留和切换流程。同步策略要在性能与可靠性之间明确取舍。思考题  流复制的延迟如何计算？  同步和异步复制的差异是什么？  复制槽有哪些风险？  延迟复制适合什么场景？  故障切换前应确认哪些信息？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。PostgreSQL 是多进程架构。理解后台进程职责，有助于判断 IO 抖动、复制延迟、autovacuum 行为和恢复过程。20.1 进程列表ps -ef | grep postgres常见进程：            进程      职责                  postmaster      主进程，管理子进程              backend      服务客户端连接              checkpointer      执行检查点              background writer      后台刷脏页              WAL writer      刷 WAL 缓冲              autovacuum launcher      调度 autovacuum              autovacuum worker      执行清理              walreceiver      流复制接收 WAL              walsender      发送 WAL              logical replication launcher      逻辑复制调度      20.2 连接进程每个连接对应一个 backend 进程：Client A -&gt; postgres: app db app_userClient B -&gt; postgres: app db app_user因此连接数过高会带来：  进程调度成本；  内存开销；  上下文切换；  锁竞争；  管理复杂。应用应使用连接池。20.3 autovacuum 进程SHOW autovacuum_max_workers;SHOW autovacuum_naptime;worker 数量不是越大越好，还要考虑 IO 限速、磁盘能力和表数量。查看正在运行：SELECT    pid,    datname,    relid::regclass AS table_name,    phase,    heap_blks_total,    heap_blks_scannedFROM pg_stat_progress_vacuum;20.4 WAL 进程WAL writer 周期性刷新 WAL 缓冲。walsender 和 walreceiver 用于物理流复制。查看复制：SELECT    pid,    usename,    application_name,    client_addr,    state,    sent_lsn,    write_lsn,    flush_lsn,    replay_lsnFROM pg_stat_replication;20.5 检查点进程查看检查点：SELECT checkpoints_timed, checkpoints_reqFROM pg_stat_bgwriter;checkpoints_req 过高说明请求检查点过多，可能与 WAL 压力、批量写入或配置有关。20.6 工作进程并行查询 worker：SHOW max_worker_processes;SHOW max_parallel_workers;SHOW max_parallel_workers_per_gather;并行 worker 消耗 CPU、内存和 IO，需要与 max_connections、备份任务和扩展任务统一规划。20.7 进程异常排查常见日志：server process was terminated by signal 9terminating connection due to administrator commandcanceling statement due to statement timeout处理顺序：  查看数据库日志；  对应时间线；  检查 OOM 和系统日志；  查看连接和内存参数；  复现并保留计划；  补监控和资源隔离。本章小结后台进程承担刷盘、检查点、清理、复制和并行执行。生产运维要能通过进程、日志和统计视图判断是业务连接、autovacuum、WAL 还是检查点在消耗资源。思考题  为什么 PostgreSQL 需要连接池？  autovacuum worker 增加就一定更快吗？  checkpoints_req 高说明什么？  walsender 和 walreceiver 分别在哪里运行？  进程被 signal 9 终止应如何排查？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。Buffer Manager 管理 shared_buffers 中的数据页缓存，决定哪些页留在内存、哪些页被写出以及如何减少磁盘 IO。19.1 页面结构PostgreSQL 数据文件按固定页组织，通常为 8KB：Table file  Page 0  Page 1  Page 2每个页包含页头、行指针、空闲空间和行数据。表和索引的页结构不同，但都由 Buffer Manager 统一缓存。19.2 shared_buffersSHOW shared_buffers;SHOW effective_cache_size;shared_buffers 是 PostgreSQL 自己申请的共享缓存；effective_cache_size 告诉优化器操作系统缓存大概有多少，不实际分配内存。经验起点：专用数据库服务器：内存的 25% 左右再结合业务、磁盘和并发压测调整19.3 缓冲命中查看数据库级：SELECT    blks_read,    blks_hit,    round(blks_hit::numeric /          greatest(blks_hit + blks_read, 1), 4) AS hit_ratioFROM pg_stat_databaseWHERE datname = current_database();命中率低不一定是 shared_buffers 不足，也可能是：  冷数据或随机访问；  扫描量过大；  索引缺失；  内存被并发竞争；  操作系统缓存不足。19.4 脏页刷写相关进程：            进程      职责                  backend      可能直接刷脏页              background writer      提前刷可淘汰脏页              checkpointer      检查点统一刷盘      查看：SELECT * FROM pg_stat_bgwriter;19.5 检查点与 IO 抖动检查点把大量脏页刷盘，可能引发 IO 高峰：checkpoint_completion_targetcheckpoint_timeoutmax_wal_size适当增加 max_wal_size、拉长完成目标可以分散写入，但会延长恢复窗口。19.6 work_mem 与中间结果SHOW work_mem;用于排序、哈希、聚合等操作。每个查询可能有多个节点同时使用该内存，并发越大总内存越高。溢写磁盘示例：Sort Method: external mergeHash Batch:...处理：  会话级调整；  优化 SQL；  减少数据量；  建匹配索引；  控制并发。19.7 预读与顺序扫描顺序扫描会读取连续页，操作系统和存储层可能做预读。大范围分析查询应关注：  shared_blks_read；  临时文件；  IO 等待事件；  索引选择；  分区剪枝。19.8 缓存观测pg_buffercache 扩展：CREATE EXTENSION IF NOT EXISTS pg_buffercache;SELECT    c.relname,    count(*) AS buffer_pages,+    pg_size_pretty(count(*) * 8192) AS sizeFROM pg_buffercache bJOIN pg_class c ON c.oid = b.relfilenodeGROUP BY c.relnameORDER BY buffer_pages DESC;19.9 常见问题            现象      方向                  命中率下降      工作集变大或扫描变多              IO wait 高      缓存、索引、检查点              数据库重启后慢      缓存冷启动              临时文件多      work_mem 或 SQL 中间结果              IO 周期抖动      检查点      本章小结Buffer Manager 决定数据页的内存命中率。shared_buffers、effective_cache_size、work_mem、检查点和后台刷写共同影响 IO 表现。优化要结合 buffers 执行计划和系统指标，而不是只调一个参数。思考题  shared_buffers 和 effective_cache_size 有什么区别？  命中率低说明什么？  work_mem 为什么不能盲目调大？  检查点如何造成 IO 抖动？  如何查看哪些对象占用缓冲？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。锁问题常表现为 SQL 突然变慢或挂起。PostgreSQL 提供锁视图和等待事件，帮助定位“谁阻塞谁”。18.1 锁类型            锁模式      典型操作                  AccessShare      SELECT              RowShare      SELECT FOR UPDATE / SHARE              ShareUpdateExclusive      VACUUM、ANALYZE              Share      CREATE INDEX              ShareRowExclusive      某些约束触发器              Exclusive      某些维护操作              AccessExclusive      DROP、TRUNCATE、VACUUM FULL、ALTER      兼容矩阵可查询官方文档。冲突越强，对并发影响越大。18.2 行锁BEGIN;SELECT * FROM orders WHERE id = 1 FOR UPDATE;UPDATE orders SET status = 'PAID' WHERE id = 1;COMMIT;行锁信息存在行版本中，不放在内存锁表。18.3 死锁事务 A：锁 row1，等待 row2事务 B：锁 row2，等待 row1PostgreSQL 检测后终止一个事务：ERROR: deadlock detected处理：  统一加锁顺序；  缩短事务；  批量操作排序；  应用捕获并重试；  保证幂等。18.4 查看锁等待SELECT    blocked.pid AS blocked_pid,    blocked.query AS blocked_query,    blocking.pid AS blocking_pid,    blocking.query AS blocking_queryFROM pg_stat_activity blockedJOIN pg_locks bl  ON bl.pid = blocked.pid AND NOT bl.grantedJOIN pg_locks ul  ON ul.locktype = bl.locktype AND ul.database IS NOT DISTINCT FROM bl.database AND ul.relation IS NOT DISTINCT FROM bl.relation AND ul.grantedJOIN pg_stat_activity blocking  ON blocking.pid = ul.pidWHERE blocked.wait_event_type = 'Lock';18.5 等待事件SELECT    pid,    wait_event_type,    wait_event,    state,    queryFROM pg_stat_activityWHERE state &lt;&gt; 'idle';常见事件：            类型      含义                  Lock      等待锁              IO      等待磁盘              LWLock      轻量锁或内部共享结构              Client      等待客户端              IPC      等待进程间通信              Timeout      等待超时      18.6 锁超时SET lock_timeout = '3s';SET statement_timeout = '30s';SET idle_in_transaction_session_timeout = '10min';建议：  DDL 设置 lock_timeout；  应用设置 statement_timeout；  治理空闲事务；  超时后要有重试和幂等。18.7 DDL 锁风险ALTER TABLE 可能需要 AccessExclusiveLock，会等待并阻塞后续查询。危险场景：ALTER 等待长查询后续 SELECT 排队连接池耗尽处理：  低峰变更；  终止无关长查询；  设置 lock_timeout；  使用在线迁移工具；  拆分高风险 DDL。18.8 常见锁问题            现象      可能原因                  INSERT 等待      唯一索引冲突              UPDATE 等待      同行事务              DDL 卡住      长查询持锁              查询全部排队      AccessExclusive 等待链              外键等待      被引用行锁              应用连接耗尽      锁等待级联      本章小结锁排查的核心是找出等待链。用 pg_stat_activity、pg_locks 和等待事件判断瓶颈，再通过短事务、排序、超时和低峰 DDL 降低冲突。思考题  SELECT 和 ALTER TABLE 的锁冲突如何发生？  死锁如何预防？  lock_timeout 和 statement_timeout 有什么区别？  空闲事务如何治理？  如何输出阻塞链？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。VACUUM 负责回收死元组空间、冻结事务 ID、维护可见性映射和更新统计相关信息。它是 PostgreSQL 长期健康运行的关键机制。17.1 为什么需要 VACUUMUPDATE / DELETE  -&gt; 产生死元组  -&gt; 旧快照不再需要后  -&gt; VACUUM 标记空间可复用VACUUM 做的事情：  回收死元组；  截断尾部空页；  冻结旧事务；  更新可见性映射；  配合 Index Only Scan；  ANALYZE 更新统计。普通 VACUUM 不归还空间给操作系统，只让页内空间可复用。17.2 autovacuum查看：SHOW autovacuum;SHOW autovacuum_vacuum_threshold;SHOW autovacuum_vacuum_scale_factor;触发估算：dead tuples &gt; threshold + scale_factor * live tuples表级参数：ALTER TABLE orders SET (    autovacuum_vacuum_scale_factor = 0.02,    autovacuum_vacuum_threshold = 1000);大表建议调小 scale factor，否则要积累大量死元组才触发。17.3 手动 VACUUMVACUUM orders;VACUUM ANALYZE orders;VACUUM FULL orders;            命令      锁      作用                  VACUUM      ShareUpdateExclusive      清理死元组              VACUUM FULL      AccessExclusive      重写表并收缩              ANALYZE      ShareUpdateExclusive      更新统计      生产大表通常避免 VACUUM FULL，膨胀严重时使用在线重建方案。17.4 可见性映射可见性映射记录数据页是否所有事务可见。页全可见时：  Index Only Scan 更高效；  VACUUM 可跳过部分页；  查询减少回表判断。频繁更新的表会导致可见性映射失效，索引只扫性能下降。17.5 冻结与防回卷SHOW vacuum_freeze_min_age;SHOW vacuum_freeze_table_age;SHOW autovacuum_freeze_max_age;当表年龄接近上限，PostgreSQL 会执行激进清理，甚至拒绝写入以防止事务 ID 回卷。查看年龄：SELECT    c.oid::regclass AS table_name,    age(c.relfrozenxid) AS xid_ageFROM pg_class cJOIN pg_namespace n ON n.oid = c.relnamespaceWHERE n.nspname NOT IN ('pg_catalog', 'information_schema')ORDER BY xid_age DESC;17.6 阻塞 VACUUM 的因素  长事务；  空闲事务；  慢查询；  逻辑复制槽；  备库 hot_standby_feedback；  prepared transaction；  备份工具保留快照。查看长事务：SELECT pid, state, xact_start, queryFROM pg_stat_activityWHERE xact_start IS NOT NULLORDER BY xact_startLIMIT 10;17.7 表膨胀判断SELECT    relname,    n_live_tup,    n_dead_tup,    round(n_dead_tup::numeric / greatest(n_live_tup, 1), 4) AS dead_ratioFROM pg_stat_user_tablesORDER BY n_dead_tup DESC;统计值只是估算，精确判断需要扩展或物理页分析。持续增长、扫描变慢和索引膨胀是重要信号。17.8 VACUUM 调优SHOW maintenance_work_mem;SHOW autovacuum_max_workers;SHOW autovacuum_vacuum_cost_limit;建议：  大表单独设置阈值；  增加维护内存；  控制 worker 数量与 IO 限速；  低峰执行大批量导入后的清理；  监控 last_autovacuum；  治理长事务。17.9 索引膨胀查看：SELECT    relname,    indexrelname,    idx_scan,    pg_size_pretty(pg_relation_size(indexrelid)) AS sizeFROM pg_stat_user_indexesORDER BY pg_relation_size(indexrelid) DESC;处理：REINDEX INDEX index_name;REINDEX TABLE CONCURRENTLY table_name;本章小结VACUUM 是 MVCC 的清道夫。生产上要为高频更新表调优 autovacuum，持续治理长事务和复制槽，监控死元组、表年龄和 last_autovacuum。膨胀治理的重点是预防，而不是频繁 VACUUM FULL。思考题  VACUUM 和 ANALYZE 的区别是什么？  为什么 VACUUM FULL 生产慎用？  长事务如何影响 VACUUM？  表年龄过高意味着什么？  大表 autovacuum 阈值如何调整？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。WAL（Write-Ahead Logging）先写日志再改数据页，是崩溃恢复、归档备份和流复制的基础。16.1 基本原理事务提交  -&gt; WAL 记录写入 wal_buffers  -&gt; 按提交策略刷入 WAL 文件  -&gt; 后台进程逐步刷脏数据页崩溃后从检查点重放 WAL，把数据库恢复到一致状态。16.2 核心参数SHOW wal_level;SHOW max_wal_size;SHOW min_wal_size;SHOW checkpoint_timeout;SHOW synchronous_commit;SHOW archive_mode;SHOW archive_command;            参数      说明                  wal_level      minimal / replica / logical              max_wal_size      检查点目标上限              checkpoint_timeout      检查点间隔              synchronous_commit      是否等待 WAL 刷盘              archive_mode      是否归档      16.3 检查点检查点把脏页刷到数据文件，并记录 WAL 恢复起点。手动：CHECKPOINT;查看：SELECT * FROM pg_stat_bgwriter;检查点过于频繁会增加 IO；过于稀疏会延长崩溃恢复时间。16.4 synchronous_commit            值      语义                  on      等待本地 WAL 刷盘              off      异步提交，可能丢最近事务              remote_apply      等待备库回放              remote_write      等待备库写入              local      等待本地刷盘，不等备库      金融核心交易通常保持 on 或 remote_apply；日志类可评估异步，但必须明确丢失窗口。16.5 WAL 归档配置：wal_level = replicaarchive_mode = onarchive_command = 'test ! -f /archive/%f &amp;&amp; cp %p /archive/%f'查看：SELECT * FROM pg_stat_archiver;归档目录要有容量监控和保留策略，且应存独立存储。16.6 PITR基础备份 + 归档 WAL 可做时间点恢复。配置恢复目标：restore_command = 'cp /archive/%f %p'recovery_target_time = '2026-08-25 12:00:00+08'恢复前要确认：  备份完整性；  WAL 连续；  恢复目标时间；  是否包含时区；  恢复到新实例验证。16.7 WAL 大小治理查看：SELECT pg_current_wal_lsn();SELECT pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), '0/0'));WAL 增长原因：  大批量写入；  高频更新；  索引过多；  归档失败；  复制槽保留；  检查点配置；  长恢复窗口。16.8 复制槽SELECT slot_name, slot_type, active, restart_lsnFROM pg_replication_slots;失效复制槽会保留 WAL，导致磁盘被占满。逻辑订阅必须有监控和清理策略。本章小结WAL 用顺序日志换取崩溃恢复和复制能力。生产要理解提交策略、检查点、归档、PITR 和复制槽的关系，并持续监控 WAL 生成与保留。思考题  为什么先写 WAL 再刷数据页？  synchronous_commit 各级别有什么差异？  检查点参数如何影响 IO？  复制槽为什么会占满磁盘？  PITR 需要哪些备份条件？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。MVCC（Multi-Version Concurrency Control）让读不阻塞写、写不阻塞读，是 PostgreSQL 并发控制的核心。15.1 行版本每个行版本包含系统字段：            字段      含义                  xmin      创建版本的事务 ID              xmax      删除或更新该版本的事务 ID              ctid      行版本物理位置      查看：SELECT xmin, xmax, ctid, *FROM usersWHERE id = 1;15.2 可见性判断UPDATE id=1    |    +-- old tuple: xmin=100, xmax=101    +-- new tuple: xmin=101, xmax=0事务 100 之前的快照看到旧版本；事务 101 之后提交后的快照看到新版本。可见性取决于：  事务 ID；  提交状态；  事务快照；  xmax 状态；  clog 提交日志。15.3 死元组当旧版本对所有活跃快照不可见后，它成为死元组：dead tuple  -&gt; VACUUM 标记空间可复用  -&gt; 后续插入或更新可复用查看：SELECT    relname,    n_live_tup,    n_dead_tup,    last_vacuum,    last_autovacuumFROM pg_stat_user_tablesORDER BY n_dead_tup DESC;15.4 表膨胀原因：  高频 UPDATE；  长事务；  复制槽堆积；  autovacuum 配置不足；  索引过多；  大批量删除；  准备语句保持快照。后果：  表和索引变大；  扫描变慢；  缓存效率降低；  统计和计划变差；  磁盘空间浪费。15.5 快照保留以下对象会保留旧版本：  长事务；  空闲事务；  慢查询；  逻辑复制槽；  备库反馈延迟；  prepared transaction。检查复制槽：SELECT slot_name, slot_type, active, restart_lsnFROM pg_replication_slots;检查两阶段事务：SELECT * FROM pg_prepared_xacts;15.6 VACUUM 与 MVCCVACUUM 不消除膨胀空间给操作系统，而是标记页内空间可复用。想缩减文件需要重建表或使用在线重建工具。VACUUM users;VACUUM FULL users;VACUUM FULL 会重写表并请求 AccessExclusiveLock，生产慎用。15.7 事务 ID 回卷事务 ID 有限，需要冻结非常旧的事务。大量未冻结旧数据会触发激进 autovacuum，甚至阻止写入。关注：SELECT    relname,    n_dead_tup,    autovacuum_count,    last_autovacuumFROM pg_stat_user_tables;以及告警中的：database is not accepting commands to avoid wraparound risk必须提前治理长事务和 autovacuum。15.8 优化高频更新  减少无效更新；  只更新变化字段；  拆分热点字段；  合并状态写放大；  使用 unlogged 或缓存过渡表；  调整填充因子；  优化 autovacuum；  考虑分区降低单表压力。本章小结MVCC 通过多版本实现高并发，代价是旧版本留在表中等待 VACUUM 清理。理解死元组、长事务、复制槽和表膨胀，是 PostgreSQL 生产运维的关键。思考题  xmin 和 xmax 分别表示什么？  为什么长事务危险？  VACUUM 和 VACUUM FULL 的差异是什么？  表膨胀后如何恢复？  高频更新表如何降低写放大？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。事务是数据库保证一致性的基本单位。PostgreSQL 使用 MVCC 实现读写互不阻塞，并通过锁和谓词机制处理写冲突。14.1 基本用法BEGIN;UPDATE accounts SET balance = balance - 100 WHERE id = 1;UPDATE accounts SET balance = balance + 100 WHERE id = 2;COMMIT;回滚：ROLLBACK;保存点：BEGIN;INSERT INTO orders(order_no) VALUES ('O001');SAVEPOINT sp1;UPDATE orders SET status = 'PAID' WHERE order_no = 'O001';ROLLBACK TO SAVEPOINT sp1;COMMIT;14.2 ACID            特性      PostgreSQL 实现                  Atomicity      事务提交或回滚              Consistency      约束、触发器、应用规则              Isolation      MVCC + 锁              Durability      WAL 与同步提交策略      14.3 隔离级别BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;PostgreSQL 默认 READ COMMITTED。            级别      防止                  Read Committed      脏读              Repeatable Read      脏读、不可重复读              Serializable      脏读、不可重复读、幻读、写倾斜      PostgreSQL 的实现细节与 SQL 标准描述并不一一对应，应通过行为验证。14.4 异常现象脏读读到未提交数据。PostgreSQL 隔离级别下均不允许。不可重复读同一事务两次读取同一行结果不同。BEGIN;SELECT amount FROM orders WHERE id = 1;-- 其他事务修改并提交SELECT amount FROM orders WHERE id = 1;COMMIT;幻读同一查询条件两次返回不同行集。写倾斜两个事务分别读取相交条件，再分别修改不同行，导致业务约束被破坏。SERIALIZABLE 通过 SSI 防止。14.5 锁冲突SELECT FOR UPDATE;SELECT FOR SHARE;SELECT FOR NO KEY UPDATE;SELECT FOR KEY SHARE;常见冲突：            操作      冲突                  UPDATE 同一行      行锁等待              外键检查      引用行锁              唯一约束      唯一索引等待              DDL      AccessExclusiveLock      14.6 SERIALIZABLEBEGIN ISOLATION LEVEL SERIALIZABLE;SELECT count(*) FROM seats WHERE room_id = 1;INSERT INTO seats(room_id, user_id) VALUES (1, 1001);COMMIT;可能返回：ERROR: could not serialize access due to read/write dependencies among transactions应用必须重试。串行化隔离会增加谓词锁和取消概率，适合强一致性关键业务。14.7 事务边界推荐：  事务尽量短；  不在事务中调用慢外部服务；  批量任务分批提交；  重试逻辑处理序列化失败和死锁；  幂等键防止重试重复；  避免交互式客户端长期打开事务。14.8 查看事务SELECT    pid,    state,    xact_start,    now() - xact_start AS duration,    queryFROM pg_stat_activityWHERE state &lt;&gt; 'idle'ORDER BY xact_start;长事务会阻止 VACUUM 回收旧版本，是 PostgreSQL 高发问题。本章小结PostgreSQL 通过 MVCC 和锁提供多级隔离。默认 Read Committed 适合多数业务；需要快照一致或串行化时，要接受相应代价并设计重试。事务边界直接影响并发和清理能力。思考题  默认隔离级别下能否出现不可重复读？  SERIALIZABLE 为什么要重试？  长事务有哪些危害？  SELECT FOR UPDATE 适合什么场景？  事务中为什么不能调用慢外部服务？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。分区表把逻辑大表拆成多个物理子表，目标是提升可管理性、剪枝性能和维护效率，不是所有大表都适合分区。13.1 分区类型CREATE TABLE events (    id bigint,    event_date date NOT NULL) PARTITION BY RANGE (event_date);            类型      适合                  RANGE      时间、序号区间              LIST      租户、地区、枚举              HASH      均匀打散              多级分区      时间 + 租户等      13.2 创建分区CREATE TABLE events_2026_08 PARTITION OF eventsFOR VALUES FROM ('2026-08-01') TO ('2026-09-01');默认分区：CREATE TABLE events_default PARTITION OF events DEFAULT;默认分区会承接无匹配数据，可能掩盖分区配置错误，使用要谨慎。13.3 自动分区PostgreSQL 原生不会自动创建所有未来分区。常见做法：  定时任务提前创建；  应用按模板创建；  使用扩展；  运维平台管理。示例存储过程：CREATE OR REPLACE FUNCTION create_event_partition(day date)RETURNS void AS $$BEGIN    EXECUTE format(        'CREATE TABLE IF NOT EXISTS %I PARTITION OF events FOR VALUES FROM (%L) TO (%L)',        'events_' || to_char(day, 'YYYY_MM'),        date_trunc('month', day),        date_trunc('month', day) + interval '1 month'    );END;$$ LANGUAGE plpgsql;13.4 分区剪枝EXPLAINSELECT count(*)FROM eventsWHERE event_date &gt;= '2026-08-01'  AND event_date &lt; '2026-09-01';运行时参数可能导致运行时剪枝，执行计划仍需查看实际分区数量。分区键上的函数包装会削弱剪枝。13.5 索引父表创建分区索引：CREATE INDEX ON events(event_date, user_id);会在各分区创建对应索引。也可以对单个分区维护索引。13.6 主键与唯一约束分区表唯一约束必须包含分区键：ALTER TABLE eventsADD CONSTRAINT pk_events PRIMARY KEY (event_date, id);如果业务唯一键不含时间，需要全局唯一方案，如 UUID、序列或应用约束。13.7 分区维护删除旧分区：DROP TABLE events_2023_08;摘除分区：ALTER TABLE events DETACH PARTITION events_2023_08;比大规模 DELETE 更高效，也更容易归档。13.8 分区数量过多分区会带来：  计划时间增加；  锁管理成本；  内存和元数据压力；  autovacuum 调度压力。建议按数据量、保留期和查询窗口选择粒度。常见日志表按月或按天，不要默认按小时。13.9 什么时候分区适合：  数据量大；  查询常按时间或租户过滤；  需要快速归档删除；  单表索引过大；  冷热存储管理。不适合：  小表；  查询无分区条件；  分区后仍访问所有分区；  唯一约束难以调整。本章小结分区表的核心收益是分区剪枝和生命周期管理。设计时先确定分区键和粒度，保证高频查询能剪枝，并提前准备自动创建和归档流程。思考题  RANGE、LIST、HASH 如何选择？  分区表唯一约束有什么限制？  为什么要提前创建分区？  分区是否总能提升查询性能？  DETACH 与 DELETE 有什么差异？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。查询优化是围绕“减少扫描、减少排序、减少中间结果、控制内存”的循环过程。12.1 优化流程收集慢 SQL  -&gt; EXPLAIN ANALYZE  -&gt; 检查扫描节点  -&gt; 检查过滤条件  -&gt; 检查索引匹配  -&gt; 检查连接顺序  -&gt; 检查排序聚合内存  -&gt; 压测并发  -&gt; 固化规范12.2 慢 SQL 来源日志：SHOW log_min_duration_statement;可使用 pg_stat_statements：CREATE EXTENSION IF NOT EXISTS pg_stat_statements;SELECT    queryid,    calls,    total_exec_time,    mean_exec_time,    rows,    shared_blks_read,    queryFROM pg_stat_statementsORDER BY total_exec_time DESCLIMIT 20;12.3 索引匹配常见失效：WHERE created_at::date = current_date改写：WHERE created_at &gt;= current_date  AND created_at &lt; current_date + interval '1 day'类型一致：bigint 列 = '123' 参数可转换text 列 = int 参数可能不走索引12.4 减少返回数据避免：SELECT * FROM orders;分页：SELECT id, order_noFROM ordersWHERE id &gt; 100000ORDER BY idLIMIT 20;深分页可用键集分页替代 OFFSET。12.5 JOIN 优化SELECT o.id, u.nameFROM orders oJOIN users u ON u.id = o.user_idWHERE o.created_at &gt;= now() - interval '1 day';要点：  连接键类型一致；  小表驱动或哈希连接由优化器选择；  内层循环必须有索引；  先过滤再连接；  避免函数包装连接键；  控制连接列重复值。12.6 聚合与排序HashAggregate 内存超限会改用磁盘：SET work_mem = '64MB';调整只针对会话或特定查询，不要全局盲目调大导致并发内存不足。优化：  先过滤；  减少分组维度；  物化汇总表；  覆盖索引；  使用近似；  分批处理。12.7 并行查询查看：EXPLAIN ANALYZESELECT count(*) FROM large_table;相关参数：SHOW max_parallel_workers_per_gather;SHOW max_parallel_workers;SHOW min_parallel_table_scan_size;并行不是万能：小表、索引点查、高并发短查询可能收益低。12.8 CTE 与子查询PostgreSQL 12+ 默认可以下推非物化 CTE。若显式 WITH MATERIALIZED，外层条件可能无法下推：WITH t AS MATERIALIZED (    SELECT * FROM orders WHERE status = 'PAID')SELECT * FROM t WHERE user_id = 1001;需要复用中间结果时物化有价值，否则用执行计划验证。12.9 参数化计划使用绑定参数可减少解析和计划次数：PREPARE get_orders(bigint) ASSELECT * FROM orders WHERE user_id = $1;EXECUTE get_orders(1001);极端数据分布下，通用计划可能不适合所有参数。PostgreSQL 有 plan cache 策略，应用层也可评估指定查询不走通用计划。12.10 常见优化清单            现象      优化                  Seq Scan 大表      检查索引和过滤条件              Index Scan 大量回表      覆盖索引或 Bitmap              Rows Removed 高      索引列不匹配              Sort Disk      work_mem、索引排序              Hash 耗时      连接键分布、过滤              估算偏差      ANALYZE、扩展统计              临时块高      减少中间结果      本章小结查询优化要从真实执行数据出发。pg_stat_statements 找成本最高的 SQL，EXPLAIN ANALYZE 定位节点，统计信息和索引决定大部分性能。并发场景必须一起压测。思考题  total_exec_time 和 mean_exec_time 分别适合什么排序？  函数包装列为什么影响索引？  work_mem 如何安全调整？  并行查询何时收益有限？  CTE 物化有什么利弊？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。优化器依赖统计信息估算行数。统计缺失、过期或表达式复杂，都可能导致错误计划。11.1 ANALYZEANALYZE orders;ANALYZE orders (user_id, status);查看：SELECT    relname,    n_live_tup,    n_mod_since_analyze,    last_analyze,    last_autoanalyzeFROM pg_stat_user_tablesORDER BY n_mod_since_analyze DESC;autovacuum 会自动触发 analyze，但大表、突变负载和复杂查询仍可能需要手动窗口。11.2 pg_statsSELECT    tablename,    attname,    n_distinct,    null_frac,    correlationFROM pg_statsWHERE schemaname = 'public'  AND tablename = 'orders';关键列：            列      含义                  n_distinct      唯一值估算              null_frac      NULL 比例              correlation      物理顺序相关性              most_common_vals      高频值              most_common_freqs      高频值频率              histogram_bounds      分布直方图      11.3 扩展统计列相关时，单独统计会低估组合基数：SELECT *FROM ordersWHERE city = 'Shanghai' AND platform = 'App';创建：CREATE STATISTICS stat_orders_city_platform (dependencies)ON city, platform FROM orders;ANALYZE orders;查看：SELECT stxname, stxdependenciesFROM pg_statistic_ext;11.4 统计目标ALTER TABLE orders ALTER COLUMN status SET STATISTICS 500;ANALYZE orders;全局默认：SHOW default_statistics_target;目标越高采样越多、统计更细，但 analyze 成本和计划时间增加。热点复杂列可单独提高。11.5 表达式统计PostgreSQL 可对表达式收集统计。高频表达式查询可验证：SELECT *FROM pg_statsWHERE tablename LIKE '%lower(email)%';需要表达式索引或相应统计支持，否则复杂函数过滤可能严重低估或高估。11.6 统计失效场景            场景      后果                  大批量导入后未 analyze      估算过期              JSONB 内部字段      默认统计不足              跨列相关      低估组合过滤              自定义函数      难以估算              分区裁剪条件复杂      计划不稳定              数据倾斜      平均分布失真      11.7 分区表统计分区表既要看父表，也要看子分区。新分区必须在数据加载后收集统计，否则首查计划可能异常。ANALYZE events_2026_08;11.8 估算问题排查EXPLAIN ANALYZESELECT *FROM ordersWHERE status = 'PAID'  AND created_at &gt;= now() - interval '1 day';对比：plan rowsactual rowsRows Removed by Filterloops处理顺序：  ANALYZE；  查 pg_stats；  检查表达式；  增加统计目标；  增加扩展统计；  改写 SQL。本章小结统计信息是优化器决策依据。日常要监控 n_mod_since_analyze，大表导入后及时分析，复杂列使用扩展统计和更高统计目标。估算修复往往比强行加索引更有效。思考题  ANALYZE 和 VACUUM 的职责有什么不同？  correlation 会影响哪种扫描？  为什么跨列相关需要扩展统计？  default_statistics_target 是否越大越好？  分区表统计要注意什么？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。BRIN 适合超大规模且物理顺序与时间或自增字段强相关的表；SP-GiST 适合空间划分、前缀和不平衡树结构。10.1 BRINBRIN（Block Range Index）不索引每一行，而是记录连续数据块的范围摘要。Block 1-128: min created_at = 08-01, max = 08-02Block 129-256: min created_at = 08-02, max = 08-03创建：CREATE INDEX idx_events_created_brinON events USING BRIN (created_at);查询：SELECT count(*)FROM eventsWHERE created_at &gt;= '2026-08-25'  AND created_at &lt; '2026-08-26';10.2 BRIN 适合场景            条件      说明                  表很大      大表收益明显              物理顺序相关      append-only 时间日志              查询范围明确      按时间窗口              索引空间敏感      B-Tree 太大              可接受粗粒度      不要求精确定位      不适合随机写入后物理顺序混乱的数据，除非先按查询键重写。10.3 BRIN 参数CREATE INDEX idx_events_created_brinON events USING BRIN (created_at)WITH (pages_per_range = 64);            参数      影响                  pages_per_range 小      更精准，索引更大              pages_per_range 大      更小，扫描范围更粗      可以配置自动汇总：ALTER TABLE eventsSET (autosummarize = on);10.4 SP-GiSTSP-GiST 支持空间划分树，适合数据天然可分区且不平衡的结构。常见用途：            类型      用途                  text      前缀匹配              point      空间划分              inet      网段              range      范围      前缀索引：CREATE INDEX idx_hosts_prefixON hosts USING SPGIST (name prefix_ops);网段：CREATE INDEX idx_ip_networkON access_log USING SPGIST (client_ip inet_ops);10.5 索引选择            查询      首选                  等值 / 范围 / 排序      B-Tree              纯等值高基数      可评估 HASH              JSON / 数组 / 全文      GIN              空间 / 范围重叠      GiST              大表时间 append      BRIN              前缀 / 网段 / 划分结构      SP-GiST      10.6 验证索引效果EXPLAIN (ANALYZE, BUFFERS)SELECT *FROM eventsWHERE created_at &gt;= now() - interval '1 hour';观察：  扫描节点；  Rows Removed by Filter；  Shared Hit / Read；  执行时间；  索引大小。本章小结BRIN 用极小空间换取粗粒度过滤，适合顺序性强的大表。SP-GiST 面向可划分结构。索引选择先看数据分布和操作符，再看空间和写入成本。思考题  BRIN 为什么适合 append-only 日志表？  pages_per_range 如何权衡？  SP-GiST 适合什么数据结构？  BRIN 如何验证扫描收益？  什么时候 B-Tree 优于 BRIN？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。GIN 和 GiST 是 PostgreSQL 支持复杂类型检索的重要索引。GIN 常用于 JSONB、数组、全文检索；GiST 常用于地理、范围类型和近邻搜索。9.1 GINCREATE INDEX idx_events_payloadON events USING GIN (payload);JSONB 查询：SELECT *FROM eventsWHERE payload @&gt; '{"event":"pay"}';数组：CREATE INDEX idx_articles_tagsON articles USING GIN (tags);SELECT *FROM articlesWHERE tags @&gt; ARRAY['java'];9.2 JSONB 操作符            操作符      含义                  @&gt;      左侧包含右侧 JSON              &lt;@      左侧被右侧包含              ?      是否存在顶层键              ?|      任一键存在              ?&amp;      所有键存在              -&gt;      取 JSON 对象              -&gt;&gt;      取文本      默认 GIN 支持 @&gt;、?、?|、?&amp;，运算符类不同支持可能有差异。9.3 全文检索CREATE TABLE docs (    id bigint PRIMARY KEY,    body text NOT NULL,    body_tsv tsvector GENERATED ALWAYS AS (        to_tsvector('simple', body)    ) STORED);CREATE INDEX idx_docs_tsvON docs USING GIN (body_tsv);查询：SELECT id, bodyFROM docsWHERE body_tsv @@ to_tsquery('simple', 'postgres &amp; index');中文分词通常需要扩展或外部方案，不能假设内置配置满足所有语言。9.4 GIN 维护成本GIN 写入更新成本较高，通常依赖 pending list 延迟合并。高写入 JSONB 表要关注索引膨胀、更新延迟、vacuum 清理、查询模式和是否应提取生成列。9.5 GiSTPostGIS 示例：CREATE EXTENSION IF NOT EXISTS postgis;CREATE TABLE shops (    id bigint PRIMARY KEY,    name text,    location geography(Point, 4326));CREATE INDEX idx_shops_locationON shops USING GIST (location);附近查询：SELECT id, nameFROM shopsWHERE ST_DWithin(    location,    ST_SetSRID(ST_MakePoint(121.47, 31.23), 4326),    1000);9.6 范围类型CREATE TABLE bookings (    id bigint PRIMARY KEY,    room_id int NOT NULL,    during tstzrange NOT NULL);CREATE INDEX idx_bookings_duringON bookings USING GIST (during);时间重叠：SELECT *FROM bookingsWHERE during &amp;&amp; tstzrange(    '2026-08-25 14:00+08',    '2026-08-25 15:00+08',    '[)');可用排除约束防止同房间时间重叠。9.7 GIN 与 GiST 对比            维度      GIN      GiST                  查询      精确包含、倒排      范围、空间、近邻              更新      较重      相对均衡              通用性      复杂值检索      几何和范围模型              典型类型      JSONB、数组、tsvector      geography、range      9.8 索引失效场景  操作符与索引运算符类不匹配；  JSON 查询全为 -&gt;&gt; 文本后再复杂条件；  数组查询表达式不可索引；  全文没有构建 tsvector；  空间查询函数不走 GiST 运算符。本章小结GIN 适合包含关系和倒排检索，GiST 适合空间、范围和近邻查询。二者都比 B-Tree 更专用，也更容易带来写入和维护成本。使用前确认操作符、数据类型和查询频率。思考题  JSONB 的 @&gt; 和 -&gt;&gt; 有什么区别？  GIN 为什么写入成本高？  全文检索为什么要生成 tsvector？  GiST 适合哪些类型？  如何决定 JSON 字段是否建 GIN？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。B-Tree 是 PostgreSQL 默认索引，适合等值、范围和排序。HASH 索引只适合等值，适用面较窄。8.1 B-TreeCREATE INDEX idx_orders_user_createdON orders(user_id, created_at DESC);B-Tree 适合：  =；  &gt;、&gt;=、&lt;、&lt;=；  BETWEEN；  IS NULL；  ORDER BY；  前缀 LIKE 'abc%'。不适合：  LIKE '%abc'；  %abc%；  对列套函数后查询；  类型不匹配导致隐式转换；  低选择性大范围扫描。8.2 复合索引CREATE INDEX idx_orders_status_createdON orders(status, created_at);有效：WHERE status = 'PAID'WHERE status = 'PAID'  AND created_at &gt; now() - interval '1 day'ORDER BY created_at较弱：WHERE created_at &gt; now() - interval '1 day'复合索引遵循左前缀原则。设计顺序通常是等值列在前、范围列在后。8.3 覆盖索引CREATE INDEX idx_orders_coverON orders(user_id, created_at)INCLUDE (amount, status);INCLUDE 列不参与排序，可用于 Index Only Scan。是否能只扫索引还取决于可见性映射和 vacuum 状态。8.4 部分索引只索引活跃订单：CREATE INDEX idx_orders_openON orders(created_at)WHERE status IN ('CREATED', 'PAID');适合状态少部分占比、查询条件固定的场景。8.5 表达式索引CREATE INDEX idx_users_lower_emailON users(lower(email));查询必须使用相同表达式：SELECT *FROM usersWHERE lower(email) = 'alice@example.com';适合函数值高频查询，但增加写入成本，并要求函数语义稳定。8.6 唯一索引CREATE UNIQUE INDEX uq_orders_order_noON orders(order_no);部分唯一索引常配合软删除：CREATE UNIQUE INDEX uq_users_email_activeON users(email)WHERE deleted_at IS NULL;8.7 HASH 索引CREATE INDEX idx_users_email_hashON users USING HASH (email);            维度      HASH      B-Tree                  等值      支持      支持              范围排序      不支持      支持              索引大小      可能更小      通常更大              适用面      较窄      通用      只有纯等值、基数高且 B-Tree 空间压力大时才值得评估 HASH。8.8 索引维护查看索引使用：SELECT    schemaname,    relname,    indexrelname,    idx_scan,    idx_tup_read,    idx_tup_fetchFROM pg_stat_user_indexesORDER BY idx_scan;重建：REINDEX INDEX idx_orders_user_created;REINDEX TABLE CONCURRENTLY orders;删除索引前观察完整业务周期，避免只看低峰一天。8.9 索引代价  写入放大；  存储成本；  更新热点；  VACUUM 负担；  优化器选择成本。索引不是越多越好。每个索引都应能对应明确查询。本章小结B-Tree 覆盖大多数等值、范围和排序需求。复合索引注意左前缀，表达式和部分索引能解决特殊模式，HASH 只适合纯等值。索引设计要同时考虑查询收益和写入代价。思考题  为什么复合索引左前缀重要？  INCLUDE 列和键列有什么区别？  部分索引适合什么场景？  表达式索引有什么限制？  如何发现无用索引？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。执行计划是 PostgreSQL 性能优化的地图。优化 SQL 前，先能读懂每个节点的访问方式、行数估算、循环次数、排序和内存使用。7.1 EXPLAINEXPLAINSELECT user_id, count(*), sum(amount)FROM ordersWHERE status = 'PAID'GROUP BY user_id;EXPLAIN 只显示计划，不执行。查看真实执行指标使用：EXPLAIN ANALYZESELECT ...查看缓冲：EXPLAIN (ANALYZE, BUFFERS)SELECT ...ANALYZE 会真正执行 DML 或耗时 SELECT，生产慎用。7.2 常见扫描节点            节点      说明                  Seq Scan      全表顺序扫描              Index Scan      索引扫描并回表              Index Only Scan      只读索引，需可见性映射支持              Bitmap Index Scan      生成位图              Bitmap Heap Scan      按位图回表              Function Scan      函数结果扫描              Foreign Scan      外部表扫描      Seq Scan 不一定是坏事。小表、大比例返回、没有合适索引时，它可能成本最低。7.3 连接节点EXPLAIN ANALYZESELECT u.name, o.amountFROM users uJOIN orders o ON o.user_id = u.id;            节点      适合                  Nested Loop      外层小、内层能走索引              Hash Join      大表等值连接              Merge Join      输入已按连接键排序      如果 Nested Loop 内层是 Seq Scan 且 loops 很大，通常是缺索引或统计信息错误。7.4 聚合与排序常见节点：            节点      说明                  Aggregate      普通聚合              GroupAggregate      按序分组              HashAggregate      哈希分组              Sort      显式排序              Incremental Sort      部分有序输入              Unique      去重      Sort 内存超出 work_mem 会溢写临时文件：Sort Method: external merge Disk: 123456kB7.5 行数估算rows=1000actual time=... rows=1000000 loops=1估算与实际差异过大时，优化器可能选错计划。处理：ANALYZE orders;SELECT relname, n_live_tup, last_analyzeFROM pg_stat_user_tables;再检查是否有统计信息、WHERE 表达式是否可估算、是否跨列相关、是否需要扩展统计、是否参数被折叠成常量。7.6 关键指标cost=startup..totalrows=估算行数width=平均行宽actual time=启动..完成rows=实际行数loops=执行次数Shared Hit / Read = 缓冲命中与磁盘读取重点看每行耗时和外层循环放大，而不是只看第一行 cost。7.7 执行计划开关SET enable_seqscan = off;这类开关用于验证索引是否可用，不应作为生产“优化配置”。更好的方式是调整统计信息、索引、SQL 或代价参数。7.8 计划管理PostgreSQL 支持扩展的计划管理方案，如 pg_hint_plan 等工具，具体可用性取决于版本和发行版。生产引入前要确认维护成本。原生可先做：  固化 SQL；  更新统计；  建合适索引；  避免隐式转换；  减少返回数据；  记录基线计划。7.9 慢计划阅读流程1. 从最内层扫描看起2. 检查过滤条件和索引匹配3. 比较估算 rows 与 actual rows4. 检查 loops5. 检查 Sort / Hash 内存6. 检查 Rows Removed7. 优化后对比 buffers 和时间本章小结执行计划回答三个问题：扫了多少数据、用了什么算法、估算是否可靠。读懂 ANALYZE 和 BUFFERS 输出，是 PostgreSQL 优化的基本功。思考题  Index Scan 和 Index Only Scan 差异是什么？  loops 很大说明什么？  Sort 溢写磁盘如何处理？  估算行数偏差过大会带来什么？  为什么 enable_seqscan = off 不适合生产？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。约束是数据库兜底的数据质量能力。应用校验、数据库约束和审计规则共同构成可靠模型。6.1 建表模板CREATE TABLE orders (    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,    order_no TEXT NOT NULL UNIQUE,    user_id BIGINT NOT NULL REFERENCES users(id),    status order_status NOT NULL DEFAULT 'CREATED',    amount NUMERIC(12,2) NOT NULL CHECK (amount &gt;= 0),    paid_at TIMESTAMPTZ,    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),    updated_at TIMESTAMPTZ NOT NULL DEFAULT now());6.2 主键ALTER TABLE orders ADD PRIMARY KEY (id);复合主键：CREATE TABLE order_items (    order_id BIGINT NOT NULL,    line_no INT NOT NULL,    sku_id BIGINT NOT NULL,    PRIMARY KEY (order_id, line_no));主键会自动创建唯一 B-Tree 索引。6.3 唯一约束ALTER TABLE usersADD CONSTRAINT uq_users_email UNIQUE (email);部分唯一索引：CREATE UNIQUE INDEX uq_users_active_emailON users(email)WHERE deleted_at IS NULL;适合软删除场景。6.4 外键ALTER TABLE ordersADD CONSTRAINT fk_orders_userFOREIGN KEY (user_id)REFERENCES users(id)ON DELETE RESTRICT;策略：            策略      含义                  NO ACTION / RESTRICT      禁止删除被引用行              CASCADE      级联删除或更新              SET NULL      置空引用列              SET DEFAULT      设置默认值      高写入系统要评估外键索引和锁开销，但不能只因为“麻烦”而放弃数据完整性。6.5 CHECK 约束ALTER TABLE ordersADD CONSTRAINT ck_orders_paid_atCHECK (status &lt;&gt; 'PAID' OR paid_at IS NOT NULL);命名约束便于排障和迁移：pk_ / uq_ / fk_ / ck_ 前缀6.6 默认值与触发器默认值：ALTER TABLE ordersALTER COLUMN created_at SET DEFAULT now();更新时间触发器：CREATE OR REPLACE FUNCTION set_updated_at()RETURNS trigger AS $$BEGIN    NEW.updated_at = now();    RETURN NEW;END;$$ LANGUAGE plpgsql;CREATE TRIGGER trg_orders_updated_atBEFORE UPDATE ON ordersFOR EACH ROWEXECUTE FUNCTION set_updated_at();触发器逻辑隐蔽，应有清单和审计。6.7 表继承与分区传统表继承较少用于新业务，分区表是更常见的物理拆分方式：CREATE TABLE events (    id bigint,    event_date date NOT NULL) PARTITION BY RANGE (event_date);分区细节在第 13 章展开。6.8 Schema 管理CREATE SCHEMA sales;ALTER TABLE sales.orders RENAME TO orders_2026;COMMENT ON TABLE orders IS '订单主表';多租户可用独立数据库、独立 Schema 或共享表 + RLS，成本和隔离性不同。6.9 DDL 迁移安全原则：  先在测试环境验证；  大表加列避免长时间锁；  建索引使用 CONCURRENTLY；  修改类型评估重写成本；  迁移可回滚或可补偿；  与应用发布顺序明确。并发建索引：CREATE INDEX CONCURRENTLY idx_orders_user_createdON orders(user_id, created_at);6.10 约束排查查看约束：SELECT    conname,    contype,    conrelid::regclass AS table_nameFROM pg_constraintWHERE conrelid = 'orders'::regclass;常见错误：            错误      含义                  duplicate key      违反唯一约束              foreign key violation      外键不存在或被引用              check constraint      条件不满足              not-null violation      必填列空      本章小结表结构设计的核心是明确业务键、状态流、时间口径和引用关系。约束让非法数据难以进入数据库，迁移则必须考虑锁时间和回滚方案。思考题  唯一约束和部分唯一索引有什么差异？  外键删除策略如何选择？  大表建索引应注意什么？  什么时候使用触发器？  软删除如何影响唯一性？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。PostgreSQL 的类型系统丰富且严格。合理选型能提升正确性、存储效率和索引效果。5.1 数值类型            类型      适合                  smallint / int / bigint      整数主键、数量              numeric(p,s)      金额、精度计算              real / double precision      科学计算、近似值              serial / identity      自增标识，新项目优先 identity      金额：CREATE TABLE payments (    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,    amount NUMERIC(12,2) NOT NULL CHECK (amount &gt;= 0),    rate NUMERIC(8,6) NOT NULL);不要使用浮点保存资金余额。5.2 文本类型            类型      说明                  varchar(n)      变长，可加长度约束              text      变长，无内置长度限制              char(n)      定长，自动填充      业务上通常使用 text，需要长度规则时用 CHECK 约束表达：CREATE TABLE members (    email text PRIMARY KEY CHECK (email ~* '^[^@]+@[^@]+$'),    name text NOT NULL CHECK (char_length(name) BETWEEN 1 AND 50));5.3 时间类型            类型      说明                  timestamp      不带时区              timestamptz      带时区语义              date      日期              time / timetz      时间              interval      时间间隔      建议业务时间使用 timestamptz：CREATE TABLE audit_log (    event_time TIMESTAMPTZ NOT NULL DEFAULT now(),    payload JSONB NOT NULL);时区转换：SELECT event_time AT TIME ZONE 'Asia/Shanghai'FROM audit_log;5.4 布尔与枚举布尔：SELECT true, false, null::boolean;枚举：CREATE TYPE order_status AS ENUM (    'CREATED', 'PAID', 'SHIPPED', 'CANCELLED');ALTER TYPE order_status ADD VALUE 'REFUNDED';枚举类型有序、存储紧凑，但删除值和顺序调整受限。需要频繁演进时可使用 text + CHECK 或字典表。5.5 UUIDCREATE EXTENSION IF NOT EXISTS pgcrypto;CREATE TABLE tenants (    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),    name text NOT NULL);UUID 适合分布式生成 ID，但随机 UUID 会降低 B-Tree 写入局部性。可评估有序 UUID 方案或数据库序列。5.6 数组CREATE TABLE articles (    id bigint PRIMARY KEY,    tags text[] NOT NULL DEFAULT '{}');查询：SELECT * FROM articles WHERE 'java' = ANY(tags);SELECT * FROM articles WHERE tags @&gt; ARRAY['java','sql'];数组适合一对多且无需独立属性的小列表。若标签需要描述、状态和权限，应建表。5.7 JSON 与 JSONBjson 保存原始文本，jsonb 是解析后的二进制格式，支持索引和更快操作。CREATE TABLE events (    id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,    payload JSONB NOT NULL,    created_at timestamptz NOT NULL DEFAULT now());查询：SELECT    payload -&gt;&gt; 'city' AS city,    payload -&gt; 'props' -&gt;&gt; 'os' AS osFROM eventsWHERE payload @&gt; '{"event":"pay"}';更新：UPDATE eventsSET payload = payload || '{"amount":99}'::jsonbWHERE id = 1;JSONB 适合 schema 演进快的半结构化数据，不适合所有字段都无边界 JSON 化。高频查询字段应生成列并索引。5.8 生成列CREATE TABLE events_v2 (    id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,    payload jsonb NOT NULL,    city text GENERATED ALWAYS AS (payload -&gt;&gt; 'city') STORED,    amount numeric(12,2) GENERATED ALWAYS AS (        (payload -&gt;&gt; 'amount')::numeric    ) STORED);对生成列建索引：CREATE INDEX idx_events_city ON events_v2(city);5.9 类型转换SELECT '2026-08-25'::date;SELECT '123'::int;SELECT now()::date;SELECT CAST('99.50' AS numeric(10,2));转换失败会报错。ETL 中可自定义安全转换函数并记录坏数据，而不是在 SQL 中吞掉异常。5.10 类型设计规范  主键明确且稳定；  金额使用 numeric；  时间使用 timestamptz；  状态使用 enum、字典表或受控 text；  高频 JSON 字段提升生成列；  外键不要只存在于应用代码；  避免一个超大宽表包含所有业务域。本章小结类型是业务约束的第一层。数值精度、时间时区、文本长度、JSON 结构和生成列都会影响正确性和性能。PostgreSQL 的扩展类型很强，但不要为了灵活牺牲数据治理。思考题  numeric 和 double precision 如何选择？  timestamp 和 timestamptz 有什么区别？  enum 类型的演进限制是什么？  JSONB 高频字段如何优化？  数组字段什么时候应该拆成表？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。PostgreSQL 的高级 SQL 能力适合做报表、批处理和复杂业务建模。本章覆盖分组集、递归查询、LATERAL、行转列、自定义函数和性能边界。4.1 分组集SELECT city, status, count(*), sum(amount)FROM orders oJOIN users u ON u.id = o.user_idGROUP BY GROUPING SETS (    (city, status),    (city),    (),    (status));等价能力：ROLLUP (city, status)CUBE (city, status)识别小计行：SELECT grouping(city) AS city_grouping, city, sum(amount)FROM ordersGROUP BY ROLLUP (city);4.2 递归 CTE组织树：WITH RECURSIVE org_tree AS (    SELECT id, parent_id, name, 1 AS level    FROM departments    WHERE parent_id IS NULL    UNION ALL    SELECT d.id, d.parent_id, d.name, t.level + 1    FROM departments d    JOIN org_tree t ON d.parent_id = t.id)SELECT * FROM org_tree;注意：  必须有终止条件；  防止环引用；  深度较大时关注执行成本；  可以用 path 数组辅助调试。4.3 LATERAL每个用户最近三笔订单：SELECT u.id, u.name, o.id AS order_id, o.amountFROM users uLEFT JOIN LATERAL (    SELECT id, amount    FROM orders    WHERE user_id = u.id    ORDER BY created_at DESC    LIMIT 3) o ON true;LATERAL 允许子查询引用左侧表字段，常用于 TopN per group 和按组展开。4.4 行转列SELECT city,       sum(amount) FILTER (WHERE status = 'PAID') AS paid_amount,       sum(amount) FILTER (WHERE status = 'CANCELLED') AS cancelled_amount,       count(*) FILTER (WHERE status = 'CREATED') AS created_countFROM ordersGROUP BY city;FILTER 比多个 CASE 更清晰：sum(CASE WHEN status = 'PAID' THEN amount ELSE 0 END)4.5 DISTINCT ONSELECT DISTINCT ON (user_id)    user_id, id, amount, created_atFROM ordersORDER BY user_id, created_at DESC;每个用户返回一行，取 ORDER BY 中该组第一行。需要其他最新字段时可配合窗口函数。4.6 生成系列补齐时间轴：SELECT generate_series(    date_trunc('day', now() - interval '6 days'),    date_trunc('day', now()),    interval '1 day')::date AS day;补零报表：SELECT d.day, coalesce(sum(o.amount), 0) AS gmvFROM generate_series(    current_date - 6,    current_date,    interval '1 day') AS d(day)LEFT JOIN orders o  ON o.created_at::date = d.day::dateGROUP BY d.dayORDER BY d.day;4.7 表表达式VALUES：SELECT *FROM (VALUES    ('CREATED', 1),    ('PAID', 2)) AS status_map(status, sort_no);UNNEST：SELECT unnest(ARRAY[1,2,3]) AS n;4.8 自定义函数CREATE OR REPLACE FUNCTION normalize_phone(input text)RETURNS textLANGUAGE sqlIMMUTABLEAS $$    SELECT regexp_replace(coalesce(input, ''), '[^0-9]', '', 'g')$$;标示：            属性      含义                  IMMUTABLE      输入相同结果必相同，可优化              STABLE      事务内稳定              VOLATILE      默认，可能有副作用      高并发热点路径慎用 PL/pgSQL 循环，能用集合 SQL 就不用逐行函数。4.9 性能边界高级 SQL 仍要看执行计划：EXPLAIN ANALYZEWITH RECURSIVE ...常见问题：  递归无界；  LATERAL 子查询无法使用索引；  窗口函数排序成本高；  CTE 物化后外层无法下推；  大结果集 FILTER 聚合内存高。本章小结高级 SQL 让许多复杂业务逻辑在数据库内一次完成。GROUPING SETS、递归 CTE、LATERAL、FILTER 和 DISTINCT ON 是 PostgreSQL 特色能力。表达力越强，越需要用执行计划约束成本。思考题  ROLLUP 和 CUBE 的区别是什么？  递归 CTE 如何防止死循环？  LATERAL 适合什么查询？  FILTER 和 CASE 聚合哪个更清晰？  函数 IMMUTABLE 有什么优化意义？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。本章覆盖 PostgreSQL 日常 SQL：查询、过滤、聚合、连接、集合、插入更新删除和 UPSERT。3.1 示例表CREATE TABLE users (    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,    name TEXT NOT NULL,    city TEXT NOT NULL,    created_at TIMESTAMPTZ NOT NULL DEFAULT now());CREATE TABLE orders (    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,    user_id BIGINT NOT NULL REFERENCES users(id),    status TEXT NOT NULL,    amount NUMERIC(12,2) NOT NULL,    created_at TIMESTAMPTZ NOT NULL DEFAULT now());3.2 查询与过滤SELECT id, name, cityFROM usersWHERE city IN ('Shanghai', 'Beijing')  AND created_at &gt;= now() - interval '7 days'ORDER BY created_at DESCLIMIT 20;常见谓词：            谓词      说明                  = / &lt;&gt;      等值              BETWEEN      范围              IN      集合              LIKE / ILIKE      模糊，ILIKE 忽略大小写              IS NULL      空值              EXISTS      子查询存在      3.3 聚合SELECT    u.city,    count(*) AS order_count,    count(DISTINCT o.user_id) AS user_count,    sum(o.amount) AS gmv,    round(avg(o.amount), 2) AS avg_amountFROM orders oJOIN users u ON u.id = o.user_idWHERE o.status = 'PAID'GROUP BY u.cityHAVING count(*) &gt; 100ORDER BY gmv DESC;过滤顺序：FROM -&gt; JOIN -&gt; WHERE -&gt; GROUP BY -&gt; HAVING -&gt; SELECT -&gt; ORDER BY -&gt; LIMIT3.4 连接SELECT u.name, o.id, o.amountFROM users uLEFT JOIN orders o ON o.user_id = u.idWHERE u.city = 'Shanghai';            连接      语义                  INNER JOIN      只保留匹配行              LEFT JOIN      左表全保留              RIGHT JOIN      右表全保留              FULL JOIN      两表全保留              CROSS JOIN      笛卡尔积      LEFT JOIN 后如果再对右表列使用 WHERE 过滤，可能退化成 INNER JOIN。右表条件通常应放 ON。3.5 子查询与 CTEWITH daily AS (    SELECT date_trunc('day', created_at) AS day,           user_id,           sum(amount) AS amount    FROM orders    WHERE status = 'PAID'    GROUP BY 1, 2)SELECT day, count(*) AS pay_users, sum(amount) AS gmvFROM dailyGROUP BY dayORDER BY day;PostgreSQL 12+ 默认 materialize 语义可通过 MATERIALIZED / NOT MATERIALIZED 控制。重复引用的 CTE 不一定自动只执行一次，需结合执行计划确认。3.6 窗口函数SELECT    user_id,    created_at::date AS day,    amount,    row_number() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rn,    sum(amount) OVER (PARTITION BY user_id ORDER BY created_at) AS running_totalFROM ordersWHERE status = 'PAID';每用户最近一笔订单：SELECT user_id, amount, created_atFROM (    SELECT *,           row_number() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rn    FROM orders) tWHERE rn = 1;3.7 插入与更新INSERT INTO users (name, city) VALUES ('Alice', 'Shanghai');UPDATE ordersSET status = 'PAID'WHERE id = 1001;DELETE FROM ordersWHERE created_at &lt; now() - interval '3 years';更新前先 SELECT 或 EXPLAIN 确认条件，避免无 WHERE 的大范围误操作。3.8 UPSERTINSERT INTO users (id, name, city)VALUES (1, 'Alice', 'Shanghai')ON CONFLICT (id)DO UPDATE SET name = EXCLUDED.name, city = EXCLUDED.city;只插入冲突时忽略：INSERT INTO users (id, name, city)VALUES (2, 'Bob', 'Beijing')ON CONFLICT (id) DO NOTHING;条件更新：ON CONFLICT (id)DO UPDATE SET city = EXCLUDED.cityWHERE users.city IS DISTINCT FROM EXCLUDED.city;3.9 RETURNINGINSERT INTO users (name, city)VALUES ('Carol', 'Hangzhou')RETURNING id, created_at;DELETE FROM ordersWHERE id = 1002RETURNING id, amount;RETURNING 常用于获取自增 ID、审计变更和实现幂等任务。3.10 空值安全SELECT    coalesce(nick_name, name, 'unknown') AS display_name,    nullif(phone, '') AS phone,    amount IS DISTINCT FROM 0 AS has_amountFROM members;注意：  NULL = NULL 结果未知；  NOT IN 遇 NULL 可能不返回行；  空字符串不是 NULL；  聚合函数通常忽略普通 NULL。本章小结PostgreSQL SQL 能力完整，日常重点是把过滤、聚合、连接和窗口函数写清楚。CTE 提升可读性，UPSERT 与 RETURNING 是业务开发高频能力。始终注意空值、连接条件和执行成本。思考题  WHERE 和 HAVING 的区别是什么？  LEFT JOIN 的右表过滤条件应放在哪里？  NOT IN 遇 NULL 有什么风险？  ON CONFLICT 依赖什么约束？  RETURNING 能解决哪些开发问题？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。本章搭建 PostgreSQL 学习与测试环境，并梳理生产部署必须提前规划的目录、认证、网络、备份和版本策略。2.1 版本选择查看版本：SELECT version();SHOW server_version;建议：            环境      版本策略                  学习      当前主流稳定版本              新项目      选择社区仍在维护的版本              存量系统      固定小版本，按计划升级              扩展使用      确认 PostGIS、pgvector 等兼容性      跨大版本升级必须使用 pg_upgrade、逻辑复制或导出导入方案，并提前演练。2.2 Docker 启动docker run -d \  --name postgres \  -e POSTGRES_PASSWORD=postgres \  -e POSTGRES_USER=postgres \  -e POSTGRES_DB=shop \  -p 5432:5432 \  -v pgdata:/var/lib/postgresql/data \  postgres:16连接：docker exec -it postgres psql -U postgres -d shop生产不建议只用默认自签名信任环境，应配置强密码、TLS、网络访问控制和最小权限用户。2.3 Linux 安装以包管理器安装后，确认服务：sudo systemctl status postgresqlsudo systemctl enable postgresql切换系统用户并连接：sudo -iu postgres psql常见目录：/var/lib/postgresql/16/main       数据目录/etc/postgresql/16/main           配置目录/var/log/postgresql               日志目录实际路径与发行版和安装方式有关，以 SHOW data_directory; 为准。2.4 psql常用命令：\l               列出数据库\c shop          切换数据库\dt              列出表\d orders        查看表结构\di              查看索引\du              查看用户和角色\x               切换扩展显示\timing          显示 SQL 耗时\q               退出执行文件：psql -h 127.0.0.1 -U postgres -d shop -f schema.sql导出 CSV：\copy (SELECT * FROM orders) TO 'orders.csv' WITH CSV HEADER2.5 初始化数据库CREATE DATABASE shop;CREATE USER app_user WITH PASSWORD 'strong-password';GRANT CONNECT ON DATABASE shop TO app_user;\c shopCREATE SCHEMA app AUTHORIZATION app_user;ALTER ROLE app_user SET search_path = app, public;最小权限：GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA app TO app_user;REVOKE ALL ON SCHEMA public FROM PUBLIC;2.6 核心配置postgresql.conf：listen_addresses = '*'max_connections = 200shared_buffers = 4GBeffective_cache_size = 12GBwork_mem = 32MBmaintenance_work_mem = 512MBwal_level = replicalogging_collector = onlog_min_duration_statement = 500初始参考不是固定公式，要根据内存、连接类型、查询负载和磁盘压测调整。2.7 认证与网络pg_hba.conf 控制：TYPE  DATABASE  USER  ADDRESS  METHOD示例：hostssl shop app_user 10.0.0.0/8 scram-sha-256local   all  postgres          peer建议：  远程连接使用 hostssl；  密码使用 scram-sha-256；  只放行应用网段；  应用用户与管理员分离；  不使用 trust 认证生产地址。2.8 基础验证SELECT current_database(), current_user, version();SHOW data_directory;SHOW shared_buffers;SHOW work_mem;查看连接：SELECT pid, usename, datname, client_addr, state, queryFROM pg_stat_activityWHERE pid &lt;&gt; pg_backend_pid();2.9 常见问题            问题      排查                  connection refused      服务、监听地址、防火墙              password authentication failed      用户、密码、pg_hba 方法              no pg_hba.conf entry      地址和方法未放行              too many clients      max_connections 和连接池              database does not exist      连接串库名错误      本章小结环境搭建不只是安装成功。PostgreSQL 生产环境要提前固定版本、规划数据目录、配置网络认证、拆分用户权限、开启日志和备份。psql 是日常运维最应该熟练的工具。思考题  shared_buffers 和 effective_cache_size 分别表达什么？  为什么生产远程连接建议使用 hostssl？  如何为应用创建最小权限用户？  pg_hba.conf 的匹配顺序有什么影响？  大版本升级前需要准备什么？</li>
  <li>这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。PostgreSQL 是功能强大的开源关系型数据库，以标准 SQL 支持完善、扩展能力强、数据类型丰富和内核工程严谨著称。它既能承担传统业务系统的事务存储，也能支持 JSONB、GIS、全文检索、时序和向量等扩展场景。本书以 PostgreSQL 16 为主线，兼容 13-15 常见生产版本，从 SQL 和索引讲到 MVCC、WAL、VACUUM、复制、高可用和生产治理。1.1 PostgreSQL 的定位PostgreSQL 常被称为“最先进的开源关系型数据库”。它的特点是：  SQL 标准支持完整；  事务和 MVCC 实现清晰；  数据类型丰富；  索引类型多；  扩展生态强；  逻辑复制和物理复制都支持；  适合复杂查询和数据治理。常见使用场景：            场景      依赖能力                  核心交易系统      ACID、MVCC、WAL              复杂报表      窗口函数、CTE、并行查询              多租户 SaaS      Schema、RLS、权限体系              地理信息系统      PostGIS              半结构化数据      JSONB              全文检索      tsvector / GIN              时序数据      TimescaleDB 扩展              向量检索      pgvector 扩展      1.2 进程与内存架构PostgreSQL 是多进程架构。Client  -&gt; postgres backend process     -&gt; Parser     -&gt; Planner / Optimizer     -&gt; Executor     -&gt; Shared Buffer     -&gt; WAL     -&gt; Data Files重要后台进程：            进程      职责                  postmaster      监听连接，创建 backend 进程              backend      服务一个客户端连接              background writer      刷脏页              checkpointer      创建检查点              WAL writer      写 WAL 缓冲              autovacuum launcher      自动清理调度              autovacuum worker      执行 VACUUM / ANALYZE              logical replication      逻辑复制      重要内存区域：            区域      说明                  shared_buffers      共享数据页缓存              work_mem      排序、哈希等操作内存              maintenance_work_mem      VACUUM、索引构建等维护内存              wal_buffers      WAL 缓冲              local memory      每个 backend 私有内存      1.3 查询执行流程一条 SQL 的处理流程：Parse  -&gt; Analyze  -&gt; Rewrite  -&gt; Plan  -&gt; Execute示例：EXPLAIN ANALYZESELECT user_id, count(*) AS order_count, sum(pay_amount) AS gmvFROM ordersWHERE created_at &gt;= now() - interval '7 days'GROUP BY user_idORDER BY gmv DESCLIMIT 20;PostgreSQL 的执行计划以树状结构展示：Limit  -&gt; Sort       -&gt; HashAggregate            -&gt; Seq Scan / Index Scan常见节点：            节点      说明                  Seq Scan      顺序扫描              Index Scan      索引扫描并回表              Index Only Scan      覆盖索引扫描              Bitmap Heap Scan      位图回表              Nest Loop      嵌套循环              Hash Join      哈希连接              Merge Join      归并连接              HashAggregate      哈希聚合              Sort      排序      1.4 MVCC 特点PostgreSQL 使用多版本并发控制，读写互不阻塞。每行保存：            字段      含义                  xmin      创建该版本的事务 ID              xmax      删除或锁定该版本的事务 ID      查询根据快照判断版本可见性。与 MySQL InnoDB 不同，PostgreSQL 的旧版本保存在表中，需要由 VACUUM 清理。因此 PostgreSQL 生产运维必须关注：  死元组数量；  表膨胀；  autovacuum 配置；  长事务；  复制槽堆积；  未使用 prepared statement；  高频更新表。查看表统计：SELECT relname, n_live_tup, n_dead_tup, last_autovacuumFROM pg_stat_user_tablesORDER BY n_dead_tup DESC;1.5 WALWAL（Write-Ahead Logging）是 PostgreSQL 崩溃恢复和复制的基础。核心原则：修改数据页前，先写 WAL 日志WAL 提供：  崩溃恢复；  时间点恢复；  流复制；  逻辑复制；  归档备份。相关参数：            参数      说明                  wal_level      minimal / replica / logical              max_wal_size      WAL 上限控制              min_wal_size      WAL 下限              checkpoint_timeout      检查点间隔              synchronous_commit      提交是否等待 WAL 刷盘              archive_mode      是否归档              archive_command      归档命令      1.6 数据类型示例PostgreSQL 的类型系统是重要优势。CREATE TABLE app_users (    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,    email TEXT NOT NULL UNIQUE,    profile JSONB NOT NULL DEFAULT '{}'::jsonb,    tags TEXT[] NOT NULL DEFAULT '{}',    last_login_at TIMESTAMPTZ,    location GEOGRAPHY(POINT, 4326));JSONB 查询：SELECT id, profile -&gt;&gt; 'city' AS cityFROM app_usersWHERE profile @&gt; '{"level": "VIP"}';数组查询：SELECT idFROM app_usersWHERE 'admin' = ANY(tags);时间建议使用 TIMESTAMPTZ，明确保存带时区语义的时间。1.7 与 MySQL 的差异            维度      PostgreSQL      MySQL                  架构      多进程      多线程              存储引擎      统一存储引擎      Server 层 + 插件引擎              MVCC 旧版本      表内死元组，需 VACUUM      Undo 版本链              DDL      事务性较好      8.0 原子 DDL，细节不同              复制      物理和逻辑复制成熟      Binlog 复制生态广              扩展      扩展生态强      插件生态不同              复杂 SQL      强      持续增强              高并发热点更新      需要治理表膨胀      行锁和热点治理成熟      选择建议：  复杂 SQL、GIS、JSONB、多租户 RLS 优先考虑 PostgreSQL；  高并发互联网交易和团队生态成熟时 MySQL 常见；  不要只用“性能排名”选型，要看查询模型、运维能力和团队经验；  迁移成本包括 SQL、驱动、备份、监控、HA 和团队技能。1.8 生产红线  不允许长事务无限持有快照；  不允许关闭 autovacuum；  不允许忽略表膨胀；  不允许复制槽失效后无人处理；  不允许没有 WAL 归档监控；  不允许无备份演练；  不允许超权限用户连接业务；  不允许在主库跑无限制分析查询；  不允许随意 VACUUM FULL 高峰表；  不允许没有连接池。本章小结PostgreSQL 是功能完备的关系型数据库，采用多进程架构、MVCC、WAL 和扩展机制。它支持丰富数据类型和多种索引，适合复杂业务和数据密集场景。生产使用时必须重点治理死元组、长事务、连接数、WAL 和备份恢复。思考题  PostgreSQL 的 backend 进程和共享内存分别承担什么职责？  PostgreSQL MVCC 与 MySQL InnoDB 的旧版本存储方式有什么不同？  为什么长事务会导致表膨胀？  WAL 支撑哪些能力？  什么业务更适合 PostgreSQL 而不是 MySQL？</li>
</ul>
