背景:文檔結構與主鍵核心機制


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 })
  1. 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


要點

  1. insertOne適合單文檔事務寫入
  2. insertMany + ordered:false提升批量吞吐量
  3. insert支持執行計劃分析
  4. 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"
}) // 成功寫入(非衝突)

核心規則:

mongodb學習筆記 - 忘川-hsm的個人空間 -_主鍵

要點

  • 複合主鍵的唯一性由字段鍵值對的有序結構決定
  • 字段順序變化將被視為不同主鍵
  • 生產環境建議固定字段順序(如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)

全局最佳實踐總結

  1. 大規模寫入使用insertMany+ordered:false
  2. 複合主鍵字段順序需通過Schema強制約束
  3. 查詢必用投射減少傳輸開銷
  4. 金融系統啓用writeConcern: majority
  5. 錯誤處理優先識別code:11000主鍵衝突


mongodb學習筆記 - 忘川-hsm的個人空間 -_操作符_02