AI全栈项目 Day 3:不仅是数据库,更是你的“数据堡垒” —— PostgreSQL 硬核入门

哈喽,掘金的各位全栈练习生们!欢迎来到 AI全栈项目实战的第三天

在前两天的课程中,也许你已经搞定了前端的页面,或者搭建了简单的 Node.js 服务。但今天,我们要进入一个更加深邃、更加迷人,也绝对是"后端工程师"分水岭的领域 ------ 数据库

如果你觉得数据库只是用来"存个数据"的,那你就太小看它了。在 AI 全栈的架构中,数据库不仅是仓库,它是数据的堡垒 ,是业务逻辑的最终防线 。今天我们的主角,就是被誉为"世界上最先进的开源关系型数据库" ------ PostgreSQL(简称 Postgres 或 PG)。

准备好了吗?我们要开始"硬核"但"愉快"的旅程了!


🧐 为什么是关系型数据库 (RDBMS)?

在 NoSQL(如 MongoDB)大行其道的今天,为什么我们依然首选关系型数据库?

想象一下,你正在通过 Excel 管理一个学校。

  • 你有一张表叫"学生表"(Users)。
  • 你有一张表叫"文章表"(Posts)。
  • 你有一张表叫"评论表"(Comments)。

这些表不是孤立的。学生写文章,文章有评论,评论属于某个学生。这种数据与数据之间存在严密逻辑关联 的结构,就是关系型数据库的灵魂。

PostgreSQL 就是这样一个以二维表格(行 Row / 列 Column) 为基础,通过主键 (Primary Key)外键 (Foreign Key) 编织数据网络,并严格遵循 ACID 特性来保证数据"即使天塌下来也不会乱"的神器。


🛡️ 每一个全栈都必须烂熟于心的 ACID

在面试中,ACID 是必考题;在开发中,ACID 是保命符。我们结合一个经典的转账场景(A 转给 B 100元)来彻底搞懂它。

1. A - Atomicity(原子性)

"要么全部成功,要么玉石俱焚。" 事务(Transaction)是数据库操作的最小单位。

  • 场景
    1. 从 A 账户扣除 100 元。
    2. (突然停电了/报错了)
    3. 向 B 账户增加 100 元。
  • 如果没有原子性:A 的钱没了,B 也没收到,这 100 元凭空消失了!
  • PG 的做法 :如果第 2 步失败了,第 1 步的操作会立刻回滚(Rollback),仿佛一切从未发生过。

2. C - Consistency(一致性)

"能量守恒定律。" 数据库必须从一个"正确状态"变更为另一个"正确状态"。

  • 场景:转账前后,A 和 B 的账户总金额必须保持不变(比如一共 1000 元)。
  • PG 的做法:通过约束(Constraints)和触发器,确保数据不会违反你设定的规则(比如余额不能为负数)。

3. I - Isolation(隔离性)

"你干你的,我干我的。" 并发执行的事务之间互不干扰。

  • 场景:A 正在给 B 转 100 元,同时 C 也在给 B 转 200 元。
  • PG 的做法:数据库会通过锁机制或 MVCC(多版本并发控制),确保这两笔交易不会因为同时发生而导致 B 的余额算错。

4. D - Durability(持久性)

"落子无悔。" 事务一旦提交(Commit),对数据的改变就是永久的。

  • 场景:提示"转账成功"后,哪怕下一秒数据库服务器爆炸了,只要硬盘还在,数据就在。

🛠️ 实战:设计一个 AI 博客系统的数据库

Talk is cheap, show me the code. 我们来为博客系统设计两张最核心的表:用户表 (Users)文章表 (Posts)

第一步:用户表 (Users) ------ 身份的基石

我们需要存储用户的 ID、用户名、密码以及注册时间。

看看这段由资深 PG 工程师编写的 SQL:

sql 复制代码
CREATE TABLE users (
    -- 【重点 1】主键与自增
    -- id: 用户的唯一标识。
    -- BIGINT: 为什么不用 INT?INT 最大约 21 亿。对于大型互联网应用,BIGINT (8字节) 更安全,避免 ID 溢出。
    -- GENERATED BY DEFAULT AS IDENTITY: 这是 SQL 标准推荐的自增写法,比旧版的 SERIAL 更规范。
    -- PRIMARY KEY: 主键约束,意味着这个字段 1. 唯一 2. 不为空 3. 自带索引。
    id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    
    -- 【重点 2】唯一性与非空
    -- VARCHAR(255): 变长字符串,节省空间。
    -- NOT NULL: 必填项,防止出现"无名氏"。
    -- UNIQUE: 唯一约束。数据库会自动为 name 字段建立索引,保证查询速度,同时拒绝重复的用户名注册。
    name VARCHAR(255) NOT NULL UNIQUE,
    
    -- 【重点 3】密码安全
    -- 永远不要明文存储密码!
    -- 预留 255 长度是为了适配 bcrypt 等哈希加密算法生成的长字符串(如 $2b$10$...)。
    password VARCHAR(255) NOT NULL,
    
    -- 【重点 4】审计字段(高级工程师的素养)
    -- 记录这条数据是什么时候创建的,什么时候最后修改的。
    -- TIMESTAMPTZ: 带时区的时间戳 (Timestamp with time zone)。全球化应用必备,它会帮你处理夏令时和跨国时区问题。
    -- DEFAULT CURRENT_TIMESTAMP: 如果你不传值,默认填入当前时间。
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

🔍 深度解析:

  • 主键 (Primary Key):就像身份证号。它是词典的索引目录,查询速度极快。
  • 唯一索引 (Unique) :给 name 加上这个,不仅是为了不重名,更是为了在登录时通过用户名快速查找到用户(数据库不需要全表扫描,而是直接走索引树)。

第二步:文章表 (Posts) ------ 关系的建立

文章属于用户。这里涉及到了一对多关系(一个用户可以写多篇文章)。

sql 复制代码
CREATE TABLE posts (
    id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    
    -- 标题不能为空,长度限制适中
    title VARCHAR(255) NOT NULL,
    
    -- 内容是长文本,使用 TEXT 类型,没有长度限制,适合存 Markdown 源码
    content TEXT,
    
    -- 【重点 5】外键关联
    -- 这里存储的是 users 表里的 id。
    -- 注意加了双引号 "userId",因为 PG 默认会将未加引号的字段转为小写。如果你想保留驼峰命名,必须加双引号!
    "userId" BigInt NOT NULL,
    
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    
    -- 【重点 6】外键约束 (Foreign Key Constraint)
    -- CONSTRAINT fk_posts_user: 给这个约束起个名字,方便报错时定位。
    -- FOREIGN KEY ("userId") REFERENCES users(id): 声明 posts 表的 userId 引用自 users 表的 id。
    -- ON DELETE CASCADE: 级联删除。这非常关键!
    -- 意思是:如果"李白"这个用户被删除了,那么"李白"写的所有文章也会被自动删除。
    -- 如果没有这句,删除用户时数据库会报错,提示还有文章依赖于该用户。
    CONSTRAINT fk_posts_user FOREIGN KEY ("userId") REFERENCES users(id) ON DELETE CASCADE
);

🔍 深度解析:

  • 外键 (Foreign Key) :这是连接两张表的桥梁。posts.userId <==> users.id
  • 普通外键 vs 乱建 :外键不能乱建,虽然它保证了数据完整性(你不能给一个不存在的用户添加文章),但过多的外键会影响写入性能(每次插入都要去检查父表)。但在核心业务中,数据的一致性 > 极致的写入性能

🥗 数据填充 (Seeds)

表建好了,空荡荡的怎么办?我们需要"种子数据"来初始化数据库,方便测试。

1. 插入用户 (Users)

注意这里密码已经是加密后的乱码(模拟真实场景)。

sql 复制代码
INSERT INTO "users" ("id", "name", "password") VALUES
('1', '王皓', '$2b$10$CsO/ykedPpuxqUETBZTYm.F2U4TXDdo01rLmoRPwjKBv3pIL5pnWq'),
('2', '小雪', '$2b$10$CsO/ykedPpuxqUETBZTYm.F2U4TXDdo01rLmoRPwjKBv3pIL5pnWq'),
('3', '李白', '$2b$10$CsO/ykedPpuxqUETBZTYm.F2U4TXDdo01rLmoRPwjKBv3pIL5pnWq'),
-- ... 更多数据
('6', '张三', '$2b$10$CsO/ykedPpuxqUETBZTYm.F2U4TXDdo01rLmoRPwjKBv3pIL5pnWq');

2. 插入文章 (Posts)

这里我们用诗句来充当内容,充满诗意的数据库!

sql 复制代码
INSERT INTO "posts" ("title", "content", "userId") VALUES 
('黄鹤楼送孟浩然之广陵', '故人西辞黄鹤楼,烟花三月下扬州', 3), -- 李白写的
('春夜喜雨', '好雨知时节,当春乃发生', 4),           -- 杜甫写的
('琵琶行', '浔阳江头夜送客,枫叶荻花秋瑟瑟', 5),       -- 白居易写的
('静夜思', '床前明月光,疑是地上霜', 3);              -- 李白写的

注意:userId 必须是 users 表里已经存在的 id(1-6),否则外键约束会直接报错!这就是 PG 的严谨之处。


🤝 玩转连接查询 (Joins)

数据分表存了,怎么把它们"连"起来看?比如我想看"所有文章以及作者的名字"。这就需要用到 Join

1. 左连接 (Left Join) ------ "以我为主"

sql 复制代码
SELECT posts.title, users.name 
FROM posts 
LEFT JOIN users ON posts."userId" = users.id;
  • 含义posts 是左表。显示所有 文章。如果某篇文章没有作者(虽然我们的约束不允许,但假设允许),作者栏会显示 NULL
  • 口诀:左表全显示,右表配不上的补空。

2. 右连接 (Right Join) ------ "客随主便"

sql 复制代码
SELECT posts.title, users.name 
FROM posts 
RIGHT JOIN users ON posts."userId" = users.id;
  • 含义users 是右表。显示所有 用户。如果"张三"没写过文章,他的名字也会出现,但文章标题栏是 NULL

3. 内连接 (Inner Join) ------ "两情相悦"

sql 复制代码
SELECT posts.title, users.name 
FROM posts 
INNER JOIN users ON posts."userId" = users.id;
  • 含义 :只显示既有文章又有作者的记录。如果张三没写文章,张三不会出现;如果有一篇无头文章,也不会出现。这是最常用的连接方式。

⚡️ 常用 PSQL 命令行指令

作为全栈开发者,不要只依赖图形化工具,命令行才是装 X...哦不,效率的体现!

  • \list\l: 列出所有数据库(看一眼你的战利品)。
  • \c xuebi: 连接到名为 xuebi 的数据库(进入战场)。
  • \dt: 列出当前数据库下的所有表(查看兵力)。
  • \d users: 查看 users 表的详细结构(查看兵种属性)。

🎓 总结

今天我们不仅学习了 SQL 语法,更重要的是理解了数据设计的哲学

  • ACID 给了我们安全感。
  • 主外键 给了我们逻辑关联。
  • 数据类型 的选择体现了工程师的远见(BIGINT, TIMESTAMPTZ)。

数据库设计没有绝对的"完美",只有最适合业务的"权衡"。希望通过今天的学习,你在敲下 CREATE TABLE 时,脑海里能浮现出数据在磁盘上井井有条流动的画面。

下节课,我们将拿起 Node.js 这个强大的武器,去连接并操作我们刚刚建立的"数据堡垒"。敬请期待!


如果你喜欢这篇文章,别忘了点赞、收藏、关注三连哦!这对我持续输出高质量技术内容非常重要! 🚀

相关推荐
天人合一peng2 小时前
kingbase数据库的
服务器·数据库·oracle
雨季6662 小时前
系统化方法论与实战案例
数据库
数据库知识分享者小北2 小时前
从极速复制“死了么”APP,看AI编程时代的技术选型
数据库·阿里云·状态模式·ai编程·supabase
想摆烂的不会研究的研究生2 小时前
并发场景——接口幂等性设计
数据库·redis·后端·缓存
星火开发设计2 小时前
关系代数:数据库查询的数学基石与实战解析
数据库·学习·oracle·知识·关系代数
u0104058362 小时前
企业微信第三方应用API对接的Java后端架构设计:解耦与可扩展性实践
java·数据库·企业微信
ascarl20102 小时前
Oracle 12c 官方卸载工具 (Deinstall Tool) 标准流程
数据库·oracle
百炼成神 LV@菜哥2 小时前
记GaussDB(for PostgreSQL)入门SQL操作
数据库·postgresql·gaussdb
之歆2 小时前
Agent:链式工作流模式
数据库