$sample是mongodb官方推荐的高效随机采样操作符,底层根据集合大小自动选用全量加载或蓄水池抽样算法,避免skip+random导致的全表扫描与性能瓶颈,且支持聚合管道中按需位置灵活使用。

$sample 是 MongoDB 对大数据集做随机采样最可靠、最高效的方式,但它的行为会随数据规模和管道位置动态变化——不是所有“随机抽 N 条”都等价。
为什么 $sample 在大数据集上比 skip + random 更稳
用 skip(Math.floor(Math.random() * count)) 抽一条看似简单,但实际会触发全表扫描+跳过大量文档,countDocuments() 本身在分片集群或无索引集合上就可能超时或不准。而 $sample 在底层自动适配:集合小(
- 千万级集合抽 100 条,
$sample通常在毫秒级完成;skip方案可能卡在count或跳过阶段 - 分片集群下,
$sample由每个 shard 独立执行再合并,天然支持水平扩展;skip必须先聚合全局 count,极易成为瓶颈 - 没有索引的集合,
skip性能断崖式下跌;$sample不依赖索引,表现稳定
$sample 的 size 参数不能瞎设
size 值直接影响执行策略和内存占用。MongoDB 要求它必须是正整数,且隐含限制:
本文档主要讲述的是用Apache Spark进行大数据处理——第一部分:入门介绍;Apache Spark是一个围绕速度、易用性和复杂分析构建的大数据处理框架。最初在2009年由加州大学伯克利分校的AMPLab开发,并于2010年成为Apache的开源项目之一。 在这个Apache Spark文章系列的第一部分中,我们将了解到什么是Spark,它与典型的MapReduce解决方案的比较以及它如何为大数据处理提供了一套完整的工具。希望本文档会给有需要的朋友带来帮助;感
- 若
size> 集合文档总数的 5%,或集合文档数 $sample 会退化为“全读 + 随机排序”,受sort内存限制(默认 100MB),可能报错Sort exceeded memory limit - 抽样量过大(如
size: 10000)时,即使总量够大,蓄水池算法也会显著增加 CPU 和内存开销 - 真实业务中,>1000 条的随机样本已属高需求,应优先考虑是否真需要“随机”而非“均匀分布”——此时可配合
$mod或哈希字段分桶
放在聚合管道什么位置才真正随机
$sample 的随机性依赖输入文档流。如果它前面有 $match、$lookup 等阶段,那抽的是过滤/关联后的结果集,不是原始集合的随机样本。
- 要从全量
orders中随机抽 50 单:直接用db.orders.aggregate([{$sample: {size: 50}}]) - 要从“近 30 天已支付”订单中随机抽 50 单:必须写成
[{$match: {status: "paid", createdAt: {$gt: ISODate("...")}}}, {$sample: {size: 50}}],顺序不能颠倒 - 如果
$sample放在$group后面,抽的是分组结果(比如每组一条),不是原始文档——这不是 bug,是设计使然
别把 $sampleRate 和 $sample 混着用
$sampleRate 是 Azure Cosmos DB for MongoDB(即 DocumentDB 兼容模式)的扩展操作符,原生 MongoDB 服务器不支持。你在本地 mongod 或 Atlas 上用 {$match: {$sampleRate: 0.1}} 会直接报错 unknown operator: $sampleRate。
- 原生 MongoDB 只认
$sample(固定数量)和$rand(生成随机数,需配合$addFields+$sort,性能差) - 想实现“抽约 10%”语义?只能先
count再算出目标size,或改用应用层控制调用频率 - Cosmos DB 用户注意:
$sampleRate不保证精确比例,只保证概率均等,且无法与$sample混用在同一管道
真正容易被忽略的是:$sample 的“随机”不提供可重现性(no seed 支持),也没有去重保障。如果你需要同一份随机样本反复测试,得把抽样结果缓存起来;如果业务要求“不重复抽中同一用户”,就得在外层加 $lookup 排除历史 ID 或用应用层布隆过滤器兜底。










