<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Alembic on Jiangwan&#39;s Blog</title>
        <link>https://jiangwan.ink/tags/alembic/</link>
        <description>Recent content in Alembic on Jiangwan&#39;s Blog</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>zh-cn</language>
        <lastBuildDate>Sun, 29 Mar 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://jiangwan.ink/tags/alembic/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>Alembic 数据库迁移实践</title>
        <link>https://jiangwan.ink/p/alembic-database-migration-practice/</link>
        <pubDate>Sun, 29 Mar 2026 00:00:00 +0000</pubDate>
        
        <guid>https://jiangwan.ink/p/alembic-database-migration-practice/</guid>
        <description>&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;导读&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;在使用 SQLAlchemy 开发项目时，&lt;code&gt;Base.metadata.create_all(engine)&lt;/code&gt; 虽然能快速初始化表，但无法处理后续的字段增加、类型修改等演进需求。为了解决表结构的版本管理与同步问题，我们需要引入 Alembic。本文将深入探讨 Alembic 的工作机制、初始化配置以及标准工作流，并复盘 SQLite 兼容性与字段重命名等常见工程挑战。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Alembic 是由 SQLAlchemy 作者编写的轻量级数据库迁移工具，它为数据库的表结构演进提供了完整的版本控制能力。&lt;/p&gt;
&lt;h2 id=&#34;一-工作机制&#34;&gt;一、 工作机制
&lt;/h2&gt;&lt;p&gt;Alembic 的核心逻辑可以类比为代码的版本控制系统。它主要依赖以下三个部分协同工作：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;环境目录 (alembic 文件夹)&lt;/strong&gt;：执行初始化命令后生成的目录，包含迁移配置、环境脚本和所有的迁移版本文件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;版本脚本 (Revision Scripts)&lt;/strong&gt;：存放于环境目录的 &lt;code&gt;versions&lt;/code&gt; 文件夹下。每个脚本对应一次表结构变更，内部包含 &lt;code&gt;upgrade()&lt;/code&gt; 向上升级和 &lt;code&gt;downgrade()&lt;/code&gt; 向下回退两个核心函数。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;alembic_version 表&lt;/strong&gt;：Alembic 会在目标数据库中自动创建一张单行表，用于记录当前数据库实际处于哪个版本号。执行迁移时，Alembic 会对比本地脚本与该表中的版本号，决定执行哪些 &lt;code&gt;upgrade&lt;/code&gt; 或 &lt;code&gt;downgrade&lt;/code&gt; 操作。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;二-初始化与配置&#34;&gt;二、 初始化与配置
&lt;/h2&gt;&lt;p&gt;在项目根目录下，通过命令行工具初始化 Alembic 环境：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;alembic init alembic
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这会生成一个 &lt;code&gt;alembic.ini&lt;/code&gt; 配置文件和一个 &lt;code&gt;alembic&lt;/code&gt; 目录。要让 Alembic 能够自动检测到 SQLAlchemy 模型的变化，必须完成两处核心配置。&lt;/p&gt;
&lt;h3 id=&#34;1-配置数据库连接&#34;&gt;1. 配置数据库连接
&lt;/h3&gt;&lt;p&gt;在 &lt;code&gt;alembic.ini&lt;/code&gt; 中找到 &lt;code&gt;sqlalchemy.url&lt;/code&gt;，修改为你的数据库连接字符串。但在实际工程中，密码和 URL 通常写在环境变量中。更安全的做法是在 &lt;code&gt;env.py&lt;/code&gt; 中动态加载项目配置并覆盖该值。&lt;/p&gt;
&lt;h3 id=&#34;2-绑定模型元数据-metadata&#34;&gt;2. 绑定模型元数据 (Metadata)
&lt;/h3&gt;&lt;p&gt;这是 Alembic 自动生成迁移脚本的关键。打开 &lt;code&gt;alembic/env.py&lt;/code&gt;，导入项目中的声明基类 &lt;code&gt;Base&lt;/code&gt; 以及所有的模型文件。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# alembic/env.py 核心修改片段&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;logging.config&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fileConfig&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;sqlalchemy&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;engine_from_config&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;sqlalchemy&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;alembic&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 导入你的项目模型，确保模型在此处被加载&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;myapp.database&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Base&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;myapp&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;models&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;config&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;config&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 将 target_metadata 设为 SQLAlchemy 模型的 metadata&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;target_metadata&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Base&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;metadata&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# ... 其他代码保持不变 ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果在执行迁移时提示检测不到表结构变化，通常是因为模型类没有在 &lt;code&gt;env.py&lt;/code&gt; 执行时被成功导入到内存中。&lt;/p&gt;
&lt;h2 id=&#34;三-标准工作流&#34;&gt;三、 标准工作流
&lt;/h2&gt;&lt;p&gt;完成配置后，日常的模型修改和数据库同步将遵循以下标准步骤：&lt;/p&gt;
&lt;h3 id=&#34;1-修改模型代码&#34;&gt;1. 修改模型代码
&lt;/h3&gt;&lt;p&gt;在 SQLAlchemy 的模型类中添加或修改字段。&lt;/p&gt;
&lt;h3 id=&#34;2-自动生成迁移脚本&#34;&gt;2. 自动生成迁移脚本
&lt;/h3&gt;&lt;p&gt;使用 &lt;code&gt;--autogenerate&lt;/code&gt; 标志，Alembic 会对比当前数据库的实际结构与内存中的 &lt;code&gt;target_metadata&lt;/code&gt; 结构，自动生成对应的迁移代码。&lt;code&gt;-m&lt;/code&gt; 参数用于添加简短的变更说明。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;alembic revision --autogenerate -m &lt;span class=&#34;s2&#34;&gt;&amp;#34;add user email column&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;执行后，必须人工检查 &lt;code&gt;versions&lt;/code&gt; 目录下新生成的脚本文件，确保 &lt;code&gt;upgrade()&lt;/code&gt; 函数中的变更逻辑符合预期。&lt;/p&gt;
&lt;h3 id=&#34;3-执行升级迁移&#34;&gt;3. 执行升级迁移
&lt;/h3&gt;&lt;p&gt;将数据库更新到最新版本：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;alembic upgrade head
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;4-版本回退&#34;&gt;4. 版本回退
&lt;/h3&gt;&lt;p&gt;如果迁移代码有误或需要撤销上一步的结构变更，可以执行回退命令。&lt;code&gt;-1&lt;/code&gt; 表示向后回退一个版本：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;alembic downgrade -1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;四-常见问题&#34;&gt;四、 常见问题
&lt;/h2&gt;&lt;p&gt;在使用 Alembic 的过程中，有几个高频的工程配置问题需要提前预防。&lt;/p&gt;
&lt;h3 id=&#34;1-sqlite-的-alter-限制&#34;&gt;1. SQLite 的 ALTER 限制
&lt;/h3&gt;&lt;p&gt;SQLite 对 &lt;code&gt;ALTER TABLE&lt;/code&gt; 操作支持有限，默认情况下 Alembic 无法在 SQLite 中直接重命名列或修改列类型。如果项目使用 SQLite，需要在 &lt;code&gt;env.py&lt;/code&gt; 中开启批处理模式。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# env.py 中的配置修改&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;with&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;connectable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;connect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;connection&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;configure&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;connection&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;connection&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;target_metadata&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;target_metadata&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;render_as_batch&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# 开启批处理模式支持 SQLite&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;2-字段重命名与空迁移&#34;&gt;2. 字段重命名与空迁移
&lt;/h3&gt;&lt;p&gt;Alembic 默认的 &lt;code&gt;--autogenerate&lt;/code&gt; 机制并不总是完美的。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;重命名&lt;/strong&gt;：如果修改了模型中的列名，Alembic 默认会将其识别为“删除旧列”并“增加新列”，这会导致该列的原有数据丢失。遇到重命名需求时，必须手动修改生成的迁移脚本，使用 &lt;code&gt;op.alter_column&lt;/code&gt; 方法。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;类型改变未检测&lt;/strong&gt;：默认情况下，Alembic 不会检查字段类型或默认值的变化。如果需要检测这些精细变更，需要在 &lt;code&gt;env.py&lt;/code&gt; 的 &lt;code&gt;context.configure&lt;/code&gt; 中添加 &lt;code&gt;compare_type=True&lt;/code&gt; 和 &lt;code&gt;compare_server_default=True&lt;/code&gt; 参数。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;五-总结&#34;&gt;五、 总结
&lt;/h2&gt;&lt;p&gt;SQLAlchemy 负责解决应用程序层面的对象关系映射，而 Alembic 填补了数据库生命周期管理中的缺失环节。建立完善的 &lt;code&gt;修改模型 -&amp;gt; 审查脚本 -&amp;gt; 升级数据库&lt;/code&gt; 工作流，是保证复杂系统数据一致性的基础工程规范。&lt;/p&gt;
</description>
        </item>
        
    </channel>
</rss>
