# 后端第八课:数据库入门(给 FastAPI 接口加上“记忆“,存下问答历史)

👋 我是 Yulia,一线 AI 应用开发工程师:4 年工程经验,在职做大模型应用落地。

只写一件事------把最新 AI 技术讲成人话 + 给你能直接上手用的代码 (真实可验证,不搬运不注水)。

有用就点个 关注,持续更新大模型应用的前沿实战。
📚 本文是《后端开发从入门到靠谱》系列第 8 篇。第七课我们用 FastAPI 写了一个 AI 问答接口,但它有个致命缺陷------一问一答后什么都没留下,服务一重启,历史全没了。这一课补上"记忆":给接口接上数据库,把问答历史存下来。整个系列共 20 篇,从 0 基础一路到能独立写生产级后端。

上一课的 /ask 接口能问答,但你有没有发现一个问题:你问过什么、答过什么,它全忘了 。因为之前我们只是把答案"返回"给客户端,数据存在内存里------服务一重启,内存清空,什么都没了。

真实的应用(客服机器人、AI 助手)一定会有"历史记录""对话上下文"。要做到这些,就需要数据库------给程序装上"记忆"。

读完你会拿到:

  • 一个直觉模型:内存 vs 数据库的本质差别(白板 vs 笔记本类比)
  • 数据库的两大门派:SQL 关系型 vs NoSQL(Excel vs 文件夹类比,含选型)
  • 关系型数据库的核心概念:表 / 行 / 列 / 主键 / 外键(0 基础也能懂)
  • SQLite 入门(Python 内置,零安装,代码可直接跑)
  • ORM 是什么、为什么能少写一半 SQL(翻译官类比 + SQLAlchemy)
  • 一个完整实战:把问答历史存进数据库(衔接上一课 FastAPI 接口)
  • 一个安全必修课:SQL 注入是怎么发生的、怎么防(参数化查询)

先看全景图,建立全局认知:
#mermaid-svg-PrlNnxVpNILsBbuD{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-PrlNnxVpNILsBbuD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PrlNnxVpNILsBbuD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PrlNnxVpNILsBbuD .error-icon{fill:#552222;}#mermaid-svg-PrlNnxVpNILsBbuD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PrlNnxVpNILsBbuD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PrlNnxVpNILsBbuD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PrlNnxVpNILsBbuD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PrlNnxVpNILsBbuD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PrlNnxVpNILsBbuD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PrlNnxVpNILsBbuD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PrlNnxVpNILsBbuD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PrlNnxVpNILsBbuD .marker.cross{stroke:#333333;}#mermaid-svg-PrlNnxVpNILsBbuD svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PrlNnxVpNILsBbuD p{margin:0;}#mermaid-svg-PrlNnxVpNILsBbuD .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-PrlNnxVpNILsBbuD .cluster-label text{fill:#333;}#mermaid-svg-PrlNnxVpNILsBbuD .cluster-label span{color:#333;}#mermaid-svg-PrlNnxVpNILsBbuD .cluster-label span p{background-color:transparent;}#mermaid-svg-PrlNnxVpNILsBbuD .label text,#mermaid-svg-PrlNnxVpNILsBbuD span{fill:#333;color:#333;}#mermaid-svg-PrlNnxVpNILsBbuD .node rect,#mermaid-svg-PrlNnxVpNILsBbuD .node circle,#mermaid-svg-PrlNnxVpNILsBbuD .node ellipse,#mermaid-svg-PrlNnxVpNILsBbuD .node polygon,#mermaid-svg-PrlNnxVpNILsBbuD .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-PrlNnxVpNILsBbuD .rough-node .label text,#mermaid-svg-PrlNnxVpNILsBbuD .node .label text,#mermaid-svg-PrlNnxVpNILsBbuD .image-shape .label,#mermaid-svg-PrlNnxVpNILsBbuD .icon-shape .label{text-anchor:middle;}#mermaid-svg-PrlNnxVpNILsBbuD .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-PrlNnxVpNILsBbuD .rough-node .label,#mermaid-svg-PrlNnxVpNILsBbuD .node .label,#mermaid-svg-PrlNnxVpNILsBbuD .image-shape .label,#mermaid-svg-PrlNnxVpNILsBbuD .icon-shape .label{text-align:center;}#mermaid-svg-PrlNnxVpNILsBbuD .node.clickable{cursor:pointer;}#mermaid-svg-PrlNnxVpNILsBbuD .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-PrlNnxVpNILsBbuD .arrowheadPath{fill:#333333;}#mermaid-svg-PrlNnxVpNILsBbuD .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-PrlNnxVpNILsBbuD .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-PrlNnxVpNILsBbuD .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PrlNnxVpNILsBbuD .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-PrlNnxVpNILsBbuD .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PrlNnxVpNILsBbuD .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-PrlNnxVpNILsBbuD .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-PrlNnxVpNILsBbuD .cluster text{fill:#333;}#mermaid-svg-PrlNnxVpNILsBbuD .cluster span{color:#333;}#mermaid-svg-PrlNnxVpNILsBbuD div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-PrlNnxVpNILsBbuD .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-PrlNnxVpNILsBbuD rect.text{fill:none;stroke-width:0;}#mermaid-svg-PrlNnxVpNILsBbuD .icon-shape,#mermaid-svg-PrlNnxVpNILsBbuD .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PrlNnxVpNILsBbuD .icon-shape p,#mermaid-svg-PrlNnxVpNILsBbuD .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-PrlNnxVpNILsBbuD .icon-shape .label rect,#mermaid-svg-PrlNnxVpNILsBbuD .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PrlNnxVpNILsBbuD .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-PrlNnxVpNILsBbuD .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-PrlNnxVpNILsBbuD :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-PrlNnxVpNILsBbuD .blue>*{fill:#4E79A7!important;color:#fff!important;stroke:none!important;}#mermaid-svg-PrlNnxVpNILsBbuD .blue span{fill:#4E79A7!important;color:#fff!important;stroke:none!important;}#mermaid-svg-PrlNnxVpNILsBbuD .blue tspan{fill:#fff!important;}#mermaid-svg-PrlNnxVpNILsBbuD .orange>*{fill:#F28E2B!important;color:#fff!important;stroke:none!important;}#mermaid-svg-PrlNnxVpNILsBbuD .orange span{fill:#F28E2B!important;color:#fff!important;stroke:none!important;}#mermaid-svg-PrlNnxVpNILsBbuD .orange tspan{fill:#fff!important;}#mermaid-svg-PrlNnxVpNILsBbuD .green>*{fill:#59A14F!important;color:#fff!important;stroke:none!important;}#mermaid-svg-PrlNnxVpNILsBbuD .green span{fill:#59A14F!important;color:#fff!important;stroke:none!important;}#mermaid-svg-PrlNnxVpNILsBbuD .green tspan{fill:#fff!important;}#mermaid-svg-PrlNnxVpNILsBbuD .red>*{fill:#E15759!important;color:#fff!important;stroke:none!important;}#mermaid-svg-PrlNnxVpNILsBbuD .red span{fill:#E15759!important;color:#fff!important;stroke:none!important;}#mermaid-svg-PrlNnxVpNILsBbuD .red tspan{fill:#fff!important;}#mermaid-svg-PrlNnxVpNILsBbuD .cyan>*{fill:#76B7B2!important;color:#fff!important;stroke:none!important;}#mermaid-svg-PrlNnxVpNILsBbuD .cyan span{fill:#76B7B2!important;color:#fff!important;stroke:none!important;}#mermaid-svg-PrlNnxVpNILsBbuD .cyan tspan{fill:#fff!important;} 存问答
翻译成 SQL
写入
查询
返回对象
返回数据
FastAPI 接口
SQLAlchemy ORM
SQLite 数据库
磁盘文件 chat.db

一、为什么需要数据库:内存会"失忆"

先搞懂最核心的问题:为什么不能把数据一直放在变量里?

一句话:内存是"白板",数据库是"笔记本"。
#mermaid-svg-LIvEXUQ5EIgAVJfO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-LIvEXUQ5EIgAVJfO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-LIvEXUQ5EIgAVJfO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-LIvEXUQ5EIgAVJfO .error-icon{fill:#552222;}#mermaid-svg-LIvEXUQ5EIgAVJfO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-LIvEXUQ5EIgAVJfO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-LIvEXUQ5EIgAVJfO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-LIvEXUQ5EIgAVJfO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-LIvEXUQ5EIgAVJfO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-LIvEXUQ5EIgAVJfO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-LIvEXUQ5EIgAVJfO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-LIvEXUQ5EIgAVJfO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-LIvEXUQ5EIgAVJfO .marker.cross{stroke:#333333;}#mermaid-svg-LIvEXUQ5EIgAVJfO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-LIvEXUQ5EIgAVJfO p{margin:0;}#mermaid-svg-LIvEXUQ5EIgAVJfO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-LIvEXUQ5EIgAVJfO .cluster-label text{fill:#333;}#mermaid-svg-LIvEXUQ5EIgAVJfO .cluster-label span{color:#333;}#mermaid-svg-LIvEXUQ5EIgAVJfO .cluster-label span p{background-color:transparent;}#mermaid-svg-LIvEXUQ5EIgAVJfO .label text,#mermaid-svg-LIvEXUQ5EIgAVJfO span{fill:#333;color:#333;}#mermaid-svg-LIvEXUQ5EIgAVJfO .node rect,#mermaid-svg-LIvEXUQ5EIgAVJfO .node circle,#mermaid-svg-LIvEXUQ5EIgAVJfO .node ellipse,#mermaid-svg-LIvEXUQ5EIgAVJfO .node polygon,#mermaid-svg-LIvEXUQ5EIgAVJfO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-LIvEXUQ5EIgAVJfO .rough-node .label text,#mermaid-svg-LIvEXUQ5EIgAVJfO .node .label text,#mermaid-svg-LIvEXUQ5EIgAVJfO .image-shape .label,#mermaid-svg-LIvEXUQ5EIgAVJfO .icon-shape .label{text-anchor:middle;}#mermaid-svg-LIvEXUQ5EIgAVJfO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-LIvEXUQ5EIgAVJfO .rough-node .label,#mermaid-svg-LIvEXUQ5EIgAVJfO .node .label,#mermaid-svg-LIvEXUQ5EIgAVJfO .image-shape .label,#mermaid-svg-LIvEXUQ5EIgAVJfO .icon-shape .label{text-align:center;}#mermaid-svg-LIvEXUQ5EIgAVJfO .node.clickable{cursor:pointer;}#mermaid-svg-LIvEXUQ5EIgAVJfO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-LIvEXUQ5EIgAVJfO .arrowheadPath{fill:#333333;}#mermaid-svg-LIvEXUQ5EIgAVJfO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-LIvEXUQ5EIgAVJfO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-LIvEXUQ5EIgAVJfO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-LIvEXUQ5EIgAVJfO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-LIvEXUQ5EIgAVJfO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-LIvEXUQ5EIgAVJfO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-LIvEXUQ5EIgAVJfO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-LIvEXUQ5EIgAVJfO .cluster text{fill:#333;}#mermaid-svg-LIvEXUQ5EIgAVJfO .cluster span{color:#333;}#mermaid-svg-LIvEXUQ5EIgAVJfO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-LIvEXUQ5EIgAVJfO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-LIvEXUQ5EIgAVJfO rect.text{fill:none;stroke-width:0;}#mermaid-svg-LIvEXUQ5EIgAVJfO .icon-shape,#mermaid-svg-LIvEXUQ5EIgAVJfO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-LIvEXUQ5EIgAVJfO .icon-shape p,#mermaid-svg-LIvEXUQ5EIgAVJfO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-LIvEXUQ5EIgAVJfO .icon-shape .label rect,#mermaid-svg-LIvEXUQ5EIgAVJfO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-LIvEXUQ5EIgAVJfO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-LIvEXUQ5EIgAVJfO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-LIvEXUQ5EIgAVJfO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-LIvEXUQ5EIgAVJfO .blue>*{fill:#4E79A7!important;color:#fff!important;stroke:none!important;}#mermaid-svg-LIvEXUQ5EIgAVJfO .blue span{fill:#4E79A7!important;color:#fff!important;stroke:none!important;}#mermaid-svg-LIvEXUQ5EIgAVJfO .blue tspan{fill:#fff!important;}#mermaid-svg-LIvEXUQ5EIgAVJfO .orange>*{fill:#F28E2B!important;color:#fff!important;stroke:none!important;}#mermaid-svg-LIvEXUQ5EIgAVJfO .orange span{fill:#F28E2B!important;color:#fff!important;stroke:none!important;}#mermaid-svg-LIvEXUQ5EIgAVJfO .orange tspan{fill:#fff!important;} 程序一关 全部消失
写到磁盘 永久保存
内存里的变量
白板 一擦就没
数据库
笔记本 一直留着

你在 Python 里写 history = [],把问答记录 append 进去------这些数据存在**内存(RAM)**里。内存的特点是:

  1. 快:读写速度极快(纳秒级);
  2. 易失:断电、程序退出,数据立刻消失。

而数据库把数据写到磁盘(硬盘/SSD)上,即使服务器重启、断电,数据还在。这就是持久化(persistence)------让数据"活"过这一次运行。

为什么不全用内存?因为内存贵、容量小、且一断电就没了。磁盘便宜、容量大、断电也不丢,代价是读写慢一些。数据库就是"牺牲一点速度,换永久保存"的折中方案。

二、数据库两大门派:SQL 关系型 vs NoSQL

数据库不是一种,而是两大类。选错类型,后面全别扭。

SQL 关系型 NoSQL 非关系型
类比 Excel 表格(规规矩矩的行列) 文件夹(每个文件自己管自己)
代表 MySQL、PostgreSQL、SQLite MongoDB、Redis
数据结构 表(table),行列固定 文档/键值对,结构灵活
适合 关系明确、要一致性(订单、用户) 结构多变、要快(缓存、日志)
查询语言 SQL(标准统一) 各自不同

关系型(SQL) 就像 Excel:每个表有固定的列(字段),每行是一条记录,表与表之间还能"关联"(外键)。它强在数据规整、一致性强、查询语言统一------这是后端最主流的默认选择。

非关系型(NoSQL) 更像文件夹:每条数据可以是任意结构,没有固定表格。它强在灵活、水平扩展容易、某些场景极快。

这一课我们学关系型,因为它是后端基础,且 AI 应用里存对话历史、用户数据这类"关系明确"的数据,默认就用关系型。NoSQL(尤其 Redis 做缓存)后续再讲。

三、关系型数据库的核心概念:表 / 行 / 列 / 键

用一张"用户表"建立直觉:

id(主键) name age city
1 张三 25 北京
2 李四 30 上海
  • 表(table):一整张 Excel 表格,比如"用户表"。
  • 列(column)/字段 :表的一竖列,比如 name、age,每个字段有固定类型(整数、字符串等)。
  • 行(row)/记录:表的一横行,一条完整数据("张三,25 岁,北京")。
  • 主键(primary key) :唯一标识一行的那一列,通常是 id。任何表都必须有主键,就像每个人有身份证号。
  • 外键(foreign key) :一列引用另一张表的主键,用来建立"表与表的关系"。比如"订单表"里有个 user_id 列,指向"用户表"的 id------表示"这笔订单是谁下的"。

用 SQL 语言操作这张表,只有四种基本动作,记住就够入门:

动作 SQL 人话
查 SELECT * FROM users WHERE age > 20 找出年龄大于 20 的用户
增 INSERT INTO users (name, age) VALUES ('王五', 28) 插入一条新用户
改 UPDATE users SET city = '深圳' WHERE id = 1 把 id=1 的城市改成深圳
删 DELETE FROM users WHERE id = 2 删掉 id=2 的用户

这四个词(增删改查,合称 CRUD)覆盖了后端 90% 的数据操作。别被 SQL 吓到,它就是"用英文描述你要对表格做什么"。

四、SQLite 入门:Python 内置,零安装

学数据库最烦的是"先装个 MySQL、配一堆东西"。但 Python 内置了 sqlite3 ------一个零配置、零安装的关系型数据库,数据就存在一个 .db 文件里。学习阶段用它最省事。

python 复制代码
import sqlite3

# 连接数据库(文件不存在会自动创建)
conn = sqlite3.connect("chat.db")
cur = conn.cursor()

# 建一张表
cur.execute("""
CREATE TABLE IF NOT EXISTS chat_records (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    question TEXT NOT NULL,
    answer TEXT NOT NULL
)
""")

# 插入一条记录(用 ? 占位符,防 SQL 注入,后面专门讲)
cur.execute("INSERT INTO chat_records (question, answer) VALUES (?, ?)",
            ("什么是 FastAPI?", "一个现代 Python Web 框架"))

# 查询所有记录
cur.execute("SELECT * FROM chat_records")
for row in cur.fetchall():
    print(row)

conn.commit()   # 提交(真正写入磁盘)
conn.close()    # 关闭连接

逐行讲清关键点:

  1. sqlite3.connect("chat.db")------连接数据库,本质是打开(或创建)一个叫 chat.db 的文件。你的整个数据库,就是一个文件。
  2. CREATE TABLE IF NOT EXISTS------建表语句,id INTEGER PRIMARY KEY AUTOINCREMENT 意思是"id 是主键、自动递增"。
  3. ? 占位符------不要用字符串拼接 SQL ,用 ? 占位 + 参数元组,这是防 SQL 注入的关键(第六节专门讲为什么)。
  4. conn.commit()------SQLite 的增删改默认不立刻写磁盘,要 commit(提交)才真正生效。

五、ORM:用 Python 对象操作数据库,少写一半 SQL

上面直接写 SQL 虽然能用,但有三个痛点:① 容易拼错 SQL 字符串;② 结果是一堆裸数据,不是 Python 对象;③ 换数据库(SQLite → MySQL)时 SQL 语法可能有差异要重写。

ORM(Object-Relational Mapping,对象关系映射) 就是来解决这些的。一句话:ORM 是"翻译官",让你用 Python 类来操作数据库,它帮你翻译成 SQL。
#mermaid-svg-M9spJmawphAPb8fS{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-M9spJmawphAPb8fS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-M9spJmawphAPb8fS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-M9spJmawphAPb8fS .error-icon{fill:#552222;}#mermaid-svg-M9spJmawphAPb8fS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-M9spJmawphAPb8fS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-M9spJmawphAPb8fS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-M9spJmawphAPb8fS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-M9spJmawphAPb8fS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-M9spJmawphAPb8fS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-M9spJmawphAPb8fS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-M9spJmawphAPb8fS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-M9spJmawphAPb8fS .marker.cross{stroke:#333333;}#mermaid-svg-M9spJmawphAPb8fS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-M9spJmawphAPb8fS p{margin:0;}#mermaid-svg-M9spJmawphAPb8fS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-M9spJmawphAPb8fS .cluster-label text{fill:#333;}#mermaid-svg-M9spJmawphAPb8fS .cluster-label span{color:#333;}#mermaid-svg-M9spJmawphAPb8fS .cluster-label span p{background-color:transparent;}#mermaid-svg-M9spJmawphAPb8fS .label text,#mermaid-svg-M9spJmawphAPb8fS span{fill:#333;color:#333;}#mermaid-svg-M9spJmawphAPb8fS .node rect,#mermaid-svg-M9spJmawphAPb8fS .node circle,#mermaid-svg-M9spJmawphAPb8fS .node ellipse,#mermaid-svg-M9spJmawphAPb8fS .node polygon,#mermaid-svg-M9spJmawphAPb8fS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-M9spJmawphAPb8fS .rough-node .label text,#mermaid-svg-M9spJmawphAPb8fS .node .label text,#mermaid-svg-M9spJmawphAPb8fS .image-shape .label,#mermaid-svg-M9spJmawphAPb8fS .icon-shape .label{text-anchor:middle;}#mermaid-svg-M9spJmawphAPb8fS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-M9spJmawphAPb8fS .rough-node .label,#mermaid-svg-M9spJmawphAPb8fS .node .label,#mermaid-svg-M9spJmawphAPb8fS .image-shape .label,#mermaid-svg-M9spJmawphAPb8fS .icon-shape .label{text-align:center;}#mermaid-svg-M9spJmawphAPb8fS .node.clickable{cursor:pointer;}#mermaid-svg-M9spJmawphAPb8fS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-M9spJmawphAPb8fS .arrowheadPath{fill:#333333;}#mermaid-svg-M9spJmawphAPb8fS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-M9spJmawphAPb8fS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-M9spJmawphAPb8fS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-M9spJmawphAPb8fS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-M9spJmawphAPb8fS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-M9spJmawphAPb8fS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-M9spJmawphAPb8fS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-M9spJmawphAPb8fS .cluster text{fill:#333;}#mermaid-svg-M9spJmawphAPb8fS .cluster span{color:#333;}#mermaid-svg-M9spJmawphAPb8fS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-M9spJmawphAPb8fS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-M9spJmawphAPb8fS rect.text{fill:none;stroke-width:0;}#mermaid-svg-M9spJmawphAPb8fS .icon-shape,#mermaid-svg-M9spJmawphAPb8fS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-M9spJmawphAPb8fS .icon-shape p,#mermaid-svg-M9spJmawphAPb8fS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-M9spJmawphAPb8fS .icon-shape .label rect,#mermaid-svg-M9spJmawphAPb8fS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-M9spJmawphAPb8fS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-M9spJmawphAPb8fS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-M9spJmawphAPb8fS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-M9spJmawphAPb8fS .blue>*{fill:#4E79A7!important;color:#fff!important;stroke:none!important;}#mermaid-svg-M9spJmawphAPb8fS .blue span{fill:#4E79A7!important;color:#fff!important;stroke:none!important;}#mermaid-svg-M9spJmawphAPb8fS .blue tspan{fill:#fff!important;}#mermaid-svg-M9spJmawphAPb8fS .orange>*{fill:#F28E2B!important;color:#fff!important;stroke:none!important;}#mermaid-svg-M9spJmawphAPb8fS .orange span{fill:#F28E2B!important;color:#fff!important;stroke:none!important;}#mermaid-svg-M9spJmawphAPb8fS .orange tspan{fill:#fff!important;}#mermaid-svg-M9spJmawphAPb8fS .green>*{fill:#59A14F!important;color:#fff!important;stroke:none!important;}#mermaid-svg-M9spJmawphAPb8fS .green span{fill:#59A14F!important;color:#fff!important;stroke:none!important;}#mermaid-svg-M9spJmawphAPb8fS .green tspan{fill:#fff!important;} ORM 翻译
ORM 翻译回来
Python 类 对象
SQL 语句
数据库

Python 生态最主流的 ORM 是 SQLAlchemy。上一课的 SQL 写法,用 SQLAlchemy 变成这样:

python 复制代码
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import declarative_base, sessionmaker

# 1. 连数据库(URL 里写 SQLite,换成 MySQL 也基本不动别的)
engine = create_engine("sqlite:///chat.db")
Base = declarative_base()

# 2. 一个 Python 类 = 一张表
class ChatRecord(Base):
    __tablename__ = "chat_records"       # 表名
    id = Column(Integer, primary_key=True)   # 主键
    question = Column(String)
    answer = Column(String)

# 3. 真正在数据库里建表
Base.metadata.create_all(engine)

# 4. 会话(Session)= 和数据库对话的"通道"
Session = sessionmaker(bind=engine)
session = Session()

# 5. 增:new 一个对象,add + commit
record = ChatRecord(question="什么是 FastAPI?", answer="一个现代 Python Web 框架")
session.add(record)
session.commit()

# 6. 查:直接用类查询,拿到的是 Python 对象列表
records = session.query(ChatRecord).all()
for r in records:
    print(r.id, r.question, r.answer)

ORM 的核心洞察 :ChatRecord 这个 Python 类,和数据库里的 chat_records 表是"一一对应"的------类的每个字段对应表的每一列,类的每个实例对应表的一行。你操作 Python 对象,ORM 负责把它翻译成 SQL 执行,再把结果翻译回 Python 对象。你几乎不用手写 SQL 了。

换数据库时,只要把 create_engine("sqlite:///chat.db") 里的 URL 换成 mysql+pymysql://user:pass@host/db,其余代码基本不变------这就是 ORM 的"数据库无关性"红利。

六、完整实战:把问答历史存进 FastAPI

现在把 ORM 接进上一课的 FastAPI 接口,让它"记住"每次问答。

python 复制代码
from fastapi import FastAPI, Depends
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import declarative_base, sessionmaker, Session
from pydantic import BaseModel

# ---- 数据库部分(和上一节一样)----
engine = create_engine("sqlite:///chat.db")
Base = declarative_base()

class ChatRecord(Base):
    __tablename__ = "chat_records"
    id = Column(Integer, primary_key=True)
    question = Column(String)
    answer = Column(String)

Base.metadata.create_all(engine)
SessionLocal = sessionmaker(bind=engine)

# ---- FastAPI 部分 ----
app = FastAPI()

class Question(BaseModel):
    text: str

# 依赖注入:每个请求拿到一个数据库会话,用完自动关闭
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

@app.post("/ask")
def ask(q: Question, db: Session = Depends(get_db)):
    answer = f"你问的是:{q.text}"          # 真实项目里这里调大模型
    record = ChatRecord(question=q.text, answer=answer)  # 存历史
    db.add(record)
    db.commit()
    return {"question": q.text, "answer": answer}

@app.get("/history")
def history(db: Session = Depends(get_db)):
    records = db.query(ChatRecord).order_by(ChatRecord.id.desc()).all()  # 按时间倒序
    return [{"id": r.id, "question": r.question, "answer": r.answer} for r in records]

逐行讲清新出现的两个概念:

  1. get_db + Depends(依赖注入) :这是 FastAPI 管理数据库会话的标准写法。Depends(get_db) 的意思是"这个 db 参数不要调用方传,由 FastAPI 自动调 get_db 生成"。yield db 在请求处理完后自动执行 finally 里的 db.close()------每个请求用完就关连接,不泄漏。
  2. order_by(ChatRecord.id.desc()):按 id 倒序排列,最新的问答排最前------查"最近聊天记录"就靠它。

现在接口有了"记忆":POST /ask 会存一条记录,GET /history 能查回所有历史。重启服务、重启电脑,数据都还在 chat.db 文件里。
#mermaid-svg-Kzc8GtzDIlPDLbMY{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Kzc8GtzDIlPDLbMY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Kzc8GtzDIlPDLbMY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Kzc8GtzDIlPDLbMY .error-icon{fill:#552222;}#mermaid-svg-Kzc8GtzDIlPDLbMY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Kzc8GtzDIlPDLbMY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Kzc8GtzDIlPDLbMY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Kzc8GtzDIlPDLbMY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Kzc8GtzDIlPDLbMY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Kzc8GtzDIlPDLbMY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Kzc8GtzDIlPDLbMY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Kzc8GtzDIlPDLbMY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Kzc8GtzDIlPDLbMY .marker.cross{stroke:#333333;}#mermaid-svg-Kzc8GtzDIlPDLbMY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Kzc8GtzDIlPDLbMY p{margin:0;}#mermaid-svg-Kzc8GtzDIlPDLbMY .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Kzc8GtzDIlPDLbMY .cluster-label text{fill:#333;}#mermaid-svg-Kzc8GtzDIlPDLbMY .cluster-label span{color:#333;}#mermaid-svg-Kzc8GtzDIlPDLbMY .cluster-label span p{background-color:transparent;}#mermaid-svg-Kzc8GtzDIlPDLbMY .label text,#mermaid-svg-Kzc8GtzDIlPDLbMY span{fill:#333;color:#333;}#mermaid-svg-Kzc8GtzDIlPDLbMY .node rect,#mermaid-svg-Kzc8GtzDIlPDLbMY .node circle,#mermaid-svg-Kzc8GtzDIlPDLbMY .node ellipse,#mermaid-svg-Kzc8GtzDIlPDLbMY .node polygon,#mermaid-svg-Kzc8GtzDIlPDLbMY .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Kzc8GtzDIlPDLbMY .rough-node .label text,#mermaid-svg-Kzc8GtzDIlPDLbMY .node .label text,#mermaid-svg-Kzc8GtzDIlPDLbMY .image-shape .label,#mermaid-svg-Kzc8GtzDIlPDLbMY .icon-shape .label{text-anchor:middle;}#mermaid-svg-Kzc8GtzDIlPDLbMY .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Kzc8GtzDIlPDLbMY .rough-node .label,#mermaid-svg-Kzc8GtzDIlPDLbMY .node .label,#mermaid-svg-Kzc8GtzDIlPDLbMY .image-shape .label,#mermaid-svg-Kzc8GtzDIlPDLbMY .icon-shape .label{text-align:center;}#mermaid-svg-Kzc8GtzDIlPDLbMY .node.clickable{cursor:pointer;}#mermaid-svg-Kzc8GtzDIlPDLbMY .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Kzc8GtzDIlPDLbMY .arrowheadPath{fill:#333333;}#mermaid-svg-Kzc8GtzDIlPDLbMY .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Kzc8GtzDIlPDLbMY .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Kzc8GtzDIlPDLbMY .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Kzc8GtzDIlPDLbMY .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Kzc8GtzDIlPDLbMY .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Kzc8GtzDIlPDLbMY .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Kzc8GtzDIlPDLbMY .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Kzc8GtzDIlPDLbMY .cluster text{fill:#333;}#mermaid-svg-Kzc8GtzDIlPDLbMY .cluster span{color:#333;}#mermaid-svg-Kzc8GtzDIlPDLbMY div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Kzc8GtzDIlPDLbMY .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Kzc8GtzDIlPDLbMY rect.text{fill:none;stroke-width:0;}#mermaid-svg-Kzc8GtzDIlPDLbMY .icon-shape,#mermaid-svg-Kzc8GtzDIlPDLbMY .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Kzc8GtzDIlPDLbMY .icon-shape p,#mermaid-svg-Kzc8GtzDIlPDLbMY .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Kzc8GtzDIlPDLbMY .icon-shape .label rect,#mermaid-svg-Kzc8GtzDIlPDLbMY .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Kzc8GtzDIlPDLbMY .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Kzc8GtzDIlPDLbMY .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Kzc8GtzDIlPDLbMY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-Kzc8GtzDIlPDLbMY .blue>*{fill:#4E79A7!important;color:#fff!important;stroke:none!important;}#mermaid-svg-Kzc8GtzDIlPDLbMY .blue span{fill:#4E79A7!important;color:#fff!important;stroke:none!important;}#mermaid-svg-Kzc8GtzDIlPDLbMY .blue tspan{fill:#fff!important;}#mermaid-svg-Kzc8GtzDIlPDLbMY .orange>*{fill:#F28E2B!important;color:#fff!important;stroke:none!important;}#mermaid-svg-Kzc8GtzDIlPDLbMY .orange span{fill:#F28E2B!important;color:#fff!important;stroke:none!important;}#mermaid-svg-Kzc8GtzDIlPDLbMY .orange tspan{fill:#fff!important;}#mermaid-svg-Kzc8GtzDIlPDLbMY .green>*{fill:#59A14F!important;color:#fff!important;stroke:none!important;}#mermaid-svg-Kzc8GtzDIlPDLbMY .green span{fill:#59A14F!important;color:#fff!important;stroke:none!important;}#mermaid-svg-Kzc8GtzDIlPDLbMY .green tspan{fill:#fff!important;} 存一条
查询
返回记录列表
POST ask
ChatRecord 对象
写入 chat.db
GET history

七、安全必修课:SQL 注入是怎么发生的

写数据库,绕不开一个安全概念------SQL 注入。它是最经典、危害最大的 Web 漏洞之一,理解它比会写 CRUD 更重要。

先说它是怎么发生的。 假设你写了个"按名字查用户"的接口,用字符串拼接 SQL:

python 复制代码
# ❌ 危险写法:字符串拼接
name = input("输入用户名:")          # 用户输入:' OR '1'='1
sql = f"SELECT * FROM users WHERE name = '{name}'"

如果用户输入的是 ' OR '1'='1,拼出来的 SQL 就变成:

sql 复制代码
SELECT * FROM users WHERE name = '' OR '1'='1'

'1'='1' 永远为真,于是 WHERE 条件失效------这条 SQL 会把整张表的所有用户都查出来 。如果换成 ' OR '1'='1'; DROP TABLE users; --,甚至能删库。这就是注入:用户输入被当成 SQL 代码执行了,而不是被当成普通数据。

怎么防?------参数化查询(也叫预编译)。 关键就一条:把"SQL 结构"和"用户数据"分开,数据永远不参与 SQL 语法拼接。

python 复制代码
# ✅ 安全写法:? 占位符 + 参数元组
name = input("输入用户名:")
cur.execute("SELECT * FROM users WHERE name = ?", (name,))

这里的 ? 是一个占位符 ,数据库先编译好 SQL 结构,再把 name 当作纯数据 填进去------即使 name 里含 ' OR '1'='1,它也只是一个字符串字面量,不会被当成 SQL 执行。SQLAlchemy 的 ORM 默认就是参数化查询,所以你用 ORM 写代码,天然免疫大部分注入。

记住这条铁律 :永远不要用字符串拼接(+ / f-string)拼 SQL 和用户输入 。要么用 ? 占位符,要么用 ORM。这是从入门到"靠谱"的分水岭。

八、常见坑与避坑指南

坑 原因 解决方案
增删改后数据没存进去 SQLite 需要 commit() 才写盘 增删改后记得 session.commit()
用字符串拼接 SQL 触发 SQL 注入 用 ? 占位符或 ORM
忘写主键 表无法唯一标识记录 每张表都设 id 主键
数据库连接不关 连接泄漏,越用越慢 用 Depends(get_db) + finally 关闭
换数据库要重写 SQL 手写 SQL 语法有差异 用 ORM,只改连接 URL
并发写同一行 两个请求同时改一条数据 用事务(transaction),后续进阶篇讲

总结:一张图带走

这课的核心,一句话串起来:

  • 内存会失忆,数据库是笔记本 :数据写进磁盘(.db 文件)才能永久保存,这叫持久化。
  • 关系型数据库 = Excel:表 / 行 / 列 / 主键 / 外键,用 SQL 做增删改查(CRUD)。
  • SQLite 零安装 :Python 内置 sqlite3,学习阶段最省事,数据就是一个文件。
  • ORM 是翻译官:用 Python 类操作数据库,SQLAlchemy 帮你翻译成 SQL,还能免疫大部分 SQL 注入。
  • FastAPI + 数据库 = 有记忆的后端 :Depends(get_db) 管理连接,POST /ask 存、GET /history 查。
  • 安全铁律 :永远不要拼接 SQL,用参数化查询(? 占位符)或 ORM。

三个立即可用的行动:

  1. 把第四、五、六节的代码亲手跑一遍(SQLite 零安装,直接能跑),感受"存进去 → 查出来";
  2. 给上一课的 /ask 接口加上 /history 查询,真正拥有自己的"有记忆的 AI 后端";
  3. 故意用字符串拼接写一个查询,再输入 ' OR '1'='1,亲眼看看 SQL 注入有多危险,再用参数化查询修掉它。

如果这篇对你有帮助,点个赞、收藏一下 ,下一篇我们讲部署上线------把这个带数据库的 AI 后端真正跑起来给别人用。

关注我,这个系列共 20 篇:Python 语法 → FastAPI 实战 → 数据库 → 部署上线 → AI 应用后端架构,一路带你到能独立写生产级后端。

你写数据库时踩过什么坑?是忘 commit() 导致数据"凭空消失",还是踩过 SQL 注入?评论区聊聊,我挨个回。

(本文代码基于 Python 3.10+ / SQLAlchemy 2.0 / sqlite3 内置模块撰写,有版本差异评论区提醒我。)

相关推荐
cspttty1 小时前
2027秋招市场调研岗:统计学专业如何补齐业务、SQL与研究交付能力
大数据·数据库
BD_Marathon1 小时前
Milvus数据库的DML操作
数据库·milvus
其实防守也摸鱼1 小时前
存储管理:页面置换算法学习任务(测验题要求给出置换页面顺序,缺页次数,缺页率)
android·前端·数据库·学习·oracle·自动化
cspttty1 小时前
2027校招:大数据管理与应用专业如何匹配电商运营与供应链管培生能力要求
大数据·数据库
ss2731 小时前
AI时代首先数据库-PostgreSQL实战笔记 | 12 PostgreSQL数据目录与重要文件结构
数据库·笔记·postgresql
paopaokaka_luck2 小时前
校园新闻推荐系统(DeepSeek新闻助手、个性化推荐算法、 Redis热词排行榜、ECharts运营分析、互动与实时通知、新闻运营闭环)
java·spring boot·mysql·spring·tomcat·echarts·intellij idea
RestCloud2 小时前
数据同步零中断:ETL服务宕机重启后异常任务自动重跑
数据仓库·mysql·etl·etlcloud·数据传输·etl工具
小天源2 小时前
智叟云集政策监测技术方案——原文解析、证据提取与可核验摘要
java·服务器·数据库
凤山老林2 小时前
从零搭建企业 OAuth2 授权中心:Spring Boot 集成 Spring Authorization Server 与
数据库·spring boot·spring