title: 异步之舞:Motor驱动与MongoDB的CRUD交响曲

date: 2025/05/19 15:30:10

updated: 2025/05/19 15:30:10

author: cmdragon

excerpt:

Motor 异步驱动是专为 Python 异步框架设计的 MongoDB 连接器,基于 asyncio 实现非阻塞 I/O 操作,提升 FastAPI 的并发处理能力。通过 CRUD 操作示例,展示了如何使用 insert_onefindupdate_onedelete 方法进行文档的创建、查询、更新和删除。聚合管道用于统计用户年龄分布,索引优化策略包括单字段索引和复合索引,遵循 ESR 规则提升查询性能。常见报错如 ServerSelectionTimeoutErrorValidationErrorDuplicateKeyError 的处理方法也进行了详细说明。

categories:

  • 后端开发
  • FastAPI

tags:

  • Motor
  • 异步驱动
  • CRUD操作
  • MongoDB
  • FastAPI
  • 索引优化
  • 聚合管道


扫描二维码

关注或者微信搜一搜:编程智域 前端至全栈交流与成长

探索数千个预构建的 AI 应用,开启你的下一个伟大创意https://tools.cmdragon.cn/

第二章:Motor 异步驱动与 CRUD 操作实践

1. Motor 异步驱动原理

MongoDB 的异步驱动 Motor 是专为 Python 异步框架设计的数据库连接器,其底层基于 asyncio 实现非阻塞 I/O 操作。与同步驱动相比,Motor

在执行数据库操作时不会阻塞事件循环,这使得 FastAPI 能够同时处理更多并发请求。

示例场景:想象餐厅里一个服务员(事件循环)同时服务多桌客人(请求),当某桌需要等待厨房做菜(数据库操作)时,服务员会先去服务其他餐桌,等厨房完成后再回来继续服务。

# 安装依赖
# pip install fastapi==0.78.0 motor==2.5.0 pydantic==1.10.7 from fastapi import FastAPI
from motor.motor_asyncio import AsyncIOMotorClient
from pydantic import BaseModel app = FastAPI() # MongoDB 连接配置
DATABASE_URL = "mongodb://localhost:27017"
client = AsyncIOMotorClient(DATABASE_URL)
db = client["mydatabase"]
users_collection = db["users"] class UserCreate(BaseModel):
name: str
age: int
email: str class UserResponse(UserCreate):
id: str

2. CRUD 操作实现

2.1 创建文档

使用 insert_one 方法实现数据插入:

@app.post("/users", response_model=UserResponse)
async def create_user(user: UserCreate):
user_dict = user.dict()
result = await users_collection.insert_one(user_dict)
created_user = await users_collection.find_one({"_id": result.inserted_id})
return {**created_user, "id": str(created_user["_id"])}

2.2 查询文档

实现多条件查询和分页:

@app.get("/users", response_model=list[UserResponse])
async def get_users(skip: int = 0, limit: int = 10):
users = []
query = {"age": {"$gte": 18}} # 查询18岁以上用户
projection = {"_id": 0, "id": {"$toString": "$_id"}, "name": 1, "age": 1} # 字段投影 async for user in users_collection.find(query).skip(skip).limit(limit).project(projection):
users.append(user) return users

2.3 更新文档

使用原子操作实现安全更新:

@app.put("/users/{user_id}")
async def update_user(user_id: str, user_update: UserCreate):
update_result = await users_collection.update_one(
{"_id": user_id},
{"$set": user_update.dict(exclude_unset=True)}
)
return {"modified_count": update_result.modified_count}

2.4 删除文档

软删除实现示例:

@app.delete("/users/{user_id}")
async def delete_user(user_id: str):
result = await users_collection.update_one(
{"_id": user_id},
{"$set": {"is_deleted": True}}
)
return {"modified_count": result.modified_count}

3. 聚合管道应用

统计用户年龄分布:

@app.get("/users/age-stats")
async def get_age_stats():
pipeline = [
{"$match": {"is_deleted": {"$ne": True}}},
{"$group": {
"_id": None,
"averageAge": {"$avg": "$age"},
"minAge": {"$min": "$age"},
"maxAge": {"$max": "$age"}
}}
] result = await users_collection.aggregate(pipeline).to_list(1)
return result[0] if result else {}

4. 索引优化策略

4.1 单字段索引

# 创建索引
async def create_indexes():
await users_collection.create_index("email", unique=True)
await users_collection.create_index([("name", "text")])

4.2 复合索引

# 针对常用查询字段创建复合索引
await users_collection.create_index([("age", 1), ("is_deleted", 1)])

索引优化建议:

  1. 优先为查询条件字段建立索引
  2. 复合索引字段顺序遵循 ESR 规则(等值→排序→范围)
  3. 使用覆盖索引减少文档读取

课后 Quiz

Q1:Motor 的异步特性如何提升性能?

A) 减少数据库连接数

B) 允许单线程处理多个并发请求

C) 自动压缩传输数据

D) 缓存查询结果

答案

B) 正确。异步驱动通过非阻塞 I/O 允许事件循环在处理数据库操作等待期间继续处理其他请求,提升并发处理能力。

Q2:如何防止重复插入相同 email 的用户?

A) 添加唯一索引

B) 在业务逻辑中检查

C) 使用事务

D) 以上都是

答案

D) 正确。最佳实践是同时使用数据库唯一索引(A)和业务逻辑校验(B),在并发场景下可配合事务(C)保证数据一致性。


常见报错处理

报错1:ServerSelectionTimeoutError

现象:连接 MongoDB 超时

motor.motor_asyncio.ServerSelectionTimeoutError: ...

解决

  1. 检查 MongoDB 服务是否运行
  2. 确认连接端口(默认27017)
  3. 验证防火墙设置

报错2:ValidationError

现象:请求参数校验失败

{
"detail": [
{
"loc": [
"body",
"age"
],
"msg": "field required",
"type": "value_error.missing"
}
]
}

处理

  1. 检查请求体是否符合 Pydantic 模型定义
  2. 使用 exclude_unset=True 处理可选字段
  3. 添加自定义验证器

报错3:DuplicateKeyError

现象:违反唯一性约束

pymongo.errors.DuplicateKeyError: E11000 duplicate key error...

处理

  1. 在插入前检查唯一字段
  2. 使用 update_one 配合 upsert=True
  3. 添加唯一索引确保数据一致性

通过本章学习,您将掌握 FastAPI 与 MongoDB 集成的核心技能。建议在开发过程中使用 MongoDB Compass 可视化工具实时观察数据变化,并结合

Python 的异步特性进行压力测试,深入理解异步编程的优势。

余下文章内容请点击跳转至 个人博客页面 或者 扫码关注或者微信搜一搜:编程智域 前端至全栈交流与成长,阅读完整的文章:异步之舞:Motor驱动与MongoDB的CRUD交响曲 | cmdragon's Blog

往期文章归档:

异步之舞:Motor驱动与MongoDB的CRUD交响曲的更多相关文章

  1. MongoDB的CRUD操作

    1. 前言 在上一篇文章中,我们介绍了MongoDB.现在,我们来看下如何在MongoDB中进行常规的CRUD操作.毕竟,作为一个存储系统,它的基本功能就是对数据进行增删改查操作. MongoDB中的 ...

  2. C#中使用官方驱动操作MongoDB

    想要在C#中使用MongoDB,首先得要有个MongoDB支持的C#版的驱动.C#版的驱动有很多种,如官方提供的,samus. 实现思路大都类似.这里我们先用官方提供的mongo-csharp-dri ...

  3. [转载]在C#中使用官方驱动操作MongoDB

    在C#中使用官方驱动操作MongoDB 8.1)下载安装 想要在C#中使用MongoDB,首先得要有个MongoDB支持的C#版的驱动.C#版的驱动有很多种,如官方提供的,samus. 实现思路大都类 ...

  4. MongoDB简单CRUD场景

    MongoDB简单CRUD命令操作 (1)新建数据库:use 数据库名 (2)显示所有数据库:show dbs; (3)新建集合(两种方式)  隐式创建:在创建集合的同时往集合里面添加数据---db. ...

  5. MongoDB学习-->命令行增删改查&JAVA驱动操作Mongodb

    MongoDB 是一个基于分布式文件存储的数据库. 由 C++ 语言编写.旨在为 WEB 应用提供可扩展的高性能数据存储解决方案. MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关 ...

  6. springboot连接mongodb进行CRUD

    springboot连接mongodb进行CRUD的过程: 在执行以下操作前已安装了mongodb并创建了用户和数据库,使用Robo 3T可成功连接. 1.创建springboot项目,加入以下mav ...

  7. 【翻译】MongoDB指南/CRUD操作(四)

    [原文地址]https://docs.mongodb.com/manual/ CRUD操作(四) 1 查询方案(Query Plans) MongoDB 查询优化程序处理查询并且针对给定可利用的索引选 ...

  8. 【翻译】MongoDB指南/CRUD操作(二)

    [原文地址]https://docs.mongodb.com/manual/ MongoDB CRUD操作(二) 主要内容: 更新文档,删除文档,批量写操作,SQL与MongoDB映射图,读隔离(读关 ...

  9. 【翻译】MongoDB指南/CRUD操作(一)

    [原文地址]https://docs.mongodb.com/manual/ MongoDB CRUD操作(一) 主要内容:CRUD操作简介,插入文档,查询文档. CRUD操作包括创建.读取.更新和删 ...

  10. C#/.NET 使用官方驱动操作MongoDB(一):插入、查询

    概述 想要在C#中使用MongoDB,首先得要有个MongoDB支持的C#版的驱动. C#版的驱动有很多,这里我们先用官方提供的 MongoDB.Driver(使用 Nuget 安装),当前版本为2. ...

随机推荐

  1. 解决 Dell PowerEdge T630 增加第三方 PCIe 设备后制冷系统异常

    博客链接:解决 Dell PowerEdge T630 增加第三方 PCIe 设备后制冷系统异常 配置 Device: Dell PowerEdge T630 CPU: Intel(R) Xeon(R ...

  2. CMD批处理脚本+VBScript脚本+Potplayer 实现文件夹内所有视频的截图任务(指定时间点)

    实现自动化视频截图,一般会直接借视频编解码如FFmpeg,动用相关函数来实现,直接从解码源头设计程序.然而我没有接触过FFmpeg,借助cmd批处理,以及vbs,还有现成的播放器potplayer,一 ...

  3. gorm事务的rollback和commit操作

    一个事务内同一操作二次回滚(Rollback)会报错,二次提交(commit)也会报错, 如果回滚完又进行提交操作,一样会报错 循环注意把事务开启tx.Begin放在事务操作前边,操作完回滚或者提交

  4. 为什么将malloc()和printf()称为不可重入?

    转载自https://mlog.club/article/1807704 在unix系统中,我们知道malloc()是一个不可重入的函数(系统调用).为什么? 类似地,printf()也被认为是不可重 ...

  5. VMware虚拟化的CPU调度原理及实践建议

    简介: ESXi的CPU调度原理及实践建议 ESXi的CPU调度原理 CPU调度器的设计目标 公平性:确保虚机按照各自配置的份额占用物理CPU.吞吐量:最大化物理CPU的使用率.响应性:vCPU从'就 ...

  6. EBUSY: resource busy or locked, rmdir

    方案一: 方案二: !!! 出现问题后,千万不要忽略npm提示你的警告... 如果以上两种方案还未解决,那么大概率是因为你的npm版本较低导致的,升级你的npm. cnpm install -g np ...

  7. RuoYi-vue配置记录

    如果这个项目能顺利运行,标志着Springboot+vue的前后端环境都配好了. 一.官方文档 若依官方文档:介绍 | RuoYi,在这个地方克隆/下载项目源代码https://gitee.com/y ...

  8. 从零开始:在Qt中使用OpenGL绘制指南

    本文只介绍基本的 QOpenGLWidget 和 QOpenGLFunctions 的使用,想要学习 OpenGL 的朋友,建议访问经典 OpenGL 学习网站:LearnOpenGL CN 本篇文章 ...

  9. 『Plotly实战指南』--箱线图绘制与应用

    在数据可视化领域,箱线图(Box Plot)是一种强大的工具,用于展示数据的分布特征.集中趋势以及异常值. 它不仅能够快速揭示数据的偏态.离散程度,还能帮助我们识别潜在的数据问题. 本文将从基础绘制到 ...

  10. SpringBoot接口 - 统一异常处理

    为什么要统一异常处理 如果不统一处理异常,程序开发时就需要在controller层写大量重复的Valid代码, 比如下面这个样子: @Slf4j @RestController public clas ...