$sample 是 MongoDB 聚合管道(Aggregation Pipeline)中的一个非常实用的阶段,它的作用是:
从集合中随机抽取指定数量的文档。
✅ 基本语法
js
{ $sample: { size: N } }
size: 你要随机抽取的文档数量。
📌 示例
假设你有一个 questions 集合,里面有 1000 道题,你想随机抽 5 道用于练习:
js
db.questions.aggregate([
{ $sample: { size: 5 } }
])
每次运行都会返回 5 条随机题目,非常适合:
- 随机组卷
- 推荐系统(随机推荐几条内容)
- 抽奖功能
- 数据抽样分析
🔍 它是怎么实现"随机"的?
MongoDB 并不是真的"完全随机读取每一条",而是使用一种高效的算法:
- 如果集合小:直接加载所有文档,随机挑选
- 如果集合大:使用 ** reservoir sampling(蓄水池抽样)** 算法,在不加载全部数据的情况下实现均匀随机
⚡️ 性能很好,即使千万级数据也能快速抽样
🆚 对比:为什么不用 find().limit().skip(Math.random())?
你可能会想:我能不能这样做?
js
const count = await db.questions.countDocuments();
const randomSkip = Math.floor(Math.random() * count);
db.questions.find().skip(randomSkip).limit(5);
❌ 不推荐!原因:
| 问题 | 说明 |
|---|---|
| 不是真正随机 | 只是从某个位置往后取,容易重复或偏移 |
| 性能差 | skip(N) 越大,越慢(要跳过前 N 条) |
| 分布不均 | 数据增删后,随机性变差 |
✅ 而 $sample:
- 真正均匀随机
- 性能稳定
- 自动优化
🧩 实际应用场景
1. 随机出题(考试系统)
js
// 随机抽 10 道单选题
db.questions.aggregate([
{ $match: { type: "single" } },
{ $sample: { size: 10 } }
])
2. 每日推荐(随机推荐 3 篇文章)
js
db.articles.aggregate([
{ $match: { status: "published" } },
{ $sample: { size: 3 } }
])
3. 抽奖系统(从参与用户中抽 1 名幸运儿)
js
db.participants.aggregate([
{ $sample: { size: 1 } }
])
⚠️ 注意事项
- 不能保证绝对唯一:多次调用可能抽到同一条(毕竟是随机)
- 适合中小规模抽样 :
size不宜过大(比如size: 10000可能慢) - 不适用于"分页随机" :如果你要做"第一页随机5条,第二页再随机5条",直接用
$sample每次都行,但无法保证不重复(需要业务层去重)
✅ 最佳实践
js
// 安全做法:先过滤,再抽样
db.questions.aggregate([
{ $match: {
status: "active",
difficulty: "easy"
} },
{ $sample: { size: 5 } }
])
这样既高效又精准。
🎯 总结
| 特性 | 说明 |
|---|---|
| 作用 | 随机抽取 N 条文档 |
| 语法 | { $sample: { size: N } } |
| 优点 | 真随机、性能好、语法简单 |
| 适用场景 | 抽题、推荐、抽奖、数据抽样 |
| 替代方案 | ❌ skip(random) 不推荐 |
如果你想做一个"每日一题"功能,用 $sample 再合适不过了!