几乎所有 Python 应用都需要持久化数据。本文从 SQLite 起步,到 SQLAlchemy ORM,再到异步数据库访问,给你一个完整的数据库技术栈。
目录
1. 数据库选型指南
| 数据库 | 适用场景 | Python 驱动 |
|---|---|---|
| SQLite | 单机应用、测试、嵌入式 | sqlite3(内置) |
| PostgreSQL | 生产应用、复杂查询 | psycopg2, asyncpg |
| MySQL | Web 应用、LAMP 栈 | PyMySQL, mysqlclient |
| MongoDB | 文档型数据 | pymongo |
| Redis | 缓存、消息队列 | redis-py |
2. SQLite:零配置数据库
2.1 基础 CRUD
import sqlite3
from pathlib import Path
DB_PATH = Path("app.db")
def init_db():
with sqlite3.connect(DB_PATH) as conn:
conn.execute("""
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
def add_user(name: str, email: str):
with sqlite3.connect(DB_PATH) as conn:
conn.execute("INSERT INTO users (name, email) VALUES (?, ?)", (name, email))
conn.commit()
def get_users():
with sqlite3.connect(DB_PATH) as conn:
conn.row_factory = sqlite3.Row # 返回字典式结果
return conn.execute("SELECT * FROM users").fetchall()
# 使用
init_db()
add_user("Alice", "alice@example.com")
for user in get_users():
print(dict(user))
2.2 SQLite 高级特性
# 事务控制
with sqlite3.connect(DB_PATH) as conn:
try:
conn.execute("INSERT INTO users (name) VALUES (?)", ("Bob",))
conn.execute("INSERT INTO invalid_table ...") # 会失败
conn.commit()
except sqlite3.Error:
conn.rollback()
# 返回字典结果
conn.row_factory = lambda c, r: {col[0]: r[idx] for idx, col in enumerate(c.description)}
# FTS5 全文搜索(需要编译时启用)
conn.execute("CREATE VIRTUAL TABLE docs USING fts5(title, content)")
3. PostgreSQL 与 psycopg2
# pip install psycopg2-binary
import psycopg2
from psycopg2.extras import RealDictCursor
conn = psycopg2.connect(
host="localhost",
database="mydb",
user="myuser",
password="mypass",
)
with conn.cursor(cursor_factory=RealDictCursor) as cur:
cur.execute("SELECT * FROM users WHERE age > %s", (18,))
for row in cur.fetchall():
print(dict(row))
conn.close()
4. SQLAlchemy ORM 核心
4.1 模型定义
# pip install sqlalchemy
from sqlalchemy import create_engine, Column, Integer, String, DateTime, ForeignKey
from sqlalchemy.orm import declarative_base, relationship, sessionmaker
from datetime import datetime
Base = declarative_base()
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
name = Column(String(100), nullable=False)
email = Column(String(255), unique=True)
created_at = Column(DateTime, default=datetime.utcnow)
posts = relationship("Post", back_populates="author")
class Post(Base):
__tablename__ = "posts"
id = Column(Integer, primary_key=True)
title = Column(String(200), nullable=False)
content = Column(String)
author_id = Column(Integer, ForeignKey("users.id"))
author = relationship("User", back_populates="posts")
# 创建表
engine = create_engine("sqlite:///app.db", echo=True)
Base.metadata.create_all(engine)
# Session
Session = sessionmaker(bind=engine)
4.2 CRUD 操作
# 创建
with Session() as session:
user = User(name="Alice", email="alice@example.com")
session.add(user)
session.commit()
print(user.id) # 自动获取自增 ID
# 查询
with Session() as session:
# 主键查询
user = session.get(User, 1)
# 条件查询
users = session.query(User).filter(User.name.like("A%")).all()
# 分页
page = session.query(User).offset(10).limit(10).all()
# 更新
with Session() as session:
user = session.get(User, 1)
user.name = "New Name"
session.commit()
# 删除
with Session() as session:
user = session.get(User, 1)
session.delete(user)
session.commit()
5. 关系与查询
5.1 关联查询
# 一对多:用户 - 文章
with Session() as session:
user = session.get(User, 1)
for post in user.posts:
print(post.title)
# 预加载(避免 N+1 查询)
from sqlalchemy.orm import joinedload
users = session.query(User).options(joinedload(User.posts)).all()
# 聚合查询
from sqlalchemy import func
result = session.query(
User.name,
func.count(Post.id).label("post_count")
).join(Post).group_by(User.id).all()
6. 异步数据库:asyncpg
# pip install asyncpg
import asyncpg
import asyncio
async def main():
conn = await asyncpg.connect("postgresql://user:pass@localhost/db")
# 查询
rows = await conn.fetch("SELECT * FROM users WHERE age > $1", 18)
for row in rows:
print(row["name"])
# 事务
async with conn.transaction():
await conn.execute("INSERT INTO users (name) VALUES ($1)", "Bob")
await conn.close()
asyncio.run(main())
SQLAlchemy 异步版本
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker
async_engine = create_async_engine("postgresql+asyncpg://user:pass@localhost/db")
AsyncSessionLocal = sessionmaker(async_engine, class_=AsyncSession)
async def get_users():
async with AsyncSessionLocal() as session:
result = await session.execute(select(User).where(User.age > 18))
return result.scalars().all()
7. 数据库迁移:Alembic
# 安装
pip install alembic
# 初始化
alembic init migrations
# 修改 alembic.ini → sqlalchemy.url = postgresql://user:pass@localhost/db
# 修改 migrations/env.py → 导入你的 Base
# 生成迁移脚本
alembic revision --autogenerate -m "create users table"
# 升级
alembic upgrade head
# 降级
alembic downgrade -1
8. 生产环境最佳实践
- 使用连接池(SQLAlchemy 内置)
- 敏感信息存环境变量
- 索引外键列
- 定期备份(pg_dump、mysqldump)
- 查询慢日志分析
延伸阅读
- Python Web 框架:FastAPI 实战 —— FastAPI + SQLAlchemy 项目结构
- Python 类型系统与 Pydantic V2 —— 数据校验与数据库模型
- Python 并发与性能深度指南 —— 数据库连接池与并发
- Python asyncio 极速异步编程 —— asyncpg 异步查询
数据库是应用的根基。从 SQLite 的快速原型到 PostgreSQL 的生产部署,SQLAlchemy 让你用同一套 API 驾驭不同数据库。掌握 ORM 的查询优化和迁移管理,是 Python 后端工程师的核心能力。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。