聚合查询兜底:MongoDB "断路器"模式 --传统数据库聚合超时只能等死
❝开头还是介绍一下群,如果感兴趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis, OceanBase, Sql Server等有问题,有需求都可以加群加群请联系 liuaustin3 ,(共3400人左右 1 + 2 + 3 + 4 +5 + 6 + 7 + 8 )(1 2 3 4 5 6 7 8群已经爆满 9群 为纯聊天群,默认不加入不得发广告,自己公众号文章链接等,发一次直接踢,默认加入8群,开10群PolarDB专业学习群115+)
最近是掉进钱眼里面了,DTCC 大会我的主题就是给单位省钱, 2026年我的数据库工作主题就是,我是能给单位省钱的人。欢迎各大单位咱们私下咨询数据库省钱的妙招。
2026年大家都很不容易,作为生巧妙招之一的MongoDB 咱们还是的说说,这里有人会说,MongoDB 是好,就是聚合做不了,应用面太窄。咱们DTCC 会分享一个小的案例,说说怎么把聚合也往MongoDB里面做。
今天咱们大会先说说MongoDB怎么就能做聚合,做什么样的聚合的兜底方案。什么叫兜底方案兜底方案就是,你传统数据库做不了的兜底我都能给你做了。
兜底1 ,你的聚合查询在传统数据库就这条,聚合超时了你怎么办,凉拌,MongoDB是有方案的,就针对这个查询,我就给你自动Kill了,无需任何外界工具或脚本,传统数据库没戏。
其中的原理是通过MongoDB 查询中的获取到了queryhash值,并将这个特别糟糕的语句设置拒绝后续请求的方式,对影响数据库的不良语句进行一个自动kill。
我们演示一下
1 我们创建一个collections
然后我们向数据表中插入10万条数据
// 插入 10 万条测试数据
const bulk = [];
const statuses = ["pending", "shipped", "completed", "cancelled"];
for (let i = 1; i <= 100000; i++) {
bulk.push({
orderId: i,
customerId: Math.floor(Math.random() * 5000), // 5000 个客户
status: statuses[Math.floor(Math.random() * statuses.length)],
createdAt: new Date(Date.now() - Math.floor(Math.random() * 1000000000)),
totalAmount: Math.floor(Math.random() * 5000) + 100 // 金额 100-5100
});
// 每 1000 条批量写入一次
if (bulk.length === 1000) {
db.orders.insertMany(bulk);
bulk.length = 0;
}
}
// 如果最后还有剩余数据
if (bulk.length > 0) {
db.orders.insertMany(bulk);
}
`
然后我们可以设置慢查询,也可以直接运行查询的语句获取queryShapeHash
db.setProfilingLevel(2, { slowms: 100 });
我们稍微做一个聚合的语句查询,然后我们看是否可以获得一个慢查询
test> // 查询最近的慢查询
| db.system.profile.find().sort({ ts: -1 }).limit(5).pretty();
|
[
{
op: 'command',
ns: 'test.orders',
isFromPriorityPortConnection: false,
command: {
aggregate: 'orders',
pipeline: [
{ '$group': { _id: '$customerId', total: { '$sum': '$totalAmount' } } },
{ '$sort': { total: -1 } },
{ '$limit': 10 }
],
cursor: {},
lsid: { id: UUID('55720095-d496-465b-b38b-7fea7947fc22') },
'$db': 'test'
},
opid: 1064046,
keysExamined: 0,
docsExamined: 100000,
peakTrackedMemBytes: Long('505000'),
hasSortStage: true,
cursorExhausted: true,
numYield: 9,
nreturned: 10,
planCacheShapeHash: '51975642',
queryHash: '51975642',
planCacheKey: '847DB05C',
queryShapeHash: '28A6104BE9582AC6A853D2BB1A387BF20D749BC50D932D0C3AE593E63E33B904',
queryFramework: 'sbe',
locks: { Global: { acquireCount: { r: Long('19') } } },
flowControl: {},
storage: { timeWaitingMicros: { storageEngineMicros: Long('628') } },
responseLength: 380,
protocol: 'op_msg',
cpuNanos: 123443645,
millis: 148,
planSummary: 'COLLSCAN',
planningTimeMicros: 1162,
ts: ISODate('2026-05-09T01:45:47.646Z'),
client: '127.0.0.1',
appName: 'mongosh 2.8.3',
allUsers: [ { user: 'root', db: 'admin' } ],
user: 'root@admin'
},
{
op: 'command',
ns: 'test.orders',
isFromPriorityPortConnection: false,
command: {
aggregate: 'orders',
pipeline: [ { '$match': {} }, { '$group': { _id: 1, n: { '$sum': 1 } } } ],
cursor: {},
lsid: { id: UUID('55720095-d496-465b-b38b-7fea7947fc22') },
'$db': 'test'
},
opid: 1064045,
keysExamined: 0,
docsExamined: 100000,
cursorExhausted: true,
numYield: 4,
nreturned: 1,
planCacheShapeHash: 'CDC0091B',
queryHash: 'CDC0091B',
planCacheKey: 'BB9BAF92',
queryShapeHash: '2F0E01F607113268E6780FAA939272B69C23C2480C51ED282DC9ACE610BFE091',
queryFramework: 'sbe',
locks: { Global: { acquireCount: { r: Long('5') } } },
flowControl: {},
responseLength: 124,
protocol: 'op_msg',
cpuNanos: 58749017,
millis: 69,
planSummary: 'COLLSCAN',
planningTimeMicros: 10687,
ts: ISODate('2026-05-09T01:45:29.950Z'),
client: '127.0.0.1',
appName: 'mongosh 2.8.3',
allUsers: [ { user: 'root', db: 'admin' } ],
user: 'root@admin'
}
]
或者我们可以通过执行计划获得你的这条语句的queryhash
rs0 [direct: primary] admin> db.adminCommand({
| setQuerySettings: "28A6104BE9582AC6A853D2BB1A387BF20D749BC50D932D0C3AE593E63E33B904",
| settings: { reject: true }
| })
{
queryShapeHash: '28A6104BE9582AC6A853D2BB1A387BF20D749BC50D932D0C3AE593E63E33B904',
settings: { reject: true },
ok: 1,
'$clusterTime': {
clusterTime: Timestamp({ t: 1778294814, i: 2 }),
signature: {
hash: Binary.createFromBase64('ca23RApt77H+5S5XbZyAMtcg15Q=', 0),
keyId: Long('7637717952812285961')
}
},
operationTime: Timestamp({ t: 1778294814, i: 2 })
}
rs0 [direct: primary] admin>
通过上面的设置,我们可以清晰的看到刚才的语句执行,已经彻底的失效,数据库不在对这个结构的查询进行响应。
MongoServerError[QueryRejectedBySettings]: Query rejected by admin query settings
系统直接给出你的查询被拒绝的提示。
这样的功能在传统数据库中是不存在的,只有云上的一些云原生数据库提供类似的功能。
第二个方式,我们对某些查询要禁止他超过执行时间的一部分,比如普通这个语句执行时间在100毫秒,但是他超过100毫秒我就禁止掉他,可以不可以,回答是可以。那么我们如何来做。我们只需要在原理的查询中,添加一个的超时的设置即可。
db.orders.aggregate([
{ $group: { _id: "$customerId", total: { $sum: "$totalAmount" } } },
{ $sort: { total: -1 } },
{ $limit: 10 }
], { maxTimeMS: 10 })
通过在语句中添加超时的设置,直接将安全的语句发送到MongoDB 超时的语句自动会被取消掉。
所以写到这里,我个人看MongoDB的一些功能是值得传统数据库学习的,很多概念是传统DBA 无法理解的,如没有表,就可以建索引,没有表就可以建立约束,一个表是可以限制大小的等等,在传统数据库中一些没有的概念。
在开发中不断总结经验 qodercli开发命令下载,DBA架构转开发第四天
PolarDB 使用4年的,我知道,你不知道的 Tips ! 有多少你知道?
我冤枉,我不知道,我就是一个DBA ,为什么 判我 3年!!
我是怎么浪费Token的 DBA架构 转 数据库运维软件开发 转行开发第三天
比起简单的Skill技能,我更想建立Agent Skill的系统思维能力--- 感谢本书作者答疑解惑
MongoDB 全文索引 与 展示查询数据的一部分,提高性能
体现价值-我们靠PostgreSQL迁移PolarDB,给公司省下了100万 “巨款”
《告别迁移焦虑:OceanBase MySQL 模式能否兼容 DBA 的“祖传”运维 SQL?》
干数据库不是买白菜:光盯着License几毛钱,看不见300台机器的电费?
一个秘密,不是你 SQL 写对了,是优化器帮“擦了屁股” 客户问迁移后为什么快了--迁移到PolarDB后的故事
AI 引入后,MySQL 列权限控制,插入,更新,读取,删除 --有了AI 真是越帮越忙
PostgreSQL 大表改字段卡死的问题解决了吗? 解决了方案在此
AI 引入DBA 工作,造成工作量增加,忙不过来,根本忙不过来!!!
三无项目导致MongoDB 持续1406% CPU 问题解决