hint 什么时候会让查询更慢?这篇给同一条查询逐个强制每个索引,量耗时和检查量,再看 hint 的写法、报错和对计划缓存的影响。
hint (优化器自己选) 0.2 ms
hint status_1 29 ms
hint ts_1 4.0 ms
hint user_1 0.2 ms
hint status_1_ts_1 1.2 ms
hint $natural: 1(全表) 46 ms
test> 不 hint 时优化器的选择
{
plan: 'FETCH > IXSCAN(user_1)',
nReturned: 0,
keysExamined: 30,
docsExamined: 30,
ms: 0
}三个条件各有索引时,优化器自己选了最快的 user_1(30 个键、0.2 ms);强制其它索引都更慢:status_1 要检查 75,000 个键。
test> hint("status_1")
{
plan: 'FETCH > IXSCAN(status_1)',
nReturned: 0,
keysExamined: 75000,
docsExamined: 75000,
ms: 32
}
test> hint("ts_1")
{
plan: 'FETCH > IXSCAN(ts_1)',
nReturned: 0,
keysExamined: 10000,
docsExamined: 10000,
ms: 4
}
test> hint("user_1")
{
plan: 'FETCH > IXSCAN(user_1)',
nReturned: 0,
keysExamined: 30,
docsExamined: 30,
ms: 0
}
test> hint("status_1_ts_1")
{
plan: 'FETCH > IXSCAN(status_1_ts_1)',
nReturned: 0,
keysExamined: 2500,
docsExamined: 2500,
ms: 1
}耗时几乎正比于检查的键数:30、2500、10,000、75,000 个键分别对应 0、1、4、32 ms。
test> hint({status: 1, ts: 1}) // 键模式
{
plan: 'FETCH > IXSCAN(status_1_ts_1)',
nReturned: 0,
keysExamined: 2500,
docsExamined: 2500,
ms: 1
}
test> hint("nope_1") // 不存在的索引
{
code: 2,
message: 'error enumerating plans for query: ns=test.tTree: $and\n' +
' status $eq "A"\n' +
' user $eq 1234\n' +
' ts $gte 290000\n' +
'Sort: {}\n' +
'Proj: {}\n' +
' planner returned error :: caused by :: hint provided does not correspond to an existing index'
}
test> hint({status: -1}) // 存在 status_1,但键模式方向不同
{
code: 2,
message: 'error enumerating plans for query: ns=test.tTree: $and\n' +
' status $eq "A"\n' +
' user $eq 1234\n' +
' ts $gte 290000\n' +
'Sort: {}\n' +
'Proj: {}\n' +
' planner returned error :: caused by :: hint provided does not correspond to an existing index'
}
test> hint({$natural: -1}) // 反向全表
{
plan: 'COLLSCAN',
nReturned: 0,
keysExamined: 0,
docsExamined: 300000,
ms: 52
}可以用索引名或键模式;不存在的索引、方向不同的键模式报 code 2;{$natural: 1} / -1 强制正向或反向全表扫描。
test> 连续 3 次带 hint("ts_1") 的查询之后缓存条目数
0
test> 连续 3 次不带 hint 的同形状查询之后缓存条目数
1带 hint 的查询不建计划缓存条目,每次都按 hint 执行。
find({user: 777}):不 hint 0.2 ms;hint status_1 114 ms(563 倍);hint $natural 43 ms(213 倍)
test> db.t.find({user: 777}).hint("status_1")
{
plan: 'FETCH > IXSCAN(status_1)',
nReturned: 30,
keysExamined: 300000,
docsExamined: 300000,
ms: 125
}本来 0.2 ms 的按 user 查询被迫用 status_1,要检查 300,000 个键,慢 563 倍。
test> aggregate([{$match: {user: 777}}], {hint: "status_1"})
{
plan: 'FETCH > IXSCAN(status_1)',
nReturned: 30,
keysExamined: 300000,
docsExamined: 300000,
ms: 125
}
test> db.t.countDocuments({user: 777}, {hint: "user_1"})
30
test> update 带 hint: "status_1" 的 explain
{ stage: 'UPDATE', docsExamined: 300000, keysExamined: 300000 }aggregate、countDocuments、update / delete 都支持 hint。