FastAPI + PostgreSQL 实战:从入门到不踩坑,一次讲透

🧐 第一部分:为什么是PostgreSQL?

你可以把PostgreSQL想象成一个**"极度守规矩的档案管理员"**------数据完整性、ACID、复杂查询支持得滴水不漏。

相比MySQL,它对JSON、全文检索、地理空间数据的支持更原生,而且这几年性能优化得特别猛。

最关键的是,它和FastAPI的异步生态配合得非常好,后面你会体会到。

⚙️ 第二部分:安装PostgreSQL(Docker一键搞定)

最怕那种"双击安装包一路下一步"然后环境变量配半小时的教程。咱们用Docker,干净利落。

复制代码
docker run -d \
  --name postgres-demo \
  -e POSTGRES_PASSWORD=mysecretpassword \
  -e POSTGRES_USER=myuser \
  -e POSTGRES_DB=mydb \
  -p 5432:5432 \
  postgres:15

然后你就可以用任何客户端(比如TablePlusDBeaver )连上localhost:5432验证一下。记得密码别写死在代码里,后面咱们用环境变量。

敲黑板: 本地开发千万别用root用户,上面我特意建了普通用户myuser,权限最小化原则。

📦 第三部分:FastAPI项目初始化

创建一个新目录,弄个虚拟环境,然后装依赖。这里我推荐两个库

asyncpg ------ PostgreSQL的异步驱动,性能爆表。

databases ------ 封装了连接池和查询,让你能用async/await执行SQL,还支持SQLAlchemy Core(可选)。

当初我图省事直接用了同步的psycopg2,结果FastAPI异步线程被阻塞,性能直接打骨折。所以,一定一定要用异步驱动!

安装命令:

复制代码
pip install fastapi uvicorn databases[asyncpg] python-dotenv

💻 第四部分:写代码,让FastAPI和PG连上线

先建一个.env文件存放敏感信息:

复制代码
DATABASE_URL=postgresql://myuser:mysecretpassword@localhost/mydb

然后写主程序main.py

复制代码
import os
from fastapi import FastAPI
from contextlib import asynccontextmanager
from databases import Database
from dotenv import load_dotenv

load_dotenv()

DATABASE_URL = os.getenv("DATABASE_URL")
database = Database(DATABASE_URL)

@asynccontextmanager
async def lifespan(app: FastAPI):
    await database.connect()
    # 创建表(实际项目用Alembic,这里演示直接执行SQL)
    query = """CREATE TABLE IF NOT EXISTS users (
        id SERIAL PRIMARY KEY,
        username VARCHAR(50) UNIQUE NOT NULL,
        email VARCHAR(100) UNIQUE NOT NULL
    );"""
    await database.execute(query=query)
    print('数据表创建完成!')
    yield
    await database.disconnect()

app = FastAPI(lifespan=lifespan)

@app.get("/users")
async def get_users():
    query = "SELECT id, username, email FROM users"
    results = await database.fetch_all(query=query)
    return [dict(r) for r in results]

@app.post("/users")
async def create_user(username: str, email: str):
    # 简单的插入,实际要加异常处理
    query = "INSERT INTO users(username, email) VALUES (:username, :email) RETURNING id"
    values = {"username": username, "email": email}
    user_id = await database.execute(query=query, values=values)
    return {"id": user_id, "username": username, "email": email}

启动服务:uvicorn main:app --reload。打开http://localhost:8000/docs,你就能看到自动生成的API文档,试一下POST和GET,是不是很爽?

⚠️ 第五部分:那些年我踩过的坑(精华)

🚨 坑1:连接被拒绝

现象:ConnectionRefusedError。大概率是Docker容器端口没映射出来,或者PostgreSQL配置只绑定了127.0.0.1。用Docker一定要加-p 5432:5432。另外检查防火墙。

🚨 坑2:asyncpg.exceptions.InvalidPasswordError

密码错误?其实很多时候是因为URL格式不对。注意postgresql://user:pass@host/db,特殊字符要URL编码。

🚨 坑3:在异步函数里用了同步的psycopg2连接

这会导致事件循环阻塞,性能急剧下降。解决方案就是全程用asyncpgdatabases

🚨 坑4:事务忘记commit?

database.transaction()上下文管理器最安全:

复制代码
async with database.transaction():
    await database.execute(query1)
    await database.execute(query2)  # 如果出错,自动回滚

🚨 坑5:连接池耗尽

默认连接池大小可能不够,高并发时需调整:

复制代码
database = Database(DATABASE_URL, min_size=5, max_size=20)
相关推荐
Wait....2 小时前
MySQL事务知识复习
数据库·mysql
杨云龙UP2 小时前
Oracle 19c:RMAN Duplicate异机复制数据库实操_20260402
linux·运维·服务器·数据库·网络协议·tcp/ip·oracle
刘~浪地球2 小时前
Redis 从入门到精通(七):集合操作详解
数据库·chrome·redis
光泽雨2 小时前
SQL分组Group By
数据库
IeE1QQ3GT2 小时前
FastAPI + SQLite:从基础CRUD到安全并发的实战指南
安全·sqlite·fastapi
我真会写代码2 小时前
MySQL高频面试题(含详细解析):从基础到高级,备战面试不踩坑
数据库·mysql·面试
观远数据2 小时前
未来3年企业数据分析的核心:基于自然语言的AI优先决策体系如何搭建
数据库·人工智能·数据分析
YummyJacky2 小时前
Redis在项目中的应用
数据库·redis·缓存
qq_283720052 小时前
MySQL 8.0新特性高频面试题 30 道(超详细答案)
数据库·mysql·面试·mysql8·高频试题