<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>数据库 on Jiangwan&#39;s Blog</title>
        <link>https://jiangwan.ink/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/</link>
        <description>Recent content in 数据库 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/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/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>
        <item>
        <title>SQLAlchemy 核心架构解析</title>
        <link>https://jiangwan.ink/p/sqlalchemy-core-architecture-analysis/</link>
        <pubDate>Sat, 28 Mar 2026 00:00:00 +0000</pubDate>
        
        <guid>https://jiangwan.ink/p/sqlalchemy-core-architecture-analysis/</guid>
        <description>&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;导读&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;很多开发者在使用 SQLAlchemy 时经常遇到诸如 &lt;code&gt;DetachedInstanceError&lt;/code&gt; 或“N+1 查询”等令人头疼的问题。这往往是因为只把它当成了拼接 SQL 的工具，而没有理解其底层的分层架构。本文将从对象与关系的“阻抗失配”出发，拆解 SQLAlchemy 的双层设计哲学，并复盘其状态管理机制中的常见问题。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;一-设计目的&#34;&gt;一、 设计目的
&lt;/h2&gt;&lt;p&gt;在后端开发中，我们使用的编程语言（如 Python）是&lt;strong&gt;面向对象&lt;/strong&gt;的，而底层存储（如 MySQL、PostgreSQL）则是&lt;strong&gt;关系型二维表&lt;/strong&gt;。这两者之间存在天然的语义鸿沟：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;对象系统&lt;/strong&gt;：有继承、多态，数据表现为图状的网络引用（比如一个 User 对象里面嵌套了一个 List 的 Order 对象）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;关系系统&lt;/strong&gt;：只有扁平的行和列，通过外键来表达关联。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这种不匹配在软件工程中被称为“&lt;strong&gt;阻抗失配&lt;/strong&gt;” (Object-Relational Impedance Mismatch)。&lt;/p&gt;
&lt;p&gt;SQLAlchemy 存在的目的，并不是简单地帮你省去写 SQL 字符串的麻烦，而是作为一座桥梁，&lt;strong&gt;抹平这种阻抗失配，负责在内存中的对象状态和磁盘上的二维表状态之间进行双向同步。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;二-核心架构&#34;&gt;二、 核心架构
&lt;/h2&gt;&lt;p&gt;为了做好这道桥梁，SQLAlchemy 采用了极其严密的&lt;strong&gt;双层解耦架构&lt;/strong&gt;。&lt;/p&gt;
&lt;pre class=&#34;mermaid&#34;&gt;
  flowchart TD
  subgraph ORMLayer[&amp;#34;ORM 层 (Object Relational Mapper)&amp;#34;]
        Session[Session&amp;lt;br/&amp;gt;会话/工作单元]
        Model[Python Objects&amp;lt;br/&amp;gt;映射对象]
        Session &amp;lt;--&amp;gt; Model
    end

  subgraph CoreLayer[&amp;#34;Core 层 (底层核心)&amp;#34;]
        Engine[Engine&amp;lt;br/&amp;gt;核心引擎]
        Pool[(Connection Pool&amp;lt;br/&amp;gt;连接池)]
        Dialect[Dialect&amp;lt;br/&amp;gt;数据库方言]
        
        Engine --&amp;gt; Pool
        Engine --&amp;gt; Dialect
    end

    subgraph PhysicalLayer[&amp;#34;数据库 DBAPI&amp;#34;]
      DB[(Database&amp;lt;br/&amp;gt;MySQL/PG等)]
    end

    Session --&amp;gt;|发送 SQL 表达式| Engine
    Pool --&amp;gt;|获取连接| DB
    Dialect --&amp;gt;|翻译为原生 SQL| DB
&lt;/pre&gt;

&lt;h3 id=&#34;1-core-层&#34;&gt;1. Core 层
&lt;/h3&gt;&lt;p&gt;Core 层最贴近数据库，它的职责只有一个：&lt;strong&gt;和数据库打交道。&lt;/strong&gt; 即使不用 ORM，你依然可以单独使用 Core 层。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Engine (引擎)&lt;/strong&gt;：整个 SQLAlchemy 的入口点。它本质上是一个配置和资源管理者。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connection Pool (连接池)&lt;/strong&gt;：维护与数据库的物理连接，避免频繁建连的巨大开销。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dialect (方言)&lt;/strong&gt;：负责翻译。由于 MySQL 和 PostgreSQL 的原生 SQL 语法有细微差异，Dialect 负责将通用的指令翻译成特定数据库听得懂的“方言”。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2-orm-层&#34;&gt;2. ORM 层
&lt;/h3&gt;&lt;p&gt;ORM 层建立在 Core 层之上，它的核心设计模式是“&lt;strong&gt;工作单元 (Unit of Work)&lt;/strong&gt;”。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Session (会话)&lt;/strong&gt;：这是我们在业务代码中最常接触的对象。它不仅是一个数据库连接的代理，更是一个&lt;strong&gt;内存暂存区&lt;/strong&gt;。它追踪了所有查询出来的对象状态（新建、修改、删除），并在适适时机统一提交给 Core 层。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;三-一条查询的生命周期&#34;&gt;三、 一条查询的生命周期
&lt;/h2&gt;&lt;p&gt;理解了双层架构，我们来推导一下执行 &lt;code&gt;session.execute(select(User))&lt;/code&gt; 时，系统到底发生了什么：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;代码调用&lt;/strong&gt;：你在业务层触发了查询指令。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Session 拦截&lt;/strong&gt;：Session 接收到指令，检查内存中是否已经缓存了该数据。如果没有，则向下传递。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Core 翻译&lt;/strong&gt;：Engine 接收到抽象的 &lt;code&gt;select()&lt;/code&gt; 表达式，调用对应的 Dialect 将其编译为真正的 SQL 字符串。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;获取连接&lt;/strong&gt;：Engine 向 Connection Pool 申请一个可用的数据库连接。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;执行查询&lt;/strong&gt;：通过底层的 DBAPI (如 &lt;code&gt;pymysql&lt;/code&gt; 或 &lt;code&gt;psycopg2&lt;/code&gt;) 将 SQL 发送给物理数据库执行。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;对象映射 (关键)&lt;/strong&gt;：拿到二维表结构的游标结果后，ORM 层将其&lt;strong&gt;反序列化&lt;/strong&gt;为 Python 的 &lt;code&gt;User&lt;/code&gt; 实例，将其挂载到 Session 的追踪名单中，最后返回给你的代码。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;四-常见问题&#34;&gt;四、 常见问题
&lt;/h2&gt;&lt;h3 id=&#34;1-n1-查询问题&#34;&gt;1. N+1 查询问题
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;现象&lt;/strong&gt;：获取 100 个用户及其对应的文章列表，原本以为只查了 1 次数据库，看日志却发现数据库跑了 101 条 SQL。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;因果推导&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;因&lt;/strong&gt;：SQLAlchemy 默认对关联关系（如 &lt;code&gt;User.articles&lt;/code&gt;）采用&lt;strong&gt;延迟加载 (Lazy Loading)&lt;/strong&gt;。当你在代码中通过循环访问 &lt;code&gt;user.articles&lt;/code&gt; 时，每次访问都会触发 Session 向数据库发送一条新的查询 SQL以获取该用户的文章。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;果&lt;/strong&gt;：导致数据库 QPS 飙升，接口响应极慢。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;解法&lt;/strong&gt;：在查询初始对象时，明确告知 SQLAlchemy 采用&lt;strong&gt;贪婪加载 (Eager Loading)&lt;/strong&gt;。使用 &lt;code&gt;joinedload&lt;/code&gt;（适合一对一）或 &lt;code&gt;selectinload&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;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;sqlalchemy.orm&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;selectinload&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;stmt&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;select&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;User&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;options&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;selectinload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;User&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;articles&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;session&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;execute&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stmt&lt;/span&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;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2-内存状态隔离--detachedinstanceerror&#34;&gt;2. 内存状态隔离 —— &lt;code&gt;DetachedInstanceError&lt;/code&gt;
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;现象&lt;/strong&gt;：查出了一个 &lt;code&gt;User&lt;/code&gt; 对象，执行了 &lt;code&gt;session.close()&lt;/code&gt; 后，试图在视图函数里获取 &lt;code&gt;user.name&lt;/code&gt;，程序直接崩溃抛出 &lt;code&gt;DetachedInstanceError&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;因果推导&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;因&lt;/strong&gt;：由架构可知，对象是依赖 &lt;code&gt;Session&lt;/code&gt; 这个上下文环境而存在的。一旦 Session 关闭，物理连接归还给了连接池，这时的 Python 对象就成了失去数据库联系的&lt;strong&gt;游离态 (Detached)&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;果&lt;/strong&gt;：此时如果访问某个尚未加载的属性（比如关联属性，或者在 &lt;code&gt;expire_on_commit=True&lt;/code&gt; 设定下被标记为过期的属性），对象试图向背后的 Session 求助去查询数据库，却发现 Session 已经没了，从而抛出异常。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;解法&lt;/strong&gt;：
&lt;ol&gt;
&lt;li&gt;保证所有需要的属性在 Session 生命周期内访问完毕。&lt;/li&gt;
&lt;li&gt;如果确实需要跨层传递对象，可以在创建 Session 时设置 &lt;code&gt;expire_on_commit=False&lt;/code&gt;，但这可能导致读取到过期的脏数据。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        
    </channel>
</rss>
