惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
The Exploit Database - CXSecurity.com
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Spread Privacy
Spread Privacy
Hacker News - Newest:
Hacker News - Newest: "LLM"
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Google Online Security Blog
Google Online Security Blog
S
Security Affairs
Help Net Security
Help Net Security
Simon Willison's Weblog
Simon Willison's Weblog
N
News and Events Feed by Topic
P
Palo Alto Networks Blog
P
Privacy International News Feed
T
Threat Research - Cisco Blogs
P
Proofpoint News Feed
N
News and Events Feed by Topic
C
Cyber Attacks, Cyber Crime and Cyber Security
TaoSecurity Blog
TaoSecurity Blog
T
Tor Project blog
Schneier on Security
Schneier on Security
WordPress大学
WordPress大学
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
V
Visual Studio Blog
F
Fortinet All Blogs
大猫的无限游戏
大猫的无限游戏
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Tenable Blog
M
MIT News - Artificial intelligence
T
Threatpost
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
N
News | PayPal Newsroom
P
Privacy & Cybersecurity Law Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
Microsoft Azure Blog
Microsoft Azure Blog
小众软件
小众软件
Cyberwarzone
Cyberwarzone
雷峰网
雷峰网
云风的 BLOG
云风的 BLOG
量子位
Google DeepMind News
Google DeepMind News
O
OpenAI News
爱范儿
爱范儿
H
Hackread – Cybersecurity News, Data Breaches, AI and More
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
F
Full Disclosure
B
Blog
Cloudbric
Cloudbric
MyScale Blog
MyScale Blog
阮一峰的网络日志
阮一峰的网络日志

博客园 - 一名程序媛呀

Anki插件开发必知必会:钩子函数与右键菜单定制 httpx 传参总报错?这次把 GET、POST、文件上传到响应处理的坑给你一次填平 从卡顿到丝滑:FastAPI 调用外部 API 的正确姿势(httpx 实战) 还在 XHR、Fetch 和 Axios 之间纠结?我踩过的坑,希望你一个都不用碰到 你的REST接口还在“过度投喂”数据吗?——FastAPI + GraphQL实战避坑指南 Uvicorn、Gunicorn 傻傻分不清?FastAPI 生产部署避坑指南 Termux里的二进制和脚本,到底怎么运行才不踩坑?Termux-service 保活妙招! 刚部署的 LibreTranslate 频频翻车?我掏出了 20 年前的 StarDict 词典,用 FastAPI 搭了个本地词典翻译 API 别再用网页翻译看源码了!你的私人翻译神器LibreTranslate,部署避坑指南来了 掏出手机就能搭个 WebDAV 同步服务器?这操作有点香 别只盯着GitBook了!这个文档神器让你的笔记秒变网站 写爬虫时用了代理还被封?Python 代理的那些隐藏坑,我替你踩明白了 FastAPI 身份验证总踩坑?这份 FastAPI Users “避坑指南”请收好 旧手机别扔!用 Termux 搭个私人云盘,比网盘香多了 你的FastAPI又在服务器上“跑不起来”了?来,今天咱把打包这件事彻底聊透 写页面时别再把 Element Plus 整个搬进来啦!Vue3按需加载的坑我帮你踩平了 前端包管理咋选?我从npm叛逃到pnpm的血泪史(附避坑指南) 聊聊 fetch 使用中我踩过的那些坑和正确打开方式 FastApiAdmin 后端接口开发好了,前端管理界面怎么调用与显示? 给 FastApiAdmin 加个“会议纪要”模块,我把后端二次开发的坑踩了个遍 我用了FastApiAdmin后,连夜把踩过的坑都整理出来了 告别 Typora 后的新欢:我把所有笔记迁移到了 Obsidian 这个“第二大脑” 你的Agent API还在裸奔?从认证到沙箱,我用FastAPI搭了几道防线 让 FastAPI Agent 思考不阻塞:手把手教你实现异步任务与后台处理方案 让FastAPI Agent真正记住你:聊聊会话记忆与持久化存储的落地实践 FastAPI Agent 函数调用实战:我让 AI 学会了“自己动手查天气“ 初探:用 FastAPI 搭建你的第一个 AI Agent 接口 FastAPI 少有人提的实用技巧:把 Depends 依赖提到路由层,代码少写60% FastAPI 生产环境静态文件完全指南:从 /favicon.ico 404 到 HSTS 混合内容,一次全根治 用了loguru我才明白,Python日志还能这么写 FastAPI 后台任务:BackgroundTasks 的使用场景与注意事项 FastAPI配置管理避坑指南:从硬编码到 .env 与 pydantic_settings 类,连路由用法都给你捋清楚 FastAPI 文件上传避坑全指南:分块存盘、类型校验与安全兜底 FastAPI + Pydantic 模型终极实战手册:从能跑就行到固若金汤,这些技巧你一定用得上 FastAPI + SQLAlchemy 2.0 通用CRUD操作手册 —— 从同步到异步,一次讲透 FastAPI订单防超卖实战:从数据库锁到Saga分布式事务,这一篇给你理清了 FastAPI服务半夜又挂了?先别急着重启,查查你的数据库连接池“池子”是不是漏了 FastAPI数据库ORM怎么选?我肝了三个Demo后,终于不再纠结了 Vue 3 组件通信,别只会用 Props 和 Emits 了,这几个狠活儿你得看看 Vue 3 组合式 API 香是香,但从Vue2迁移时你可别像我当初一样踩进这 3 个深坑里 我用fastapi-scaff搭了个项目,两天工期缩到两小时,老板以为我开挂了 FastAPI+Vue:文件分片上传+秒传+断点续传,这坑我帮你踩平了! FastAPI自动生成的API文档太丑?我花了一晚上把它改成了客户愿意付费的样子 告别手写 API 胶水代码:FastAPI 与 Vue 的“契约自动机” OpenAPI 实战 FastAPI + Vue 前后端分离实战:我的项目结构“避坑指南” FastAPI + Celery 实战:异步任务里调用 Redis 和数据库的全解析,及生产级组织方案 FastAPI里玩转Redis和数据库的正确姿势,别让异步任务把你坑哭了! FastAPI + Celery 实战:异步任务的坑与解法,我帮你踩了一遍 FastAPI子应用挂载:别再让root_path坑你一夜 FastAPI项目半夜报警吵醒你?聊聊告警这事儿怎么搞! 别再数据线了!用FastAPI 5分钟搭个局域网文件+剪贴板神器 FastAPI单元测试实战:别等上线被喷才后悔,TestClient用对了真香! FastAPI状态共享秘籍:别再让中间件、依赖和路由“各自为政”了! FastAPI实战:WebSocket vs Socket.IO,这回真给我整明白了! 从0到1,FastAPI + PostgreSQL + Tortoise ORM 实战避坑指南 FastAPI + PostgreSQL 实战:给应用装上“缓存”和“日志”翅膀 FastAPI + PostgreSQL 实战:从入门到不踩坑,一次讲透
FastAPI 生产环境避坑指南:用 Alembic 管理数据库迁移,别再手动改表结构了!
一名程序媛呀 · 2026-04-21 · via 博客园 - 一名程序媛呀

你可以想像一个场景:
凌晨三点,产品突然在群里疯狂 @ 你,说新上的功能把用户数据搞乱了。你睡眼惺忪地爬起来一通排查,发现竟然是刚刚上线的时候,忘了在线上数据库执行那个加字段的 SQL 语句。那一刻,是不是想死的心都有了?

好,今天咱们不聊那些高大上的架构,就踏踏实实地把这个看似不起眼,实则能要命的【数据库迁移】问题给聊透。我会把自已用 FastAPI 搭配 Alembic 的实战经验,特别是踩过的坑和从坑里爬出来的姿势,一次性全分享给你。🎯

😭 第一幕:那些年,我们一起手动执行的 SQL

我相信很多朋友在项目初期或者一些小项目里,都干过这样的事儿:

本地开发改了 Model,然后把改表的 SQL 语句保存到一个 txt 或者 sql 文件里。上线的时候,战战兢兢地连上 生产数据库 ,复制、粘贴、回车,一气呵成。

运气好,一切顺利;运气不好,一个语法错误或者字段冲突,整个上线流程就卡住了,甚至更糟——服务直接崩了。

这感觉像什么?就像你开着一辆没有后视镜的跑车在高速上狂飙,爽是爽,但翻车也就是一瞬间的事儿。手动管理数据库变更,就是咱们技术债里最危险的一笔。

你可能会问:“那用 Alembic 这种迁移工具,不也是执行 SQL 吗?有啥区别?”

问得好!区别就在于,它把变更 版本化、自动化、可追溯、可回滚 了。它就像是给你的数据库操作装上了“行车记录仪“和“倒车影像“,让你每一步都心里有底。

⚙️ 第二幕:Alembic 到底是个啥?三分钟搞懂核心三件套

别被那些术语吓到,Alembic 其实特简单。你可以把它想象成一个专业的数据库版本控制工具,就像我们用 Git 管理代码一样。

它有三个核心玩意儿,咱们必须得认识一下:

- 迁移脚本 (Migration Script)

这就是那个记录了你这次要对数据库“做什么”的 Python 文件。
里面主要是两个函数, upgrade() 是往前走(加字段、建表), downgrade() 是往后退(删字段、删表)。

- 版本表 (alembic_version)

Alembic 会在你的数据库里悄悄建一张表,里面就一个字段,记录着当前数据库结构对应的是哪个版本的迁移脚本。每次执行成功,它就更新一下版本号。

- 配置文件 (alembic.ini & env.py)

alembic.ini 是全局配置,主要告诉 Alembic 你的数据库地址在哪。
env.py 是核心逻辑所在,怎么连数据库、怎么生成脚本,全在这儿定义。

是不是以为这样就完了?不,对于咱们 FastAPI 玩家来说,真正的挑战才刚刚开始,因为我们用的是异步

💣 第三幕:FastAPI 异步环境下,三步搞定 Alembic 配置

好消息是,Alembic 官方早就为我们这些异步党准备了专属模板,再也不用像以前那样手写一大坨 env.py 配置了。你只需要用下面这个命令初始化:

alembic init -t async alembic

这个 -t async 参数,会直接给你生成一套适配 AsyncSQLAlchemy 的 env.py 和脚本模板,省去了大量繁琐的手动改造工作。

但别高兴得太早,生成完不是就万事大吉了,下面这三步“填空题”要是没做好,照样翻车。

第 1 步:告诉 Alembic 你的 Model 长啥样

打开生成的 alembic/env.py ,找到 target_metadata = None 这一行。它默认是空的,Alembic 根本不知道你项目的表结构在哪儿。你得把它改成:

from your_app.models import Base  # 改成你实际项目的导入路径
target_metadata = Base.metadata

第 2 步:导入所有 Model 类

紧接着上面那步,还有一个巨坑。你必须在 env.py 里把所有的 Model 文件都导入一遍,哪怕只是一个 import 不用它的任何东西也行。
因为 Python 不导入那个模块, SQLAlchemy 的 Base.metadata 就不知道那个表存在!

我自己就吃过这个亏,加了个新 Model,跑 autogenerate 死活不生成建表语句,排查半天发现是忘了在 env.py 里加一行 from your_app import new_model

所以,养成习惯,在 env.py 最上面来一句 from your_app.models import * 一劳永逸。
(实测 * 有时会导致识别不到子模块,安全的建议还是将所有自定义模型明确引入,这里保留 * 是想提醒诸位,全部引入,全部引入,全部引入!!!)

第 3 步:配置数据库连接串

异步模板会从 alembic.ini 里的 sqlalchemy.url 读取连接信息。但你很可能在项目里用 Pydantic Settings 管理配置。
所以更推荐的做法是,在 env.py 里动态从你的配置对象读取 URL,而不是写死在 .ini 文件里。这样切换开发/测试/生产环境才方便。

做法很简单,在 run_migrations_online 函数里找到读取配置的地方,改成:

from your_app.config import settings

# 在 run_migrations_online 函数里
config_section = config.get_section(config.config_ini_section, {})
# 直接覆盖掉从 .ini 读来的 url
config_section["sqlalchemy.url"] = settings.DATABASE_URL

connectable = async_engine_from_config(
    config_section,
    prefix="sqlalchemy.",
    poolclass=pool.NullPool,
)

搞定这三步,你的 Alembic 异步环境才算真正配置好了。是不是比想象中简单?

🚀 第四幕:标准工作流 & 生产级零停机的骚操作

配好环境,咱们的日常操作流就顺滑无比了:

1️⃣ 改完 Model 代码。

2️⃣ 运行 alembic revision --autogenerate -m "add_user_bio_field"

3️⃣ 停下来!打开生成的迁移脚本,瞪大眼睛仔细检查!
Alembic 自动生成的东西有时候会缺心眼,比如它可能会把改字段名识别成先删后建,这要是在生产环境跑了,数据就没了!

4️⃣ 确认无误,运行 alembic upgrade head

接下来重点来了,咱们聊聊 千万级大表零停机部署 的注意事项。

如果你直接在几千万数据的表上通过 Alembic 加一个带默认值的字段,数据库可能会锁住,导致你的线上服务几分钟甚至几十分钟不可用。这绝对是一场灾难。

根据以往的经验,调整成这样的策略会更稳:

- 向后兼容原则:永远只做“加法”,不做或少做“减法”。

新加的字段必须允许为空( nullable=True ),并且不要设置默认值。设置默认值会导致数据库立刻去改写所有现有行,这是锁表的元凶。

- 分步执行:加字段分三步走。

第一步,执行迁移只加字段(nullable)。
第二步,部署新代码,代码层面处理新字段为空的逻辑。
第三步,再写个后台脚本或者另一次迁移,慢慢把所有旧数据的该字段填上值,最后再考虑改成非空。

这个工具的选择,好比选螺丝刀,不是最贵的就好,而是用对了方法才顺手。Alembic 给你提供了螺丝刀,但怎么拧螺丝不把板子拧裂,还得靠经验和技巧。

🎬 写在最后

好了,关于 FastAPI + Alembic 的这一套组合拳,从入门到能抗生产,差不多就是这些了。

技术这条路,从来没有什么银弹,都是在解决一个又一个具体问题的过程中,慢慢变得游刃有余的。希望我分享的这些踩坑经历,能让你在面对数据库变更时,少一丝慌张,多一份从容。

最后啰嗦一句,不管用什么工具,上线前多检查一遍迁移脚本,这个习惯能救你无数次。


嘿,我是你的老朋友一名程序媛。这篇文章里聊的 -t async 用法和零停机策略,都是我亲自趟过的坑总结出来的。如果你觉得有点用,别藏着,点个「赞」加「关注」分享给身边还在手动执行 SQL 的难兄难弟们吧。也欢迎留言区跟我聊聊,你都在数据库迁移上栽过哪些跟头?咱们下次见!👋