Mongo的增删改查
一、MongoDB 是什么?
MongoDB 是一个开源的、面向文档的 NoSQL 数据库,由 C++ 编写,旨在为 Web 应用提供可扩展的高性能数据存储解决方案。
- 核心特点:
- 存储的是 BSON(Binary JSON) 格式的文档,结构灵活。
- 支持动态 schema,无需提前定义表结构。
- 高性能、高可用(副本集)、可水平扩展(分片集群)。
- 支持丰富的查询语言,包括范围查询、正则表达式、聚合管道等。
- ·
二、MongoDB 的数据类型(常用)
MongoDB 支持丰富的数据类型,比 JSON 更强大:
类型 | 说明 |
| 字符串 |
| 整数、浮点数( , , , ) |
| true / false |
| 嵌套文档 |
| 数组,可包含多个值或嵌套文档 |
| 日期时间(毫秒级) |
| 12字节的唯一标识符,常用于 主键 |
| 表示空值 |
| 存储二进制数据 |
| 用于内部操作的时间戳 |
| 正则表达式 |
✅ MongoDB 数据类型与 Java 类型对照表
MongoDB 类型 | 说明 | 示例 | Java 常用类型(Spring Data MongoDB) |
String | UTF-8 编码的字符串,最常用类型 |
, |
|
Number | 数值类型,根据值自动判断:<br>• / (整数)<br>• (浮点数)<br>• (高精度) |
, , | • / <br>• / <br>• / <br>• (高精度金额) |
Boolean | 布尔值,表示真或假 |
, |
/ |
Object | 嵌套文档(子文档),用于结构化数据 |
| 自定义 Java 类(如 ),通过 或普通 POJO 映射 |
Array | 数组,可包含字符串、数字、对象等,支持嵌套 |
, |
(最常用)<br>或 (数组)<br>例如: , |
Date | 日期时间类型,存储 UTC 时间戳(毫秒级精度) |
| • (传统)<br>• (推荐)<br>• (需注意时区处理)<br>• |
ObjectId | 12字节的唯一标识符,MongoDB 默认 主键类型,由时间戳 + 机器信息生成 |
| • (BSON 库)<br>• (存储为十六进制字符串,便于传输)<br>⚠️ 建议:Java 实体中用 ,数据库用 ,通过 Converter 转换 |
Null | 表示空值或字段不存在 |
|
(任何引用类型) |
Binary Data | 存储二进制数据,如图片、文件、加密数据等 |
|
(最常用)<br>或 |
Timestamp | 用于 MongoDB 内部操作的时间戳(如 oplog),不推荐用于业务时间 |
|
(极少用于业务开发) |
Regex | 正则表达式对象,用于模式匹配查询 |
|
(Java 中表示正则) |
示例文档:
{// ObjectId 类型:12字节的唯一标识符,MongoDB 默认主键// 由时间戳 + 机器ID + 进程ID + 计数器生成,全局唯一,避免冲突// 在 Java 中通常映射为 String 或 ObjectId 类型"_id": ObjectId("507f1f77bcf86cd799439011"),// String 类型:UTF-8 编码的字符串// 存储姓名、昵称、标题等文本信息// 在 Java 中对应 String"name": "张三",// NumberInt 类型:32位整数(默认)// 如果数值较大,建议使用 NumberLong(64位)// 在 Java 中对应 int / Integer 或 long / Long"age": 30,// Array 类型:有序列表,可包含相同或不同类型的元素(推荐同类型)// 这里是字符串数组,存储用户的兴趣爱好// 支持 $push, $addToSet, $pull 等数组操作// 在 Java 中通常映射为 List<String>"hobbies": ["reading", "coding"],// Embedded Document(嵌套文档)类型:文档中包含子文档// 用于结构化数据,如地址、配置、用户详情等// 支持通过 "address.city" 这样的点号语法查询// 在 Java 中通常映射为一个独立的类(如 Address.class)"address": {"city": "杭州", // String:城市"district": "余杭区" // String:区县},// Date 类型:ISODate,存储 UTC 时间戳(毫秒级精度)// MongoDB 内部以 64 位整数存储毫秒数,支持时区转换// 在 Java 中对应 java.util.Date 或 java.time.Instant / LocalDateTime// 阿里规范:所有时间字段统一用 UTC 存储,前端展示时转换为本地时间"createdAt": ISODate("2025-01-01T00:00:00Z")
}
三、MongoDB vs Redis vs MySQL
既要读写又要事务:mysql
只要读写速度,不要事务,数据量小:redis
既要读写,数据量又大:mongoDb
对比维度 | MongoDB | Redis | MySQL |
数据模型 | 文档(BSON) | 键值对(支持 String, Hash, List 等) | 关系型表(行和列) |
查询能力 | 强:支持复杂查询、索引、聚合 | 弱:主要基于 key,复杂查询需应用层处理 | 强:SQL,支持 JOIN、事务等 |
性能 | 高读写性能,适合海量数据 | 极高(内存数据库),适合缓存 | 中等,受磁盘和锁机制影响 |
持久化 | 支持(WAL 日志 + 定期刷盘) | 支持(RDB/AOF) | 支持(InnoDB) |
扩展性 | 易水平扩展(Sharding) | 支持集群,但复杂 | 难水平扩展(需中间件如 MyCAT) |
事务支持 | 支持多文档 ACID 事务(4.0+) | 支持简单事务(MULTI/EXEC) | 强事务支持(ACID) |
典型用途 | 内容管理、日志、用户画像 | 缓存、会话存储、排行榜 | 核心交易系统、财务系统 |
✅ MongoDB 的优势总结:
- 比 MySQL 更灵活,适合 schema 频繁变更的场景。
- 比 Redis 存储容量更大,支持复杂查询,适合持久化存储。
- 天然支持嵌套结构,减少 JOIN,适合 JSON 类数据。
四、MongoDB 的典型使用场景
- 内容管理系统(CMS)
- 文章、页面结构多变,适合用文档存储。
- 用户行为日志 & 埋点数据
- 数据量大、写入频繁、schema 不固定。
- 商品详情页(阿里商品中心常用)
- 商品属性复杂、SKU 多,用嵌套文档天然匹配。
- 实时分析系统
- 聚合管道(Aggregation Pipeline)支持复杂统计。
- 物联网(IoT)数据
- 设备上报数据结构多样,写入频率高。
- 微服务架构中的独立数据存储
- 每个服务拥有自己的数据库,避免耦合。
五、MongoDB 增删改查(CRUD)操作(Shell 语法)
假设集合名为 users
。
1. 插入(insert)
// 插入单条
db.users.insertOne({name: "李四",age: 25,city: "北京"
})// 插入多条
db.users.insertMany([{ name: "王五", age: 28, city: "上海" },{ name: "赵六", age: 32, city: "深圳" }
])
2. 查询(find)
查询 ObjectId 类型字段时,必须用 ObjectId("...") 包裹,并加引号。
查询字符串类型字段时,直接用引号包裹字符串
// 查询所有
db.users.find()// id查询
//查询 ObjectId 类型字段时,必须用 ObjectId("...") 包裹,并加引号。
db.crm_company_extensible.findOne({_id: {$eq: ObjectId("68a4279a9e2a6581afa4838d")}});// 条件查询
db.users.find({ age: { $gt: 26 } }) 查询年龄大于26岁的数据
//查询name=xx的数据 下面两种写法等价
db.crm_company_extensible.find({name:{$eq:"成都京东楠苑贸易有限公司"}});
db.crm_company_extensible.find({name:"成都京东楠苑贸易有限公司"});// 投影(只返回某些字段) 第二个{},就是你要返回的字段,写1代表要,0代表不要
db.users.find({ city: "北京" }, { name: 1, age: 1, _id: 0 })
db.crm_company_extensible.find({name:"成都京东楠苑贸易有限公司"},{name:1,mainStaffInfo:1});
解释:
第一个 {}:查询条件(filter)—— 找出 city 是 "北京" 的文档。
第二个 {}:投影(projection)—— 控制返回哪些字段:
1 表示包含该字段
0 表示排除该字段
_id: 0 表示不返回 _id 字段(默认总是返回)// 排序 + 分页
db.users.find().sort({ age: -1 }).limit(2).skip(1)
解释:
.sort({ age: -1 }):按 age 降序排列(1 为升序)
.limit(2):最多返回 2 条
.skip(1):跳过前 1 条(常用于分页)skip + limit 在大数据量下性能较差,建议结合 游标(cursor)或范围查询 优化
// 更优方式:用 age > lastSeenAge 实现“下一页”
db.users.find({ age: { $lt: 30 } }).sort({ age: -1 }).limit(10)// 模糊查询
db.users.find({ name: /李/ })
db.crm_company_extensible.find({ name: /成/ })
{ name: /^李/ } // 以“李”开头
{ name: /李$/ } // 以“李”结尾
{ name: /li/i } // 忽略大小写(i 标志)// 嵌套查询
db.users.find({ "address.city": "杭州" })
db.crm_company_extensible.find({ "basicInfo.approvedTime": "2024-04-30" }) //
嵌套查询
db.users.find({ "address.city": "杭州" })
- 查询
address
是一个嵌套文档,其中city
字段等于 "杭州" 的记录。 - 使用 点号(dot notation) 访问嵌套字段。
✅ 示例文档:
{"name": "张三","address": {"city": "杭州","district": "余杭区"}
}
分页:skip + limit 在大数据量下性能较差,建议结合 游标(cursor)或范围查询 优化
嵌套查询
3. 更新(Update)
操作符,看下面,这个比mysql复杂一点,可以直接介于操作符加减等
// 更新单条(默认只更新匹配的第一条)
db.users.updateOne({ name: "李四" }, //更新条件{ $set: { age: 26, city: "天津" } } //更新age为26,城市为天津
)
db.crm_company_extensible.updateOne({name:"企查查科技股份有限公司"},{$set:{name:"企查查科技股份有限公司1"}})// 更新多条
db.users.updateMany({ age: { $lt: 30 } },{ $inc: { age: 1 } } // 年龄 +1
)
db.crm_company_extensible.updateMany({name:"成都京东楠苑贸易有限公司"},{$set:{name:"成都京东楠苑贸易有限公司1"}})// 替换整个文档
db.users.replaceOne({ name: "赵六" },{ name: "钱七", age: 33, city: "广州" }
)
4. 删除(Delete)
// 删除单条
db.users.deleteOne({ name: "钱七" })
db.crm_company_extensible.deleteOne({name:"成都京东楠苑贸易有限公司1"})// 删除多条
db.users.deleteMany({ city: "深圳" })// 删除所有(慎用)
db.users.deleteMany({})
1. acknowledged
- 类型:布尔值 (
true
或false
) - 含义:
true
表示服务器已经确认了你的删除请求,并且该操作已经被记录到日志中。false
表示服务器没有确认你的删除请求,可能是因为网络问题、超时或其他错误。
2. deletedCount
- 类型:整数
- 含义:
- 这个字段表示实际被删除的文档数量。
- 如果你执行了一个删除操作,但没有任何文档匹配你的查询条件,那么
deletedCount
将会是0
。 - 如果有多个文档匹配你的查询条件并且都被成功删除,那么
deletedCount
将会是你所删除的文档的数量。
六、排序
1
:升序(从小到大)-1
:降序(从大到小)
按年龄升序排列
db.users.find().sort({ age: 1 })按时间倒序(最新在前)
db.users.find().sort({ createdAt: -1 })多字段排序 —— 先按年龄升序,再按分数降序
db.users.find().sort({ age: 1, score: -1 })结合查询条件 + 排序 + 分页(真实生产写法)
// 查询状态为 "active" 的用户,按分数降序,取前 10 条
db.users.find({ status: "active" }).sort({ score: -1 }).limit(10)
七、操作符
在 MongoDB 中,所有以 $
开头的关键词都是“操作符”,用于表达查询条件、更新操作、聚合逻辑等。
常见 $
操作符分类:
1. 查询操作符(Query Operators)
操作符 | 说明 | 示例 |
| 等于 |
|
| 大于 |
|
| 大于等于 |
|
| 小于 |
|
| 在集合中 |
|
| 不在集合中 |
|
| 字段是否存在 |
|
| 正则匹配 |
|
✅ 上面的 /李/
是简写,等价于 { name: { $regex: "李" } }
2. 更新操作符(Update Operators)
操作符 | 说明 | 示例 |
| 设置字段值 |
|
| 删除字段 |
|
| 数值增加 |
|
| 向数组添加元素 |
|
| 从数组删除元素 |
|
| 向数组添加(去重) |
|
🔁 你之前看到的:
{ $set: { age: 26 } } // 修改字段
{ $inc: { age: 1 } } // 年龄 +1
3. 逻辑操作符
操作符 | 说明 |
| 且 |
| 或 |
| 非 |
| 都不满足 |
示例:
db.users.find({$or: [{ age: { $lt: 18 } },{ city: "杭州" }]
})
补充:Java 中如何操作 MongoDB?
在阿里,我们通常使用 Spring Data MongoDB 或 Mongo Java Driver。
// 示例:Spring Data MongoDB
@Repository
public interface UserRepository extends MongoRepository<User, String> {List<User> findByCity(String city);List<User> findByAgeGreaterThan(int age);
}
总结
- MongoDB 是文档数据库,适合存储结构灵活、读写频繁的业务数据。
- 相比 Redis:Mongo 更适合持久化和复杂查询;Redis 更适合高速缓存。
- 相比 MySQL:Mongo 更易扩展、schema 自由;MySQL 更适合强一致性事务。
- 使用场景:日志、内容、用户画像、商品详情等。
- CRUD 操作 简洁直观,支持丰富查询语法。