背景:文档结构与主键核心机制
1 ) 文档主键(_id)的本质
技术简注:_id是MongoDB文档的全局唯一标识符,功能等同于SQL数据库的主键。
-
强制性唯一标识
- 每篇文档必须包含
_id字段,值在集合内绝对唯一 - 支持除数组外所有数据类型:整型、UUID、字符串或复合主键(嵌套文档)
- 每篇文档必须包含
-
ObjectId自动化生成
字节段 长度 作用 时间戳 4 Unix秒级时间( getTimestamp()提取)机器ID 5 生成主机的唯一标识符 计数器 3 同一秒内的防重复序列 // 生成与解析ObjectId const newId = new ObjectId(); console.log(newId.getTimestamp()); // 输出:ISODate("2023-10-05T08:30:00Z") -
时钟偏差风险
分布式系统中客户端时钟不同步时,生成顺序可能与创建时间顺序不一致。
方法:文档创建操作核心技术
1 ) 单文档插入(insertOne)
db.accounts.insertOne(
{
_id: "account1", // 显式主键(可选)
name: "Alice",
balance: 1000
},
{ writeConcern: { w: "majority" } } // 安全写级别配置
)
特性:
- 自动建集合(目标集合不存在时)
- 主键冲突报错:
E11000 duplicate key error - 返回结构:
{ acknowledged: true, insertedId: "account1" }
2 ) 批量插入(insertMany)
// 无序写入(ordered:false优化吞吐量)
db.accounts.insertMany(
[
{ _id: "dupKey", name: "ErrorDoc" },
{ name: "ValidDoc" } // 此文档成功写入
],
{ ordered: false }
)
| 操作模式 | 错误处理机制 | 适用场景 |
|---|---|---|
ordered:true(默认) | 遇错立即停止 | 事务性连续写入 |
ordered:false | 继续执行非冲突文档 | 大数据批量导入 |
3 ) 通用插入命令(insert)
// 单文档与批量统一接口
db.accounts.insert({ name: "SingleDoc" }) // 单文档
db.accounts.insert([ { _id: "doc1" }, { _id: "doc2" } ]) // 多文档
与insertMany差异:
- 支持
explain()执行计划分析 - 返回格式:
WriteResult({ "nInserted": N })
- save命令的智能逻辑
// 根据_id存在性自动选择操作
db.accounts.save({ name: "New" }) // 无_id → insert操作
db.accounts.save({ _id: "exist", name: "Update" }) // 有_id → update操作
命令对比与特性总结
| 命令 | 适用场景 | 返回结果格式 | 是否支持explain |
|---|---|---|---|
insertOne | 单文档插入 | {acknowledged, insertedId} | ❌ |
insertMany | 多文档插入 | {acknowledged, insertedIds} | ❌ |
insert | 单/多文档插入 | WriteResult / BulkWriteResult | ✅ |
要点
insertOne适合单文档事务写入insertMany+ordered:false提升批量吞吐量insert支持执行计划分析save根据_id自动路由操作类型
ObjectId 的显式操作
// 生成新ObjectId
const newId = ObjectId() // 输出:ObjectId("507f1f77bcf86cd799439011")
// 解析时间戳
const creationTime = newId.getTimestamp() // 返回ISO日期字符串
// 从字符串构造ObjectId
const customId = ObjectId("507f1f77bcf86cd799439011")
案例:复合主键实战与陷阱
1 ) 复合主键定义
db.accounts.insert({
_id: {
type: "savings", // 字段顺序敏感!
number: "001"
},
name: "ComplexKey"
})
2 ) 主键顺序陷阱
// 字段顺序变化生成新主键!
db.accounts.insert({
_id: {
number: "001", // 顺序颠倒
type: "savings"
},
name: "OrderMatters"
}) // 成功写入(非冲突)
核心规则:
要点
- 复合主键的唯一性由字段键值对的有序结构决定
- 字段顺序变化将被视为不同主键
- 生产环境建议固定字段顺序(如Schema强制约束)
方法:文档读取核心技术栈
1 ) find查询引擎与游标
// 链式操作:查询→排序→分页
const cursor = db.accounts.find({ balance: { $gt: 500 } })
cursor.sort({ name: 1 })
.skip(10)
.limit(5)
.forEach(printjson);
2 ) 投射(Projection)性能优化
技术简注:投射通过字段筛选减少网络传输。
// 只返回name和balance(排除_id)
db.accounts.find(
{ type: "checking" },
{ name: 1, balance: 1, _id: 0 } // 1=包含, 0=排除
)
性能影响:
- 减少70%+网络传输开销
- 嵌套文档支持:
{ "contact.phone": 1 }
3 ) 查询操作符体系
| 类型 | 操作符示例 | 作用 |
|---|---|---|
| 比较操作符 | $eq, $gt, $in | 值比较 |
| 逻辑操作符 | $and, $or, $not | 条件组合 |
| 数组操作符 | $elemMatch, $size | 数组字段查询 |
| 元素操作符 | $exists, $type | 字段存在性/类型检查 |
案例:NestJS集成MongoDB实战
1 ) 数据模型定义
// account.schema.ts
@Schema()
export class Account extends Document {
@Prop({ type: Object, required: true })
_id: { type: string; number: string }; // 复合主键
@Prop({ required: true })
name: string;
@Prop({ default: 0 })
balance: number;
}
2 ) 服务层CRUD实现
@Injectable()
export class AccountsService {
constructor(
@InjectModel(Account.name) private accountModel: Model<Account>
) {}
// 批量插入(无序)
async bulkCreate(accounts: AccountDto[]) {
return this.accountModel.insertMany(accounts, { ordered: false });
}
// 复合主键查询
async findByCompositeKey(type: string, number: string) {
return this.accountModel.findOne({
_id: { type, number } // 字段顺序必须一致!
});
}
}
最佳实践与错误处理
1 ) 错误处理标准化
| 操作 | 成功响应格式 | 主键冲突错误 |
|---|---|---|
insertOne | {acknowledged:true, insertedId} | WriteError对象 |
insertMany | {acknowledged:true, insertedIds} | BulkWriteError含错误统计 |
// NestJS错误处理范例
try {
await collection.insertMany(data, { ordered: false });
} catch (error) {
if (error.code === 11000) { // 主键冲突代码
const dupKeys = error.keyValue;
throw new ConflictException(`重复主键: ${dupKeys}`);
}
}
2 )核心性能优化策略
- 主键设计:
- 优先使用ObjectId规避唯一性管理负担
- 复合主键需严格字段顺序管理
- 写入优化:
/* 性能诊断语句 */ db.runCommand({ explain: { insert: "accounts", documents: [...] }, verbosity: "executionStats" })- 金融系统启用安全写:
{ w: "majority", j: true }(日志持久化) - 控制写入操作的确认机制,通过
w参数指定:w: 1(默认):主节点确认即返回w: "majority":多数节点确认j: true:写入日志后返回
- 金融系统启用安全写:
- 查询优化:
- 投射过滤减少70%+网络传输
- 使用
snapshot()防止重复读取,batchSize()控制网络传输量db.accounts.find().snapshot().batchSize(50) - 游标分批加载:
cursor.batchSize(50)
全局最佳实践总结
- 大规模写入使用
insertMany+ordered:false - 复合主键字段顺序需通过Schema强制约束
- 查询必用投射减少传输开销
- 金融系统启用
writeConcern: majority - 错误处理优先识别
code:11000主键冲突
1547

被折叠的 条评论
为什么被折叠?



