泉州模板建站哪家好,网站数据丢失了做数据恢复需多久,什么是ui设计?,成都解放号网站建设概述
索引是一种用来快速查询数据的数据结构。BTree 就是一种常用的数据库索引数据结构#xff0c;MongoDB 采用 BTree 做索引#xff0c;索引创建 colletions 上。MongoDB 不使用索引的查询#xff0c;先扫描所有的文档#xff0c;再匹配符合条件的文档。使用索引的查询Tree 就是一种常用的数据库索引数据结构MongoDB 采用 BTree 做索引索引创建 colletions 上。MongoDB 不使用索引的查询先扫描所有的文档再匹配符合条件的文档。使用索引的查询通过索引找到文档使用索引能够极大的提升查询效率。MongoDB 用的数据结构是 B-Tree具体来说是 BTree因为 BTree 是 B-Tree 的子集。
索引数据结构
B-Tree 说法来源于官方文档然后就导致了分歧有人说 MongoDB 索引数据结构使用的是 B-Tree有的人又说是 BTree。 MongoDB 官方文档https://docs.mongodb.com/manual/indexes/ WiredTiger官方文档https://source.wiredtiger.com/3.0.0/tune_page_size_and_comp.html WiredTiger maintains a table’s data in memory using a data structure called a B-Tree ( B Tree to be specific), referring to the nodes of a B-Tree as pages. Internal pages carry only keys. The leaf pages store both keys and values. WiredTiger 数据文件在磁盘的存储结构BTree 中的 leaf page 包含一个页头page header、块头block header和真正的数据key/value其中页头定义了页的类型、页中实际载荷数据的大小、页中记录条数等信息块头定义了此页的 checksum、块在磁盘上的寻址位置等信息。WiredTiger 有一个块设备管理的模块用来为 page 分配 block。如果要定位某一行数据key/value的位置可以先通过 block 的位置找到此 page相对于文件起始位置的偏移量再通过 page 找到行数据的相对位置最后可以得到行数据相对于文件起始位置的偏移量 offsets。
索引操作
创建索引
创建索引语法格式
db.collection.createIndex(keys, options)Key 值为你要创建的索引字段1 按升序创建索引 -1 按降序创建索引。 Key 可选参数列表如下
ParameterTypeDescriptionbackgroundBoolean建索引过程会阻塞其它数据库操作background 可指定以后台方式创建索引即增加 “background” 可选参数。 “background” 默认值为 false。uniqueBoolean建立的索引是否唯一。指定为 true 创建唯一索引。默认值为 false。namestring索引的名称。如果未指定MongoDB 的通过连接索引的字段名和排序顺序生成一个索引名称。dropDupsBoolean3.0 版本已废弃。在建立唯一索引时是否删除重复记录指定 true 创建唯一索引。默认值为 false。sparseBoolean对文档中不存在的字段数据不启用索引。这个参数需要特别注意如果设置为 true 的话在索引字段中不会查询出不包含对应字段的文档。默认值为 false。expireAfterSecondsinteger指定一个以秒为单位的数值完成 TTL 设定设定集合的生存时间。vindex version索引的版本号。默认的索引版本取决于 mongodb 创建索引时运行的版本。weightsdocument索引权重值数值在 1 到 99,999 之间表示该索引相对于其他索引字段的得分权重。default_languagestring对于文本索引该参数决定了停用词及词干和词器的规则的列表。 默认为英语。language_overridestring对于文本索引该参数指定了包含在文档中的字段名语言覆盖默认的 language默认值为 language。
注意3.0.0 版本前创建索引方法为 db.collection.ensureIndex()。
# 创建索引后台执行
db.values.createIndex({open: 1, close: 1}, {background: true})
# 创建唯一索引
db.values.createIndex({title:1},{unique:true})查看索引
# 查看索引信息
db.books.getIndexes()
# 查看索引键
db.books.getIndexKeys()删除索引
# 删除集合指定索引
db.col.dropIndex(索引名称)
# 删除集合所有索引不能删除主键索引
db.col.dropIndexes()索引类型
与大多数数据库一样MongoDB 支持各种丰富的索引类型包括单键索引、复合索引唯一索引等一些常用的结构。由于采用了灵活可变的文档类型因此它也同样支持对嵌套字段、数组进行索引。通过建立合适的索引我们可以极大地提升数据的检索速度。在一些特殊应用场景MongoDB 还支持地理空间索引、文本检索索引、TTL 索引等不同的特性。
单键索引Single Field Indexes
在某一个特定的字段上建立索引。MongoDB 在 ID 上建立了唯一的单键索引所以经常会使用 id 来进行查询。在索引字段上进行精确匹配、排序以及范围查找都会使用此索引。
db.books.createIndex({title:1})
# 对内嵌文档字段创建索引
db.books.createIndex({author.name:1})复合索引Compound Index
复合索引是多个字段组合而成的索引其性质和单字段索引类似。但不同的是复合索引中字段的顺序、字段的升降序对查询性能有直接的影响因此在设计复合索引时则需要考虑不同的查询场景。
db.books.createIndex({type:1,favCount:1})
# 查看执行计划
db.books.find({type:novel,favCount:{$gt:50}}).explain()多键数组索引Multikey Index
在数组的属性上建立索引。针对这个数组的任意值的查询都会定位到这个文档既多个索引入口或者键值引用同一个文档。
准备 inventory 集合
db.inventory.insertMany([
{ _id: 5, type: food, item: aaa, ratings: [ 5, 8, 9 ] },
{ _id: 6, type: food, item: bbb, ratings: [ 5, 9 ] },
{ _id: 7, type: food, item: ccc, ratings: [ 9, 5, 8 ] },
{ _id: 8, type: food, item: ddd, ratings: [ 9, 5 ] },
{ _id: 9, type: food, item: eee, ratings: [ 5, 9, 5 ] }
])创建多键索引
db.inventory.createIndex( { ratings: 1 } )多键索引很容易与复合索引产生混淆复合索引是多个字段的组合而多键索引则仅仅是在一个字段上出现了多键multi key。而实质上多键索引也可以出现在复合字段上。
# 创建复合多值索引
db.inventory.createIndex( { item:1,ratings: 1 } )注意MongoDB 并不支持一个复合索引中同时出现多个数组字段。
嵌入文档的索引数组
db.inventory.insertMany([
{_id: 1,item: abc,stock: [{ size: S, color: red, quantity: 25 },{ size: S, color: blue, quantity: 10 },{ size: M, color: blue, quantity: 50 }]
},
{_id: 2,item: def,stock: [{ size: S, color: blue, quantity: 20 },{ size: M, color: blue, quantity: 5 },{ size: M, color: black, quantity: 10 },{ size: L, color: red, quantity: 2 }]
},
{_id: 3,item: ijk,stock: [{ size: M, color: blue, quantity: 15 },{ size: L, color: blue, quantity: 100 },{ size: L, color: red, quantity: 25 }]
}
])在包含嵌套对象的数组字段上创建多键索引
db.inventory.createIndex( { stock.size: 1, stock.quantity: 1 } )db.inventory.find({stock.size:S,stock.quantity:{$gt:20}}).explain()Hash 索引Hashed Indexes
不同于传统的 B-Tree 索引哈希索引使用 hash 函数来创建索引。在索引字段上进行精确匹配但不支持范围查询不支持多键 hash。Hash 索引上的入口是均匀分布的在分片集合中非常有用。
db.users.createIndex({username : hashed})地理空间索引Geospatial Index
在移动互联网时代基于地理位置的检索LBS功能几乎是所有应用系统的标配。MongoDB 为地理空间检索提供了非常方便的功能。地理空间索引2dsphereindex就是专门用于实现位置检索的一种特殊索引。
案例MongoDB 如何实现“查询附近商家假设商家的数据模型如下
db.restaurant.insert({restaurantId: 0,restaurantName:兰州牛肉面,location : {type: Point,coordinates: [ -73.97, 40.77 ]}
})创建一个 2dsphere 索引
db.restaurant.createIndex({location : 2dsphere})查询附近 10000 米商家信息
db.restaurant.find( { location:{ $near :{$geometry :{ type : Point ,coordinates : [ -73.88, 40.78 ] } ,$maxDistance:10000 } }
} )$near 查询操作符用于实现附近商家的检索返回数据结果会按距离排序。 $geometry 操作符用于指定一个 GeoJSON 格式的地理空间对象typePoint 表示地理坐标点coordinates 则是用户当前所在的经纬度位置。 $maxDistance 限定了最大距离单位是米。 全文索引Text Indexes
MongoDB 支持全文检索功能可通过建立文本索引来实现简易的分词检索。
db.reviews.createIndex( { comments: text } )t e x t 操作符可以在有 t e x t i n d e x 的集合上执行文本检索。 text 操作符可以在有 text index 的集合上执行文本检索。 text操作符可以在有textindex的集合上执行文本检索。text 将会使用空格和标点符号作为分隔符对检索字符串进行分词并且对检索字符串中所有的分词结果进行一个逻辑上的 OR 操作。全文索引能解决快速文本查找的需求比如有一个博客文章集合需要根据博客的内容来快速查找则可以针对博客内容建立文本索引。
案例
数据准备
db.stores.insert([{ _id: 1, name: Java Hut, description: Coffee and cakes },{ _id: 2, name: Burger Buns, description: Gourmet hamburgers },{ _id: 3, name: Coffee Shop, description: Just coffee },{ _id: 4, name: Clothes Clothes Clothes, description: Discount clothing },{ _id: 5, name: Java Shopping, description: Indonesian goods }]
)创建 name 和 description 的全文索引
db.stores.createIndex({name: text, description: text})测试通过 $text 操作符来查寻数据中所有包含“coffee”、“shop”、“java”列表中任何词语的商店
db.stores.find({$text: {$search: java coffee shop}})MongoDB 的文本索引功能存在诸多限制而官方并未提供中文分词的功能这使得该功能的应用场景十分受限。
通配符索引Wildcard Indexes
MongoDB 的文档模式是动态变化的而通配符索引可以建立在一些不可预知的字段上以此实现查询的加速。MongoDB 4.2 引入了通配符索引来支持对未知或任意字段的查询。
案例
准备商品数据不同商品属性不一样
db.products.insert([{product_name : Spy Coat,product_attributes : {material : [ Tweed, Wool, Leather ],size : {length : 72,units : inches}}},{product_name : Spy Pen,product_attributes : {colors : [ Blue, Black ],secret_feature : {name : laser,power : 1000,units : watts,}}},{product_name : Spy Book}
])创建通配符索引
db.products.createIndex( { product_attributes.$** : 1 } )测试通配符索引可以支持任意单字段查询 product_attributes 或其嵌入字段。
db.products.find( { product_attributes.size.length : { $gt : 60 } } )
db.products.find( { product_attributes.material : Leather } )
db.products.find( { product_attributes.secret_feature.name : laser } )注意事项
通配符索引不兼容的索引类型或属性 通配符索引是稀疏的不索引空字段。因此通配符索引不能支持查询字段不存在的文档。
# 通配符索引不能支持以下查询
db.products.find( {product_attributes : { $exists : false } } )
db.products.aggregate([{ $match : { product_attributes : { $exists : false } } }
])通配符索引为文档或数组的内容生成条目而不是文档/数组本身。因此通配符索引不能支持精确的文档/数组相等匹配。通配符索引可以支持查询字段等于空文档{}的情况。
# 通配符索引不能支持以下查询:
db.products.find({ product_attributes.colors : [ Blue, Black ] } )db.products.aggregate([{$match : { product_attributes.colors : [ Blue, Black ] }
}])索引属性
唯一索引Unique Indexes
在现实场景中唯一性是很常见的一种索引约束需求重复的数据记录会带来许多处理上的麻烦比如订单的编号、用户的登录名等。通过建立唯一性索引可以保证集合中文档的指定字段拥有唯一值。
# 创建唯一索引
db.values.createIndex({title:1},{unique:true})
# 复合索引支持唯一性约束
db.values.createIndex({title:1type:1},{unique:true})
# 多键索引支持唯一性约束
db.inventory.createIndex( { ratings: 1 },{unique:true} )唯一性索引对于文档中缺失的字段会使用 null 值代替因此不允许存在多个文档缺失索引字段的情况。对于分片的集合唯一性约束必须匹配分片规则。换句话说为了保证全局的唯一性分片键必须作为唯一性索引的前缀字段。
部分索引Partial Indexes
部分索引仅对满足指定过滤器表达式的文档进行索引。通过在一个集合中为文档的一个子集建立索引部分索引具有更低的存储需求和更低的索引创建和维护的性能成本。3.2 新版功能。部分索引提供了稀疏索引功能的超集应该优先于稀疏索引。
db.restaurants.createIndex({ cuisine: 1, name: 1 },{ partialFilterExpression: { rating: { $gt: 5 } } }
)partialFilterExpression 选项接受指定过滤条件的文档 等式表达式例如field: value 或使用 $eq 操作符 $exists: true $gt $gte $lt $lte $type 顶层的 $and # 符合条件使用索引
db.restaurants.find( { cuisine: Italian, rating: { $gte: 8 } } )
# 不符合条件不能使用索引
db.restaurants.find( { cuisine: Italian } )唯一约束结合部分索引使用导致唯一约束失效的问题如果同时指定了 partialFilterExpression 和唯一约束那么唯一约束只适用于满足筛选器表达式的文档。如果文档不满足筛选条件那么带有惟一约束的部分索引不会阻止插入不满足惟一约束的文档。
稀疏索引Sparse Indexes
索引的稀疏属性确保索引只包含具有索引字段的文档的条目索引将跳过没有索引字段的文档。特性只对存在字段的文档进行索引包括字段值为 null 的文档。
# 不索引不包含xmpp_id字段的文档
db.addresses.createIndex( { xmpp_id: 1 }, { sparse: true } )如果稀疏索引会导致查询和排序操作的结果集不完整MongoDB 将不会使用该索引除非 hint() 明确指定索引。
案例
数据准备
db.scores.insertMany([{userid : newbie},{userid : abby, score : 82},{userid : nina, score : 90}
])创建稀疏索引
db.scores.createIndex( { score: 1 } , { sparse: true } )测试
# 使用稀疏索引
db.scores.find( { score: { $lt: 90 } } )# 即使排序是通过索引字段MongoDB也不会选择稀疏索引来完成查询以返回完整的结果
db.scores.find().sort( { score: -1 } )# 要使用稀疏索引使用hint()显式指定索引
db.scores.find().sort( { score: -1 } ).hint( { score: 1 } )同时具有稀疏性和唯一性的索引可以防止集合中存在字段值重复的文档但允许不包含此索引字段的文档插入。
TTL索引TTL Indexes
在一般的应用系统中并非所有的数据都需要永久存储。例如一些系统事件、用户消息等这些数据随着时间的推移其重要程度逐渐降低。更重要的是存储这些大量的历史数据需要花费较高的成本因此项目中通常会对过期且不再使用的数据进行老化处理。
通常的做法如下方案一为每个数据记录一个时间戳应用侧开启一个定时器按时间戳定期删除过期的数据。方案二数据按日期进行分表同一天的数据归档到同一张表同样使用定时器删除过期的表。
对于数据老化MongoDB 提供了一种更加便捷的做法TTLTime To Live索引。TTL 索引需要声明在一个日期类型的字段中TTL 索引是特殊的单字段索引MongoDB 可以使用它在一定时间或特定时钟时间后自动从集合中删除文档。
# 创建 TTL 索引TTL 值为3600秒
db.eventlog.createIndex( { lastModifiedDate: 1 }, { expireAfterSeconds: 3600 } )对集合创建 TTL 索引之后MongoDB 会在周期性运行的后台线程中对该集合进行检查及数据清理工作。除了数据老化功能TTL 索引具有普通索引的功能同样可以用于加速数据的查询。
TTL 索引不保证过期数据会在过期后立即被删除。文档过期和 MongoDB 从数据库中删除文档的时间之间可能存在延迟。删除过期文档的后台任务每 60 秒运行一次。因此在文档到期和后台任务运行之间的时间段内文档可能会保留在集合中。
案例
数据准备
db.log_events.insertOne( {createdAt: new Date(),logEvent: 2,logMessage: Success!
} )创建 TTL 索引
db.log_events.createIndex( { createdAt: 1 }, { expireAfterSeconds: 20 } )可变的过期时间
TTL 索引在创建之后仍然可以对过期时间进行修改。这需要使用 collMod 命令对索引的定义进行变更
db.runCommand({collMod:log_events,index:{keyPattern:{createdAt:1},expireAfterSeconds:600}})使用约束
TTL 索引的确可以减少开发的工作量而且通过数据库自动清理的方式会更加高效、可靠但是在使用 TTL 索引时需要注意以下的限制
TTL 索引只能支持单个字段并且必须是非 _id 字段。TTL 索引不能用于固定集合。TTL 索引无法保证及时的数据老化MongoDB 会通过后台的 TTLMonitor 定时器来清理老化数据默认的间隔时间是 1 分钟。当然如果在数据库负载过高的情况下TTL 的行为则会进一步受到影响。TTL 索引对于数据的清理仅仅使用了 remove 命令这种方式并不是很高效。因此 TTL Monitor 在运行期间对系统 CPU、磁盘都会造成一定的压力。相比之下按日期分表的方式操作会更加高效。
隐藏索引Hidden Indexes
隐藏索引对查询规划器不可见不能用于支持查询。通过对规划器隐藏索引用户可以在不实际删除索引的情况下评估删除索引的潜在影响。如果影响是负面的用户可以取消隐藏索引而不必重新创建已删除的索引。4.4 新版功能。
# 创建隐藏索引
db.restaurants.createIndex({ borough: 1 },{ hidden: true });
# 隐藏现有索引
db.restaurants.hideIndex( { borough: 1} );
db.restaurants.hideIndex( 索引名称 )
# 取消隐藏索引
db.restaurants.unhideIndex( { borough: 1} );
db.restaurants.unhideIndex( 索引名称 ); 案例
数据准备
db.scores.insertMany([{userid : newbie},{userid : abby, score : 82},{userid : nina, score : 90}
])创建隐藏索引
db.scores.createIndex({ userid: 1 },{ hidden: true }
)查看索引信息
db.scores.getIndexes()索引属性 hidden 只在值为 true 时返回。测试
# 不使用索引
db.scores.find({userid:abby}).explain()
# 取消隐藏索引
db.scores.unhideIndex( { userid: 1} )
# 使用索引
db.scores.find({userid:abby}).explain()索引使用建议
1为每一个查询建立合适的索引这个是针对于数据量较大比如说超过几十上百万文档数目数量级的集合。如果没有索引 MongoDB 需要把所有的 Document 从盘上读到内存这会对 MongoDB 服务器造成较大的压力并影响到其他请求的执行。
2创建合适的复合索引不要依赖于交叉索引如果你的查询会使用到多个字段MongoDB 有两个索引技术可以使用交叉索引和复合索引。交叉索引就是针对每个字段单独建立一个单字段索引然后在查询执行时候使用相应的单字段索引进行索引交叉而得到查询结果。交叉索引目前触发率较低所以如果你有一个多字段查询的时候建议使用复合索引能够保证索引正常的使用。
# 查找所有年龄小于30岁的深圳市马拉松运动员
db.athelets.find({sport: marathon, location: sz, age: {$lt: 30}}})
# 创建复合索引
db.athelets.createIndex({sport:1, location:1, age:1})3复合索引字段顺序匹配条件在前范围条件在后Equality First, Range After前面的例子在创建复合索引时如果条件有匹配和范围之分那么匹配条件sport: “marathon”) 应该在复合索引的前面。范围条件(age: 30)字段应该放在复合索引的后面。
4尽可能使用覆盖索引Covered Index建议只返回需要的字段同时利用覆盖索引来提升性能。
5建索引要在后台运行在对一个集合创建索引时该集合所在的数据库将不接受其他读写操作。对大数据量的集合建索引建议使用后台运行选项 {background: true}。
6避免设计过长的数组索引数组索引是多值的在存储时需要使用更多的空间。如果索引的数组长度特别长或者数组的增长不受控制则可能导致索引空间急剧膨胀。
explain 执行计划
通常我们需要关心的问题
查询是否使用了索引索引是否减少了扫描的记录数量是否存在低效的内存排序
MongoDB 提供了 explain 命令它可以帮助我们评估指定查询模型querymodel的执行计划根据实际情况进行调整然后提高查询效率。explain() 方法的形式如下:
db.collection.find().explain(verbose)verbose可选参数表示执行计划的输出模式默认 queryPlanner verbose 枚举如下
**模式名字 **描述queryPlanner执行计划的详细信息包括查询计划、集合信息、查询条件、最佳执行计划、查询方式和 MongoDB 服务信息等。exectionStats最佳执行计划的执行情况和被拒绝的计划等信息。allPlansExecution选择并执行最佳执行计划并返回最佳执行计划和其他执行计划的执行情况。
queryPlanner
# 未创建title的索引
db.books.find({title:book-1}).explain(queryPlanner)结果字段说明
**字段名称 **描述plannerVersion执行计划的版本namespace查询的集合indexFilterSet是否使用索引parsedQuery查询条件winningPlan最佳执行计划stage查询方式filter过滤条件direction查询顺序rejectedPlans拒绝的执行计划serverInfomongodb 服务器信息
executionStats
executionStats 模式的返回信息中包含了 queryPlanner 模式的所有字段并且还包含了最佳执行计划的执行情况
# 创建索引
db.books.createIndex({title:1})db.books.find({title:book-1}).explain(executionStats)结果字段说明
**字段名称 **描述winningPlan.inputStage用来描述子 stage并且为其父 stage 提供文档和索引关键字winningPlan.inputStage.stage子查询方式winningPlan.inputStage.keyPattern所扫描的 index 内容winningPlan.inputStage.indexName索引名winningPlan.inputStage.isMultiKey是否是 Multikey。如果索引建立在 array 上将是 trueexecutionStats.executionSuccess是否执行成功executionStats.nReturned返回的个数executionStats.executionTimeMillis这条语句执行时间executionStats.executionStages.executionTimeMillisEstimate检索文档获取数据的时间executionStats.executionStages.inputStage.executionTimeMillisEstimate扫描获取数据的时间executionStats.totalKeysExamined索引扫描次数executionStats.totalDocsExamined文档扫描次数executionStats.executionStages.isEOF是否到达 steam 结尾1 或者 true 代表已到达结尾executionStats.executionStages.works工作单元数一个查询会分解成小的工作单元executionStats.executionStages.advanced优先返回的结果数executionStats.executionStages.docsExamined文档检查数
allPlansExecution
allPlansExecution 返回的信息包含 executionStats 模式的内容且包含 allPlansExecution:[] 块
allPlansExecution : [{nReturned : int,executionTimeMillisEstimate : int,totalKeysExamined : int,totalDocsExamined :int,executionStages : {stage : STAGEA,nReturned : int,executionTimeMillisEstimate : int,...}}},...]stage 状态
**状态 **描述COLLSCAN全表扫描IXSCAN索引扫描FETCH根据索引检索指定文档SHARD_MERGE将各个分片返回数据进行合并SORT在内存中进行了排序LIMIT使用 limit 限制返回数SKIP使用 skip 进行跳过IDHACK对 _id 进行查询SHARDING_FILTER通过 mongos 对分片数据进行查询COUNTSCANcount 不使用 Index 进行 count 时的 stage 返回COUNT_SCANcount 使用了 Index 进行 count 时的 stage 返回SUBPLA未使用到索引的 $or 查询的 stage 返回TEXT使用全文索引进行查询时候的 stage 返回PROJECTION限定返回字段时候 stage 的返回
执行计划的返回结果中尽量不要出现以下 stage
COLLSCAN全表扫描SORT使用 sort 但是无 index不合理的 SKIPSUBPLA未用到 index 的 $orCOUNTSCAN不使用 index 进行 count