昨天的文章介绍了各种索引以及建立方式,这篇会讲一些使用上的一些经验。
在建立索引时还有一些属性能设定,这些非常关键请务必先了解。
透过设定 index unique,使得该栏位变成唯一值(亦可用在多栏位)。使用方法是在後方加上{unique:true}
即可,例如:
db.employee.createIndex({"department": 1}, {unique:true})
复合索引使用方式也是一样。
Unique index 可以建立在不存在的栏位上,当一笔资料写入时,会给予它 null
当作值; 下一笔进来後,若也没有这个栏位,则会发生 key duplicated。
Partial 属性是设定该栏位符合条件才会建立索引,这样一来充分的减少 索引创建的成本与空间。
例如要针对 age > 30 以上的资料建立索引,语法如下:
db.employee.createIndex({"department": 1}, {partialFilterExpression:{ age: { $gt:30 }}})
在以下的查询中,会走 parptial index 的有哪些呢?
1. db.employee.find({"department": 1, "age": {$gt: 30}}})
2. db.employee.find({"department": 1, "age": {$lte: 30}}})
3. db.employee.find({"department": 1, "age": 40})
4. db.employee.find({"department": 1})
稀疏索引是针对存在的栏位做索引,上面那个是符合条件才建立,而这个是存在才建立,一样省去建立与维护成本。这听起来很像是 partial index 的一种,是的,没错。partial index 应用范围更大,包含了 sparse 特性。
使用语法如下:
db.employee.createIndex( { "assets": 1 }, { sparse: true } )
TTL index 顾名思义是针对文件建立 TTL,对於资料生命周期非常好用,基本上绝大部分 collection 需要建立,除非是不太会增加的 global configuration 就不用。
db.employee.createIndex( { "expiredTime": 1 }, { expireAfterSeconds: 32 * 86400, name: "ttlIndex" })
这应该是 4.4 版本蛮亮眼的功能,当同事们对於索引相持不同意见时,可以请 DBA 执行这个指令。此功能是隐藏某个索引,输入後在执行计画里面都无法看到,但如果有资料更新,还是会帮新的文件建立索引喔,不用太担心。
隐藏後可以观察效能的各种改变,若确实没有什麽 side effect,就可以真的删除掉此索引了。大幅减少建立、删除索引的时间、效能耗损。
// hide
db.employee.hideIndex( {"index_name"})
// unhide
db.employee.unhideIndex( {"index_name"})
建立索引栏位的顺序非常重要!!
假设我们建立了一把索引
{ "field_a": 1, "field_b": 1, "field_c": 1, "field_d":1}
查询条件如果是以下,都是ok的
但如果是以下
就很不恰当。尽管在 (execution) explain 可能看到走在 index 上,但效率也是极差,甚至你使用 hint 强迫走在索引上也没用。依照我的使用经验,建议查询条件要符合前两个栏位。
我们很多时候会需要使用到排序功能,若要提升效能也必须把排序栏位加在索引内,这样才能有效提升查询效能。
例如索引 {field_a:1, field_b:1, field_c:1}
db.collection.find( {field_1: 5} ).sort({ field_b:1 }) // ok
db.collection.find( {field_1: 5, field_b:1} ).sort({ field_c:1 }) // ok
db.collection.find( {field_1: 5, field_b:1} ).sort({ field_d:1 }) // bad
建立索引时,可以在後面加上背景执行的设定,比较不会让整个资料库卡住。
db.employee.createIndex({"department": 1}, {unique:true}, {background:true})
这是理想上最佳状态,但实务上通常不太能满足。当建立的索引栏位包含查询结果栏位时,速度是最快的,因为不用实际去取得文件本身资料。
db.employee.find({ index_field: {$lte:5000 } }, { _id:0 , index_field:1 })
上述范例就是查询了有建立索引的栏位,且 project 这个栏位,这样 MongoDB 实际上就不用走到文件本身去取资料,而是在索引上即可完成查询。
这个中文我也翻不上来,意思就是在一个查询使用两个以上索引。
所以什麽是 intersection index?
假设我们有两把索引
{ field_a : 1}
{ field_b : 1}
但我的查询是...
db.collection.find({ field_a: {$gte:3} , field_b: 100)
/// or
db.collection.find({ field_a: {$gte:3}).sort( {field_b: -1} )
这种情境下就是 intersection index,通常可以藉由调整索引或是文件结构来避免。
首先要有个观念,什麽样资料适合用在 TTL?
当然不会是交易类型的重要资料,要知道,即便是使用排程自动删除或者TTL方式,都需要执行时间且要考虑到失败的可能性,因此重要且有时效性的资料不会这样做(心脏够大例外)。
除此之外,通常整点会有一些固定的排程在执行,考量到 TTL 相当於删除资料的概念,这个不是最紧急或重要的事会抢占去系统资源,同时 oplog (MongoDB抄写机制)会塞入大量资料,更不是一件好事,因此这点非常重要。
如果你能非常肯定资料量很小,且未来不会有大幅度的成长,那很多设计或操作倒是没什麽影响。
加上 {dropDups:true}
即可!
db.employee.createIndex({name:-1},{unique:true,dropDups:true})
可以,透过 sparse。语法如下:
db.employee.createIndex({"department": 1}, {sparse: true, unique:true})
索引一直以来都是资料库非常重要的一环,了解与善用非常重要,尤其尽量避免带有RDBMS的观念,虽然有些是相通的,但看到现在也能了解很多特点是 MongoDB 才有的,很多眉角在设计上无法光靠教科书或者教学文章就能透彻理解,毕竟每个情境都不同,最重要的还是实际去执行测试以及评估。那麽要如何评估索引的好坏呢~接着就是明天的事了 explain
!
本系列文章会同步发表於我个人的部落格 Pie Note
<<: 应用 LINE Front-end Framework 轻松建立互动 (2)
>>: JavaScript入门 Day09_ 有关数字的语法1
昨天介绍完SMO算法第四步,今天就要来写这个方法在迭代中的限制, 基本上每次在计算完Ei之後就要看E...
PUT / PATCH:修改资料 一样的模式再来一次,根据以上的Reqres API 来示范 首先一...
敏捷思维 敏捷(agile)是一种思维(mindset),由价值观(values),原则(prin...
前言 对图形的搜寻有了基本观念之後,接下来要在图形的边加入权重的部分,这样就能找到最短路径 生活常识...
超强的 AI 作曲 在 2020 年的时候 NVIDIA 在 YT 上发表了这支影片,里头使用到的音...