Bytebase

Postgres 18 中值得开发者关注的新功能

Image
原文地址:https://www.bytebase.com/blog/what-is-new-in-postgres-18-for-developer

PostgreSQL 18 在 2025 年 9 月 25 日正式发布。毫无疑问,最具影响力的变更必定是全新的异步 I/O(AIO)子系统。不过,本文会聚焦那些影响开发者日常工作的功能,首先介绍的是原生 UUID v7 支持。

Image

原生支持 UUID v7

PostgreSQL 18 新增了uuidv7()函数,用来生成 UUID v7。几乎终结了关于主键应采用 SERIAL/IDENTITY 类型还是 UUID 的长期争论。UUID v7 兼具两者的优势:既保留了 UUID 的全局唯一性,又具备使序列键性能优异的顺序特性。
之前虽然也有扩展能做到,但主流的uuid-ossp并不支持 v7,而那些支持的扩展在很多发行版或云服务里并不默认安装。这导致不少团队只能在应用层自己实现 UUID v7 的生成逻辑。现在数据库原生支持后,使用成本大大降低。
-- 生成 UUIDv7SELECT uuidv7();-- 用 UUID v7 作为主键建表CREATE TABLE orders (    id UUID PRIMARY KEY DEFAULT uuidv7(),    customer_id INT,    total DECIMAL(10,2),    created_at TIMESTAMP DEFAULT NOW());
Image

VIRTUAL 生成列

PostgreSQL 在 12 版本时就支持了生成列,但当时只能用STORED,也就是写入时计算并保存到磁盘。这种方式不仅占空间,新增 STORED 列时还可能重写整张表。

PostgreSQL 18 引入了 VIRTUAL 选项,并将其设为生成列的默认行为。此类列在读取时而非写入时计算其值。这种更常见的做法使其成为默认设置,使 PostgreSQL 与其他主流数据库保持一致。

-- 用 virtual生成列建表 CREATE TABLE employees (    id SERIAL PRIMARY KEY,    first_name TEXT NOT NULL,    last_name TEXT NOT NULL,    salary DECIMAL(10,2),    -- 由于 VIRTUAL 是默认选项,您也可以跳过该选项    annual_salary DECIMAL(12,2) GENERATED ALWAYS AS (salary * 12) VIRTUAL);-- 插入数据(生成的列将自动计算)INSERT INTO employees (first_name, last_name, salary) VALUES    ('John', 'Doe', 5000.00),    ('Jane', 'Smith', 6500.00);SELECT first_name, last_name, salary, annual_salaryFROM employees;

这样做的好处:

  • 节省存储空间:对于无需物理存储的衍生数据,虚拟列可消除冗余磁盘占用。

  • 动态计算:某些计算在查询时执行更具优势,尤其当其依赖当前系统状态时。

另一方面,对于极少变更的密集型计算,存储生成列仍具价值 —— 此时重复计算的性能开销远大于存储成本。
但需注意提交说明中提及的限制:最显著的是无法在虚拟生成列上创建索引。
另外,PostgreSQL 18 还新增了逻辑复制中对生成列的支持(参见提交说明与文档)。此功能同样仅适用于存储型生成列。
Image

RETURNING 子句支持 OLD/NEW 值

PostgreSQL 18 扩展了RETURNING子句,现在可以同时返回修改前(OLD)和修改后(NEW)的行值。这样写审计日志就简单多了。

UPDATE 操作

-- 新价格并同时查看新旧数值UPDATE productsSET price = price * 1.10WHERE price <= 99.99RETURNING    name,    old.price AS old_price,    new.price AS new_price,    new.price - old.price AS price_change;

带有 ON CONFLICT 的插入

-- 带变更跟踪的插入或更新操作INSERT INTO products (name, price) VALUES ('Widget', 25.00)ON CONFLICT (name) DO UPDATE SET price = EXCLUDED.priceRETURNING    name,    old.price AS previous_price,    new.price AS current_price,    (old.price IS NULL) AS is_new_record;

删除操作

-- 追踪已删除内容DELETE FROM productsWHERE price < 10.00RETURNING    old.name AS deleted_product,    old.price AS deleted_price;
Image

EXPLAIN ANALYZE 默认显示 BUFFERS

PostgreSQL 18 改进了 EXPLAIN 命令,可自动提供更详细的查询执行信息。
EXPLAIN ANALYZE 现默认包含缓冲区使用信息,无需再使用 BUFFERS 选项。此默认行为有助于开发人员发现原本可能被忽略的 I/O 性能问题:
postgres=# EXPLAIN ANALYZE SELECT*FROM users WHERE age >25;-- PostgreSQL 18:自动包含 BUFFERS Seq Scan on users  (cost=0.00..18.75rows=125 width=64) (actual time=0.029..0.087rows=178 loops=1)   Filter: (age >25)   Rows Removed byFilter: 89   Buffers: shared hit=12 Planning:   Buffers: shared hit=156 read=3   I/O Timings: shared read=0.024 Planning Time: 0.512 ms Execution Time: 0.734 ms

pg_get_acl():权限检查更方便

新版本增加了pg_get_acl()函数,用于统一查看数据库对象的权限(ACL)。若您曾耗费大量时间调试臭名昭著的 ERROR 42501: 权限被拒绝错误信息,便会深切体会到统一检查对象权限方式的价值。

过去如果遇到permission denied错误,不同对象类型要查不同的系统表( pg_class、pg_proc、pg_namespace 等),非常麻烦。现在一个函数就能搞定。

postgres=# SELECT    (pg_identify_object(s.classid,s.objid,s.objsubid)).*,    pg_catalog.pg_get_acl(s.classid,s.objid,s.objsubid) AS aclFROM pg_catalog.pg_shdepend AS sJOIN pg_catalog.pg_database AS d    ON d.datname = current_database() AND       d.oid = s.dbidJOIN pg_catalog.pg_authid AS a    ON a.oid = s.refobjid AND       s.refclassid = 'pg_authid'::regclassWHERE s.deptype = 'a';-[ RECORD 1 ]-----------------------------------------type     | tableschema   | publicname     | testtabidentity | public.testtabacl      | {postgres=arwdDxtm/postgres,foo=r/postgres}

这对开发者调试权限问题很有帮助,对自动化系统或 AI agent 来说也更加友好。


延展阅读

DBA 视角:Postgres 18 新版本,带来了哪些变化?

Image

DBA 视角:Postgres 18 新版本,带来了哪些变化?

Bytebase 3.10.1 - 支持项目键值标签

如何在Reddit发布技术产品,单帖2万+阅读的复盘

日本增长史上最快的 AI SaaS 平台 LayerX 如何在云端实现「无痛」治理与安全ImageImageImage