PostgreSQL笔记3:从学术项目到开源数据库标杆——PostgreSQL发展历程回顾

📅 2026/8/16 3:28:32
PostgreSQL笔记3:从学术项目到开源数据库标杆——PostgreSQL发展历程回顾
纲要PostgreSQL发展历史INGRES项目1977–1985Michael Stonebraker 教授QUEL查询语言POSTGRES项目1986–1994论文The Design of Postgres1986面向对象特性与规则系统Postgres951994–1995Andrew Yu 与 Jolly ChenQUEL替换为SQLPostgreSQL开源项目1996至今1996年7月 CVS 仓库建立1997年1月PostgreSQL 6.0发布Bruce Momjian 加入版本演进关键特性9.6 ~ 18并行查询、逻辑复制、存储过程、分区、MERGE、AIO开源社区与BSD许可证引言PostgreSQL被誉为全球最先进的开源关系型数据库其发展历程跨越了三十余年。从一个加州大学伯克利分校的学术研究项目到如今成为企业级应用的事实标准PostgreSQL的演进史是一部历久弥新的技术史诗。理解这段历史有助于深入把握PostgreSQL的技术基因与生态优势。INGRES一切的起点1977–1985PostgreSQL的故事始于 1977 年。加州大学伯克利分校的 Michael Stonebraker 教授与 Eugene Wong 共同启动了INGRESInteractiveGraphics andREtrievalSystem项目。该项目从关系代数理论中获得启发开发了一款早期的关系型数据库系统。INGRES提出了自己的查询语言——QUELQuery Language。这一时期奠定了伯克利分校在数据库研究领域的领先地位也为后续的POSTGRES项目积累了宝贵的实践经验。POSTGRES下一代数据库的探索1986–1994在INGRES成功商业化之后Stonebraker 教授回到伯克利着手设计新一代数据库系统。1986 年他与 Lawrence A. Rowe 发表了具有里程碑意义的论文The Design of Postgres。该论文阐述了POSTGRES的设计初衷解决INGRES在实践中遇到的局限性整合科研应用与传统商业数据库处理的需求提供比当时数据库更加灵活和可扩展的架构。POSTGRES这一名称意为Post-Ingres即超越INGRES的下一代数据库。POSTGRES引入了诸多在当时极为超前的设计理念面向对象思维支持更复杂的数据类型可扩展机制允许用户自定义数据类型、函数和操作符规则系统提供了强大的查询重写能力Postgres95SQL 的引入与开源萌芽1994–19951994 年伯克利实验室因经费问题正式关闭了POSTGRES项目。然而两位研究生Andrew Yu和Jolly Chen将POSTGRES 4.2的代码搬回宿舍将原有的QUEL查询语言替换为SQL创建了Postgres95。这一举措具有深远的历史意义。SQL已于 1992 年发布国际标准SQL92成为数据库领域的事实标准。Postgres95的诞生使该项目从学术研究走向了更广阔的应用场景。PostgreSQL开源社区的崛起1996至今正式更名与开源协作的奠基1996 年Postgres95正式更名为PostgreSQL以突出其对SQL的支持。同年 7 月 8 日Marc Fournier 将代码提交至新架设的 CVS 仓库标志着PostgreSQL开源项目的正式启动。项目采用了类BSD的宽松开源许可证允许自由使用、修改和商业化分发。1996 年 7 月Bruce Momjian主动承担起 Bug Tracker 的角色成为社区的核心维护者之一。1997 年 1 月 29 日PostgreSQL 6.0正式发布。来自美国、加拿大、俄罗斯、日本等国的开发者陆续加入社区迅速扩大。版本演进的关键里程碑PostgreSQL自 1997 年起进入了持续迭代、稳步发展的长周期。以下列举从 9.6 版本以来的关键特性演进版本发布时间关键特性9.62016-09-29并行查询、同步复制、流复制、物化视图102017-10-05原生逻辑复制、声明式分区112018-10-18存储过程PROCEDURE支持122019-10-03哈希分区、table access method表访问方法132020-09-24增量排序、分区索引优化142021-09-30海量连接场景下的吞吐量大幅优化152022-10-13MERGE命令、逻辑复制行过滤与列过滤162023-09-14备库并行逻辑复制回放、libpq负载均衡172024-09-26VACUUM内存管理重构内存消耗降低 20 倍、JSON_TABLE182025-09-25异步 I/OAIO子系统、uuidv7()、B-tree 跳跃扫描、pg_upgrade统计信息保留PostgreSQL 18 核心特性解析PostgreSQL 18于 2025 年 9 月 25 日发布。其中最具突破性的特性是异步 I/OAIO子系统。AIO解决的问题此前PostgreSQL依赖操作系统的预读readahead机制加速数据检索。然而操作系统无法感知数据库特有的访问模式难以准确预测所需数据。AIO的解决方案PostgreSQL 18允许并发发出多个 I/O 请求无需等待每个请求顺序完成。支持的 I/O 操作包括顺序扫描sequential scan位图堆扫描bitmap heap scanVACUUM操作性能提升基准测试显示在特定场景下性能提升高达3 倍。配置方式新增io_method参数支持worker、io_uring和sync三种模式。此外PostgreSQL 18还引入了以下重要特性uuidv7()函数提供更好的索引局部性与读取性能B-tree 跳跃扫描skip scan优化多列索引查询虚拟生成列virtual generated columns查询时动态计算值OAuth 2.0 身份验证简化与单点登录SSO系统的集成pg_upgrade增强保留查询规划器统计信息加速升级后性能恢复支持--jobs并行检查与--swap目录交换PostgreSQL 的成功密码PostgreSQL能够从一个学术项目成长为全球顶级的开源数据库其成功可归结为以下几个核心因素类BSD开源许可证允许自由使用、修改和商业化分发极大促进了生态繁荣全球协作的社区以技术为导向吸引大量顶级开发者和企业参与保持谦逊务实的风格持续专注的技术方向三十余年专注于可靠性、可维护性和可扩展性逐步拓展到时序、地理信息、向量等新领域API 速览1.VACUUM所属PostgreSQL内建命令语法VACUUM[(option[,...])][table_and_columns[,...]]参数FULL回收更多空间但需排他锁FREEZE冻结事务 IDVERBOSE输出详细报告ANALYZE同时更新统计信息DISABLE_PAGE_SKIPPING禁用页面跳过示例-- 标准清理VACUUM my_table;-- 带分析VACUUMANALYZEmy_table;-- 详细输出VACUUM VERBOSE my_table;2.MERGE所属PostgreSQL 15语法MERGEINTOtarget_tableAStargetUSINGsource_tableASsourceONmerge_conditionWHENMATCHEDTHENUPDATESETcolumn1value1,...WHENNOTMATCHEDTHENINSERT(column1,column2,...)VALUES(value1,value2,...);示例MERGEINTOproductsAStUSINGupdatesASsONt.product_ids.product_idWHENMATCHEDTHENUPDATESETprices.price,updated_atCURRENT_TIMESTAMPWHENNOTMATCHEDTHENINSERT(product_id,name,price)VALUES(s.product_id,s.name,s.price);3.pg_upgrade所属PostgreSQL配套工具语法pg_upgrade-boldbindir-Bnewbindir-dolddatadir-Dnewdatadir[options]常用选项-j/--jobs并行检查的作业数PostgreSQL 18--swap交换目录而非复制PostgreSQL 18--check仅执行检查不实际升级示例# PostgreSQL 18 升级保留统计信息pg_upgrade-b/usr/lib/postgresql/17/bin\-B/usr/lib/postgresql/18/bin\-d/var/lib/postgresql/17/data\-D/var/lib/postgresql/18/data\--jobs44.io_methodPostgreSQL 18所属postgresql.conf配置参数可选值sync保持原有同步 I/O 行为worker使用工作进程执行异步 I/Oio_uring使用 Linuxio_uring接口配置示例# postgresql.conf io_method io_uring5.uuidv7()PostgreSQL 18所属pgcrypto扩展语法uuidv7()示例CREATEEXTENSION pgcrypto;SELECTuuidv7();-- 输出0193a2b0-xxxx-xxxx-xxxx-xxxxxxxxxxxx-- 作为主键默认值CREATETABLEorders(id UUIDPRIMARYKEYDEFAULTuuidv7(),created_atTIMESTAMPDEFAULTCURRENT_TIMESTAMP);Demo 简单示例运行说明本 Demo 使用 Node.js 和node-postgrespg驱动演示PostgreSQL 18的部分核心特性。环境要求Node.js 18PostgreSQL 18pg库安装依赖npminit-ynpminstallpg启动 PostgreSQLpg_ctl-D/path/to/data-llogfile start代码说明import{Client}frompg;constclientnewClient({host:localhost,port:5432,database:testdb,user:postgres,password:yourpassword,});asyncfunctiondemo(){awaitclient.connect();// 1. 创建扩展与表使用 uuidv7awaitclient.query(CREATE EXTENSION IF NOT EXISTS pgcrypto;);awaitclient.query(CREATE TABLE IF NOT EXISTS orders ( id UUID PRIMARY KEY DEFAULT uuidv7(), order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, amount DECIMAL(10, 2) ););// 2. 插入数据awaitclient.query(INSERT INTO orders (amount) VALUES (100.50), (250.00), (75.25), (300.75););// 3. 查询数据constresawaitclient.query(SELECT * FROM orders ORDER BY order_date;);console.log(Orders:,res.rows);// 4. 使用 MERGEPostgreSQL 15awaitclient.query(MERGE INTO orders AS t USING (VALUES (0193a2b0-0000-0000-0000-000000000001, 150.00)) AS s(id, amount) ON t.id s.id::UUID WHEN MATCHED THEN UPDATE SET amount s.amount WHEN NOT MATCHED THEN INSERT (id, amount) VALUES (s.id::UUID, s.amount););// 5. 查看 VACUUM 效果awaitclient.query(VACUUM ANALYZE orders;);awaitclient.end();}demo().catch(console.error);对应的 PostgreSQL 原生指令-- 创建扩展CREATEEXTENSIONIFNOTEXISTSpgcrypto;-- 创建表CREATETABLEIFNOTEXISTSorders(id UUIDPRIMARYKEYDEFAULTuuidv7(),order_dateTIMESTAMPDEFAULTCURRENT_TIMESTAMP,amountDECIMAL(10,2));-- 插入数据INSERTINTOorders(amount)VALUES(100.50),(250.00),(75.25),(300.75);-- 查询SELECT*FROMordersORDERBYorder_date;-- MERGE 操作MERGEINTOordersAStUSING(VALUES(0193a2b0-0000-0000-0000-000000000001,150.00))ASs(id,amount)ONt.ids.id::UUIDWHENMATCHEDTHENUPDATESETamounts.amountWHENNOTMATCHEDTHENINSERT(id,amount)VALUES(s.id::UUID,s.amount);-- VACUUM 分析VACUUMANALYZEorders;技术点总结技术点说明适用版本uuidv7()时间排序友好的 UUID 生成PostgreSQL 18MERGE条件合并UPSERTPostgreSQL 15VACUUM ANALYZE回收空间并更新统计信息所有版本pg驱动Node.js 连接 PostgreSQL 的官方驱动—补充 Demo 示例Go、Python、Java以下分别提供 Go、Python 和 Java 的完整示例功能与 Node.js 版本一致连接 PostgreSQL 18创建表使用uuidv7()默认值插入数据执行MERGE并调用VACUUM ANALYZE。所有示例均包含运行说明与关键依赖。1. Go使用pgx驱动运行说明安装 Go 1.21安装依赖go get github.com/jackc/pgx/v5修改连接字符串中的用户名、密码、数据库名运行go run main.go代码示例packagemainimport(contextfmtloggithub.com/jackc/pgx/v5)funcmain(){ctx:context.Background()// 连接字符串connStr:postgres://postgres:yourpasswordlocalhost:5432/testdbconn,err:pgx.Connect(ctx,connStr)iferr!nil{log.Fatal(err)}deferconn.Close(ctx)// 1. 创建扩展和表使用 uuidv7_,errconn.Exec(ctx,CREATE EXTENSION IF NOT EXISTS pgcrypto;)iferr!nil{log.Fatal(err)}_,errconn.Exec(ctx, CREATE TABLE IF NOT EXISTS orders ( id UUID PRIMARY KEY DEFAULT uuidv7(), order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, amount DECIMAL(10, 2) ); )iferr!nil{log.Fatal(err)}// 2. 插入数据_,errconn.Exec(ctx, INSERT INTO orders (amount) VALUES (100.50), (250.00), (75.25), (300.75); )iferr!nil{log.Fatal(err)}// 3. 查询数据rows,err:conn.Query(ctx,SELECT id, order_date, amount FROM orders ORDER BY order_date;)iferr!nil{log.Fatal(err)}deferrows.Close()fmt.Println(Orders:)forrows.Next(){varidstringvarorderDatestringvaramountfloat64iferr:rows.Scan(id,orderDate,amount);err!nil{log.Fatal(err)}fmt.Printf(ID: %s, Date: %s, Amount: %.2f\n,id,orderDate,amount)}// 4. MERGE 操作_,errconn.Exec(ctx, MERGE INTO orders AS t USING (VALUES (0193a2b0-0000-0000-0000-000000000001, 150.00)) AS s(id, amount) ON t.id s.id::UUID WHEN MATCHED THEN UPDATE SET amount s.amount WHEN NOT MATCHED THEN INSERT (id, amount) VALUES (s.id::UUID, s.amount); )iferr!nil{log.Fatal(err)}// 5. VACUUM ANALYZE_,errconn.Exec(ctx,VACUUM ANALYZE orders;)iferr!nil{log.Fatal(err)}fmt.Println(Demo completed successfully.)}2. Python使用psycopg2运行说明安装 Python 3.9安装依赖pip install psycopg2-binary修改连接参数host, port, dbname, user, password运行python main.py代码示例importpsycopg2frompsycopg2importsqldefmain():# 连接参数connpsycopg2.connect(hostlocalhost,port5432,dbnametestdb,userpostgres,passwordyourpassword)conn.autocommitTruecurconn.cursor()# 1. 创建扩展和表cur.execute(CREATE EXTENSION IF NOT EXISTS pgcrypto;)cur.execute( CREATE TABLE IF NOT EXISTS orders ( id UUID PRIMARY KEY DEFAULT uuidv7(), order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, amount DECIMAL(10, 2) ); )# 2. 插入数据cur.execute( INSERT INTO orders (amount) VALUES (100.50), (250.00), (75.25), (300.75); )# 3. 查询数据cur.execute(SELECT id, order_date, amount FROM orders ORDER BY order_date;)print(Orders:)forrowincur.fetchall():print(fID:{row[0]}, Date:{row[1]}, Amount:{row[2]})# 4. MERGE 操作cur.execute( MERGE INTO orders AS t USING (VALUES (0193a2b0-0000-0000-0000-000000000001, 150.00)) AS s(id, amount) ON t.id s.id::UUID WHEN MATCHED THEN UPDATE SET amount s.amount WHEN NOT MATCHED THEN INSERT (id, amount) VALUES (s.id::UUID, s.amount); )# 5. VACUUM ANALYZEcur.execute(VACUUM ANALYZE orders;)cur.close()conn.close()print(Demo completed successfully.)if__name____main__:main()3. Java使用 JDBC运行说明安装 JDK 17添加 PostgreSQL JDBC 驱动依赖如使用 Maven 或 GradleMavendependencygroupIdorg.postgresql/groupIdartifactIdpostgresql/artifactIdversion42.7.2/version/dependency修改连接 URL、用户名、密码编译运行javac Main.java java Main代码示例importjava.sql.*;importjava.util.UUID;publicclassMain{publicstaticvoidmain(String[]args){Stringurljdbc:postgresql://localhost:5432/testdb;Stringuserpostgres;Stringpasswordyourpassword;try(ConnectionconnDriverManager.getConnection(url,user,password)){conn.setAutoCommit(true);Statementstmtconn.createStatement();// 1. 创建扩展和表stmt.execute(CREATE EXTENSION IF NOT EXISTS pgcrypto;);stmt.execute( CREATE TABLE IF NOT EXISTS orders ( id UUID PRIMARY KEY DEFAULT uuidv7(), order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, amount DECIMAL(10, 2) ); );// 2. 插入数据stmt.execute( INSERT INTO orders (amount) VALUES (100.50), (250.00), (75.25), (300.75); );// 3. 查询数据ResultSetrsstmt.executeQuery(SELECT id, order_date, amount FROM orders ORDER BY order_date;);System.out.println(Orders:);while(rs.next()){System.out.printf(ID: %s, Date: %s, Amount: %.2f%n,rs.getString(id),rs.getTimestamp(order_date),rs.getDouble(amount));}rs.close();// 4. MERGE 操作stmt.execute( MERGE INTO orders AS t USING (VALUES (0193a2b0-0000-0000-0000-000000000001, 150.00)) AS s(id, amount) ON t.id s.id::UUID WHEN MATCHED THEN UPDATE SET amount s.amount WHEN NOT MATCHED THEN INSERT (id, amount) VALUES (s.id::UUID, s.amount); );// 5. VACUUM ANALYZEstmt.execute(VACUUM ANALYZE orders;);stmt.close();System.out.println(Demo completed successfully.);}catch(SQLExceptione){e.printStackTrace();}}}以上三个示例均实现了与 Node.js 版本相同的逻辑可直接编译运行并针对PostgreSQL 18的uuidv7()和MERGE特性进行了验证。运行前请确保 PostgreSQL 服务已启动并创建了相应的测试数据库。官方文档PostgreSQL 官方文档PostgreSQL 18 发布说明PostgreSQL 18 新闻资料包PostgreSQL Wiki参考链接PostgreSQL 版本发布历史PostgreSQL: Past, Present, and Future - Bruce MomjianThe Design of Postgres - Stonebraker Rowe, SIGMOD 1986总结PostgreSQL从 1977 年的INGRES项目起步历经POSTGRES1986的学术探索、Postgres951994的SQL转型最终于 1996 年以开源项目PostgreSQL的身份走向全球。三十余年间PostgreSQL社区以每年一个大版本的节奏持续迭代从 9.6 的并行查询到 18 的异步 I/O每一次演进都体现了对可靠性、可扩展性和性能的执着追求。类BSD的开源许可、全球协作的社区文化以及持续专注的技术方向共同铸就了PostgreSQL作为开源数据库事实标准的地位。