Skip to content

PostgreSQL 是什么 ​

本节介绍 PostgreSQL 的基本定位、核心特性以及它与编程语言的区别。学完本节,你将理解 PostgreSQL 是什么、它凭什么成为"世界上最先进的开源数据库",以及为什么值得把它作为你的主力数据库。

前置知识

无需前置知识,适合零基础读者。只要你用过电脑、听说过"数据库"这个词,本节就是你的起点。

基础概念 ​

PostgreSQL 是一个开源的对象关系型数据库管理系统(ORDBMS, Object-Relational Database Management System)。它诞生于 1986 年加州大学伯克利分校的 POSTGRES 项目,经过三十多年持续开发,如今由全球开发者社区共同维护。

要理解这个定义,需要拆成三个关键词:

关键词含义对使用者的意义
开源(Open Source)源码公开,采用 PostgreSQL 许可证(类似 BSD/MIT)免费商用、可自由修改、无厂商锁定
对象关系型(Object-Relational)在关系模型基础上支持自定义类型、继承、函数等对象特性能用 SQL 表达更复杂的业务模型
数据库管理系统(DBMS)提供数据存储、查询、事务、权限、备份等完整能力一个软件覆盖从建库到运维的全流程

官方对它的一句话定位是:"The World's Most Advanced Open Source Relational Database"(世界上最先进的开源关系型数据库)。这句话并非纯营销——PostgreSQL 在 SQL 标准兼容度、数据类型丰富度、扩展能力上确实长期领先同类开源产品。

在 PostgreSQL 中,有几个术语你需要尽早建立印象:

  • 关系(Relation):就是"表"。PostgreSQL 内部把表、索引、视图统称为关系,系统目录里也用这个词。
  • 元组(Tuple):就是"一行记录"。在文档和系统视图中,行(Row)与元组(Tuple)经常混用。
  • 模式(Schema):数据库内部的命名空间,用于把表分组,类似操作系统里的文件夹。
  • 系统目录(System Catalog):PostgreSQL 把自身的元数据(有哪些表、哪些列、哪些用户)也存成表,放在 pg_catalog 模式中。
  • 集群(Cluster):一个 initdb 初始化的数据目录 + 一个正在运行的服务进程,可包含多个数据库。

语法与用法 ​

PostgreSQL 的使用入口是 SQL(Structured Query Language,结构化查询语言)。你通过客户端发送 SQL 语句,服务端解析、执行并返回结果。最基础的交互链路如下:

bash
# 1. 连接到本地 PostgreSQL 服务(默认端口 5432,默认用户 postgres)
psql -h 127.0.0.1 -p 5432 -U postgres -d demo

# 2. 连接成功后会出现提示符,直接输入 SQL
# demo=#
sql
-- 3. 查询服务端版本,确认你连的是哪个版本
SELECT version();

-- 4. 查询当前数据库名与当前用户
SELECT current_database(), current_user;

-- 5. 查看数据库里有哪些表
SELECT table_schema, table_name
FROM information_schema.tables
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
ORDER BY table_schema, table_name;

SQL 语句的基本骨架由若干子句(Clause)组成,书写顺序固定:

sql
-- 查询语句的通用骨架,[] 内为可选
SELECT   column_list          -- 要返回哪些列
FROM     table_name           -- 从哪张表取数
WHERE    condition            -- 过滤哪些行
GROUP BY column_list          -- 按什么分组
HAVING   group_condition      -- 过滤哪些分组
ORDER BY column_list          -- 结果如何排序
LIMIT    n;                   -- 最多返回几行

除了标准 SQL,PostgreSQL 还提供psql 元命令(Meta-Command),它们以反斜杠开头,不是 SQL,而是客户端自己的指令:

text
\?        查看所有元命令帮助
\l        列出所有数据库
\c demo   切换到 demo 数据库
\dt       列出当前模式下的表
\d users  查看 users 表的结构
\dn       列出所有模式
\du       列出所有角色
\q        退出 psql

详细说明 ​

开源与许可证 ​

PostgreSQL 采用 PostgreSQL License,这是一个与 BSD/MIT 同类的宽松许可证。它的要点是:

许可要点说明
免费使用商业项目、闭源项目均可免费使用
可修改分发可以修改源码并分发,但需保留版权声明
无 copyleft你的衍生作品不必开源
无专利条款风险不像某些许可证附带专利授权限制

这与 MySQL 的 GPL/商业双许可、Oracle 的专有许可形成鲜明对比。对企业而言,最直接的好处是:没有按 CPU/核心数计费的风险,也没有因厂商变更许可策略而被迫迁移的风险。

对象关系型:不只是"表 + 行" ​

传统关系型数据库只认"表、行、列、基本类型"。PostgreSQL 在此之上引入了对象特性:

sql
-- 自定义复合类型(Composite Type),可当作列类型使用
CREATE TYPE address AS (
  province TEXT,
  city     TEXT,
  street   TEXT
);

CREATE TABLE users (
  id      BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  name    TEXT NOT NULL,
  addr    address              -- 直接使用自定义类型作为列类型
);

INSERT INTO users (name, addr)
VALUES ('张三', ROW('广东省', '深圳市', '科技园路 1 号'));

-- 用括号访问复合类型的字段
SELECT name, (addr).city AS city FROM users;
sql
-- 表继承(Table Inheritance):子表自动继承父表的列
CREATE TABLE events (
  id         BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE login_events (
  user_id BIGINT NOT NULL
) INHERITS (events);

-- 查询父表会默认包含子表的数据
SELECT * FROM events;

还有自定义函数与操作符,你可以为新类型定义属于自己的 +、-> 甚至全新的操作符:

sql
-- 定义一个新操作符,计算两个区间的重叠长度
CREATE FUNCTION overlap_len(a INT4RANGE, b INT4RANGE)
RETURNS INT
LANGUAGE SQL IMMUTABLE AS $$
  SELECT GREATEST(
    0,
    LEAST(upper(a) - 1, upper(b) - 1) - GREATEST(lower(a), lower(b)) + 1
  );
$$;

CREATE OPERATOR <-> (
  LEFTARG  = INT4RANGE,
  RIGHTARG = INT4RANGE,
  FUNCTION = overlap_len
);

SELECT '[1,10]'::int4range <-> '[5,20]'::int4range AS overlap;  -- 6

此外,PostgreSQL 允许用 C、PL/pgSQL、PL/Python、PL/Perl 等语言编写扩展(Extension),以 CREATE EXTENSION 一键安装。这使得它能在同一个进程内同时承担多种角色——这也是下一节《PostgreSQL 能做什么》的主题。

标准兼容性 ​

PostgreSQL 对 SQL 标准的兼容度在开源数据库中名列前茅,核心标准特性基本全覆盖:

SQL 标准能力PostgreSQL 支持情况
事务与隔离级别完整支持 READ COMMITTED / REPEATABLE READ / SERIALIZABLE
窗口函数完整支持,含 ROWS / RANGE / GROUPS 帧
公共表表达式(CTE)支持 WITH,含递归 WITH RECURSIVE
JSON 支持支持 SQL/JSON 标准(JSON_TABLE 等,PostgreSQL 17+ 完善)
分组集合支持 GROUPING SETS / CUBE / ROLLUP
外键与约束支持 CHECK / EXCLUDE / 延迟约束
MERGE 语句PostgreSQL 15+ 支持标准 MERGE

这意味着你在 MySQL、Oracle、SQL Server 里学到的标准 SQL 知识,大部分可以直接迁移过来;反过来,写过 PostgreSQL SQL 的人通常也能较快适应其他数据库。当然,各家的方言(Dialect)差异依然存在,比如自增列、字符串拼接、日期函数就不统一。

与编程语言的区别 ​

这是初学者最容易混淆的地方。PostgreSQL 不是编程语言,它是数据库系统;SQL 才是你与它对话的语言。

对比维度PostgreSQL(数据库系统)编程语言(PHP、Python、Go 等)
本质长期运行的服务进程 + 数据文件一段被执行的代码
职责存储、检索、保证数据一致性实现业务逻辑、编排流程
状态有状态,数据持久保存通常无状态,进程结束即消失
并发内建多用户并发与事务隔离需自行处理并发与锁
语言SQL(声明式)命令式 / 函数式
部署需要初始化、调优、备份随应用部署
失效影响数据丢失,影响全局单次请求失败

关键区别在于声明式(Declarative)与命令式(Imperative):

sql
-- SQL 是声明式的:你描述"要什么",不描述"怎么取"
-- 至于走全表扫描还是索引扫描,由查询优化器决定
SELECT name, email
FROM users
WHERE city = '深圳市' AND created_at >= '2026-01-01';
php
// 编程语言是命令式的:你逐步描述"怎么做"
// 需要自己写循环、判断、累加
$result = [];
foreach ($users as $user) {
  if ($user['city'] === '深圳市' && $user['created_at'] >= '2026-01-01') {
    $result[] = ['name' => $user['name'], 'email' => $user['email']];
  }
}

但两者并非割裂。PostgreSQL 支持服务端编程,你可以把逻辑写进数据库,由数据库直接执行,减少网络往返:

sql
-- PL/pgSQL 是 PostgreSQL 内置的过程化语言
CREATE FUNCTION user_level(total_amount NUMERIC)
RETURNS TEXT
LANGUAGE plpgsql IMMUTABLE AS $$
BEGIN
  IF total_amount >= 100000 THEN
    RETURN '钻石';
  ELSIF total_amount >= 10000 THEN
    RETURN '黄金';
  ELSE
    RETURN '普通';
  END IF;
END;
$$;

SELECT user_level(50000);  -- 黄金

为什么选择 PostgreSQL ​

综合来看,选择 PostgreSQL 的常见理由有以下几条:

理由具体说明
功能完备一个数据库覆盖 OLTP、JSON、全文搜索、地理空间、向量检索等场景
可靠性高完整的 WAL 日志、崩溃恢复、复制机制,社区口碑"从不丢数据"
扩展性强扩展(Extension)机制让你按需装载能力,而不必更换数据库
标准兼容SQL 标准支持好,知识可迁移、代码可移植
社区活跃每年一个大版本,全球核心团队 + 数百贡献者,非单一公司控制
生态成熟云厂商全部支持,ORM、监控、备份工具齐全
成本可控无许可证费用,云上 RDS 定价通常低于同等商业数据库

反过来,PostgreSQL 也并非没有代价:它的默认配置偏保守,需要按硬件调优;单表超大时的运维(VACUUM、膨胀)比某些数据库更需关注;连接数模型是进程模型,高并发短连接场景需要配合 PgBouncer 之类的连接池。这些在后续章节都会展开。

实战示例 ​

下面用一个完整的小案例,把"建库 → 建表 → 插入 → 查询"跑通,让你对 PostgreSQL 的工作方式有直观感受。

bash
# 第 1 步:创建并连接数据库(在 psql 里执行)
createdb -h 127.0.0.1 -U postgres demo
psql -h 127.0.0.1 -U postgres -d demo
sql
-- 第 2 步:建表。用 IDENTITY 生成自增主键(PostgreSQL 10+ 的推荐写法)
CREATE TABLE products (
  id          BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  sku         TEXT           NOT NULL UNIQUE,          -- 商品编码,唯一
  name        TEXT           NOT NULL,                 -- 商品名称
  price       NUMERIC(10, 2) NOT NULL CHECK (price >= 0),
  stock       INTEGER        NOT NULL DEFAULT 0,
  tags        JSONB          NOT NULL DEFAULT '[]',    -- 标签,用 JSONB 存
  created_at  TIMESTAMPTZ    NOT NULL DEFAULT now()
);

-- 第 3 步:插入三条测试数据
INSERT INTO products (sku, name, price, stock, tags)
VALUES
  ('SKU-1001', '机械键盘', 399.00, 120, '["数码", "外设"]'),
  ('SKU-1002', '人体工学椅', 1299.00, 35, '["家具", "办公"]'),
  ('SKU-1003', '显示器支架', 199.00, 200, '["数码", "配件"]');

-- 第 4 步:查询价格在 200 到 1500 之间的商品,按价格降序
SELECT id, sku, name, price, stock
FROM products
WHERE price BETWEEN 200 AND 1500
ORDER BY price DESC;

预期输出:

text
 id |   sku    |    name    |  price  | stock
----+----------+------------+---------+-------
  2 | SKU-1002 | 人体工学椅 | 1299.00 |    35
  1 | SKU-1001 | 机械键盘   |  399.00 |   120
(2 rows)

再进一步,不用改表结构就能直接查 JSONB 字段里的内容:

sql
-- 查询带有 "数码" 标签的商品(@> 是 JSONB 的包含操作符)
SELECT name, tags
FROM products
WHERE tags @> '["数码"]'::jsonb
ORDER BY name;
text
    name    |        tags
------------+--------------------
 显示器支架 | ["数码", "配件"]
 机械键盘   | ["数码", "外设"]
(2 rows)

最后一次数据库层面的验证:

sql
-- 确认版本、当前库、当前用户
SELECT version() AS ver,
       current_database() AS db,
       current_user AS usr;
text
            ver                          |  db  |   usr
-----------------------------------------+------+----------
 PostgreSQL 18.6 on x86_64-pc-linux-gnu  | demo | postgres
(1 row)

注意事项 ​

  • PostgreSQL 是服务端,不是嵌入式库。它必须作为独立进程运行,客户端通过网络或 Unix Socket 连接,这一点与 SQLite 有本质区别。
  • 不要用 SELECT * 做线上查询。列顺序变化或新增列都会直接影响下游程序,应显式列出所需列。
  • 字符串必须用单引号。双引号在 SQL 里表示标识符(表名/列名),写错会报 column "xxx" does not exist。
sql
-- 错误!双引号会被当成列名,而非字符串
SELECT * FROM users WHERE name = "张三";

-- 正确:使用单引号表示字符串字面量
SELECT * FROM users WHERE name = '张三';
  • 标识符大小写敏感规则容易踩坑。未加双引号的标识符会被折叠为小写,加了双引号则严格区分大小写。
sql
-- 不推荐:加了双引号,之后每次都必须写双引号
CREATE TABLE "Users" ("Id" BIGINT, "Name" TEXT);

-- 推荐:全部小写下划线,永不需要双引号
CREATE TABLE users (id BIGINT, name TEXT);
  • PostgreSQL 没有 AUTO_INCREMENT 关键字,自增应使用 GENERATED ... AS IDENTITY 或 BIGSERIAL。
sql
-- 错误!这是 MySQL 语法,PostgreSQL 不支持
CREATE TABLE t (id BIGINT AUTO_INCREMENT PRIMARY KEY);

-- 正确:PostgreSQL 10+ 推荐的标准写法
CREATE TABLE t (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY);
  • \d 这类元命令只在 psql 里有效,写进 SQL 文件或通过驱动执行会报语法错误。
  • 数据库名、用户名的默认权限模型较严格:新库默认只有属主能建表,跨用户访问需显式 GRANT。

最佳实践 ​

  • 优先选择受支持的稳定大版本(当前为 PostgreSQL 18),并保持小版本最新,以获得安全修复。
  • 标识符统一小写下划线风格(user_orders、created_at),避免大小写与保留字问题。
  • 主键统一用 BIGINT GENERATED ALWAYS AS IDENTITY,不用 SERIAL(SERIAL 是历史遗留语法,存在权限与序列归属的坑)。
  • 时间列统一用 TIMESTAMPTZ(带时区),不要用 TIMESTAMP,避免时区换算带来的隐性 bug。
  • 结构化字段用列,半结构化字段用 JSONB,不要把所有东西都塞进一个 JSON 列。
  • 金额用 NUMERIC 而非 FLOAT,FLOAT 存在二进制浮点误差。
  • 每个库都显式设置 search_path 或使用完整限定名,避免对象被错误模式解析。
  • 生产环境务必关闭 SELECT * 式的全表拉取,把过滤与聚合尽量放在数据库侧完成。

下一节

继续学习:PostgreSQL 能做什么

参考链接 ​