不确定能不能删一个索引?隐藏它!这篇验证隐藏索引的行为:查询计划、访问计数、写入维护、唯一与 TTL 约束、hint 以及限制,说明它为什么是“删除索引前的安全开关”。
test> db.t.find({a: 1234}) // a_1 可见
{
plan: 'FETCH > IXSCAN(a_1)',
nReturned: 40,
keysExamined: 40,
docsExamined: 40,
ms: 4
}
test> db.t.hideIndex("a_1")
{ hidden_old: false, hidden_new: true, ok: 1 }
test> db.t.find({a: 1234}) // a_1 已隐藏
{
plan: 'COLLSCAN',
nReturned: 40,
keysExamined: 0,
docsExamined: 200000,
ms: 37
}
test> db.t.unhideIndex("a_1")
{ hidden_old: true, hidden_new: false, ok: 1 }
test> db.t.find({a: 1234}) // 取消隐藏
{
plan: 'FETCH > IXSCAN(a_1)',
nReturned: 40,
keysExamined: 40,
docsExamined: 40,
ms: 0
}
耗时(9 次中位数):可见 0.2 ms;隐藏 28 ms;取消隐藏 0.3 ms隐藏后优化器不再考虑这个索引,同一条查询从 IXSCAN 变成 COLLSCAN(0.2 ms 变 28 ms);取消隐藏立刻恢复,不需要重建。
test> db.t.hideIndex("a_1"); db.t.getIndexes().map(i => i.name + (i.hidden ? " (hidden)" : ""))
[ '_id_', 'a_1 (hidden)', 'u_1', 'b_1' ]
test> $indexStats(隐藏中)
[ { name: 'a_1', ops: Long('0') } ]
test> 又查了两次 a 之后的 $indexStats(隐藏中,ops 应该没变)
[ { name: 'a_1', ops: Long('0') } ]
test> 取消隐藏后查一次 a
[ { name: 'a_1', ops: Long('1') } ]getIndexes 里有 hidden: true;$indexStats 的访问计数在隐藏期间不增长,可以用来评估“如果删掉它会怎样”。
test> db.t.hideIndex("a_1"); db.t.insertMany([{_id: 9000001, a: 999999, b: 1, u: 9000001}, {_id: 9000002, a: 999999, b: 1, u: 9000002}]).insertedCount
validate 的索引条目数(隐藏中): {'_id_': 200002, 'a_1': 200002, 'b_1': 200002, 'u_1': 200002}
test> 取消隐藏后 db.t.find({a: 999999})
2
test> explain
{
plan: 'FETCH > IXSCAN(a_1)',
nReturned: 2,
keysExamined: 2,
docsExamined: 2,
ms: 0
}隐藏的索引照常随写入更新:隐藏期间插入的文档,取消隐藏后马上可以用索引查到。
test> 隐藏后 hint("a_1")
{
code: 2,
message: ':: caused by :: hint provided does not correspond to an existing index'
}
test> 隐藏后 hint({a: 1})
{
code: 2,
message: ':: caused by :: hint provided does not correspond to an existing index'
}隐藏的索引对 hint 也相当于不存在,报 code 2。
test> 隐藏唯一索引 u_1 后插入重复的 u
E11000 duplicate key error collection: test.t index: u_1 dup key: { u: 5 }
test> db.ttl.hideIndex("c_ttl")
{ hidden_old: false, hidden_new: true, ok: 1 }
隐藏的 TTL 索引 c_ttl,插入 10 个已过期的文档,4 秒后剩余: 0 个隐藏的唯一索引仍然拒绝重复,隐藏的 TTL 索引仍然过期文档;隐藏只影响优化器。
test> try { db.t.hideIndex("_id_") } catch (e) { e.message }
can't hide _id index
test> hideIndex("nope")
cannot find index nope for ns test.t
test> [隐藏前缓存条目数, hideIndex 之后的缓存条目数]
[ 1, 0 ]不能隐藏 _id 索引;隐藏与取消隐藏都会清空该集合的计划缓存。