MongoDB索引详解

概述

索引是一种用来快速查询数据的数据结构。B+Tree 就是一种常用的数据库索引数据结构,MongoDB 采用 B+Tree 做索引,索引创建 colletions 上。MongoDB 不使用索引的查询,先扫描所有的文档,再匹配符合条件的文档。使用索引的查询,通过索引找到文档,使用索引能够极大的提升查询效率。
MongoDB 用的数据结构是 B-Tree,具体来说是 B+Tree,因为 B+Tree 是 B-Tree 的子集。

索引数据结构

B-Tree 说法来源于官方文档,然后就导致了分歧:有人说 MongoDB 索引数据结构使用的是 B-Tree,有的人又说是 B+Tree。

MongoDB 官方文档:https://docs.mongodb.com/manual/indexes/

image.png

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 数据文件在磁盘的存储结构:
image.png
B+Tree 中的 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 可选参数列表如下:

ParameterTypeDescription
backgroundBoolean建索引过程会阻塞其它数据库操作,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 格式的地理空间对象,type=Point 表示地理坐标点,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" } )

注意事项

  • 通配符索引不兼容的索引类型或属性

image.png

  • 通配符索引是稀疏的,不索引空字段。因此,通配符索引不能支持查询字段不存在的文档。
# 通配符索引不能支持以下查询
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:1,type: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 提供了一种更加便捷的做法:TTL(Time 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")

结果:
image.png
字段说明:

**字段名称 **描述
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")

结果:
image.png
字段说明:

**字段名称 **描述
winningPlan.inputStage用来描述子 stage,并且为其父 stage 提供文档和索引关键字
winningPlan.inputStage.stage子查询方式
winningPlan.inputStage.keyPattern所扫描的 index 内容
winningPlan.inputStage.indexName索引名
winningPlan.inputStage.isMultiKey是否是 Multikey。如果索引建立在 array 上,将是 true
executionStats.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)
  • 不合理的 SKIP
  • SUBPLA(未用到 index 的 $or)
  • COUNTSCAN(不使用 index 进行 count)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/235988.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【漏洞复现】天融信TOPSEC static_convert 远程命令执行

漏洞描述 天融信TOPSEC Static_Convert存在严重的远程命令执行漏洞。攻击者通过发送精心构造的恶意请求,利用了该漏洞,成功实现在目标系统上执行任意系统命令的攻击。成功利用漏洞的攻击者可在目标系统上执行恶意操作,可能导致数据泄露、系统瘫痪或远程控制。强烈建议立即更…

单片机中的PWM(脉宽调制)的工作原理以及它在电机控制中的应用。

目录 工作原理 在电机控制中的应用 脉宽调制&#xff08;PWM&#xff09;是一种在单片机中常用的控制技术&#xff0c;它通过调整信号的脉冲宽度来控制输出信号的平均电平。PWM常用于模拟输出一个可调电平的数字信号&#xff0c;用于控制电机速度、亮度、电压等。 工作原理 …

3D模型UV展开原理

今年早些时候&#xff0c;我为 MAKE 杂志写了一篇教程&#xff0c;介绍如何制作视频游戏角色的毛绒动物。 该技术采用给定的角色 3D 模型及其纹理&#xff0c;并以编程方式生成缝纫图案。 虽然我已经编写了一般摘要并将源代码上传到 GitHub&#xff0c;但我在这里编写了对使这一…

力扣日记1.11-【二叉树篇】450. 删除二叉搜索树中的节点

力扣日记&#xff1a;【二叉树篇】450. 删除二叉搜索树中的节点 日期&#xff1a;2024.1.11 参考&#xff1a;代码随想录、力扣 450. 删除二叉搜索树中的节点 题目描述 难度&#xff1a;中等 给定一个二叉搜索树的根节点 root 和一个值 key&#xff0c;删除二叉搜索树中的 key…

多测师肖sir___ui自动化测试po框架讲解版

po框架 一、ui自动化po框架介绍 &#xff08;1&#xff09;PO是Page Object的缩写 &#xff08;2&#xff09;业务流程与页面元素操作分离的模式&#xff0c;可以简单理解为每个页面下面都有一个配置class&#xff0c; 配置class就用来维护页面元素或操作方法 &#xff08;3&am…

XTuner 大模型单卡低成本微调实战

XTuner 大模型单卡低成本微调实战 Finetune简介增量预训练微调指令跟随微调LoRA XTuner介绍功能亮点 8GB显存玩转LLMFlash AttentionDeepSpeed ZeRO 上手操作平台激活环境微调 参考教程&#xff1a;XTuner Finetune简介 LLM的下游应用任务中&#xff0c;增量预训练和指令跟随…

【python】python新年烟花代码【附源码】

欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 新年的钟声即将敲响&#xff0c;为了庆祝这个喜庆的时刻&#xff0c;我们可以用 Python 编写一个炫彩夺目的烟花盛典。本文将详细介绍如何使用 Pygame 库创建一个令人惊叹的烟花效果。 一、效果图&#xff1a; 二…

互联网加竞赛 基于卷积神经网络的乳腺癌分类 深度学习 医学图像

文章目录 1 前言2 前言3 数据集3.1 良性样本3.2 病变样本 4 开发环境5 代码实现5.1 实现流程5.2 部分代码实现5.2.1 导入库5.2.2 图像加载5.2.3 标记5.2.4 分组5.2.5 构建模型训练 6 分析指标6.1 精度&#xff0c;召回率和F1度量6.2 混淆矩阵 7 结果和结论8 最后 1 前言 &…

Linux 部署 AI 换脸

我使用的系统是 Ubuntu 20.04 文章实操主要分为以下几个部分 1、python 环境安装 2、下载 FaceFusion 上传服务器 3、创建 python 虚拟环境 4、下载 FaceFusion 依赖&#xff08;这里的命令执行时间会很长&#xff0c;够你睡午觉了&#xff09; 5、运行 FaceFusion 6、开…

基于css实现动画效果

介绍 本文将会基于css&#xff0c;实现各种动画效果&#xff0c;接下来会从简单几个例子入手。 案例 三颗球 <!DOCTYPE html> <html lang"en"><head><meta charset"utf-8" /><title>React App</title><style>…

初识Hadoop-概述与关键技术

一.大数据概述 1.什么是大数据 高速发展的信息时代&#xff0c;新一轮科技革命和变革正在加速推进&#xff0c;技术创新日益成为重塑经济发展模式和促进经济增长的重要驱动力量&#xff0c;而“大数据”无疑是核心推动力。 那么&#xff0c;什么是“大数据”呢&#xff1…

0基础学习VR全景平台篇第137篇:720VR全景,DJI无人机遥控器调参

上课&#xff01;全体起立~ 大家好&#xff0c;欢迎观看蛙色官方系列全景摄影课程&#xff01; 这节课以御2为例 介绍的是无人机调参 步骤一&#xff1a;下载DJI Go 4并注册账号 步骤二&#xff1a;拿下遥杆并装好&#xff0c;展开遥控天线。将无人机与遥控器相连&#xff…

Elasticsearch倒排索引详解

倒排索引&#xff1a; 组成 term index(词项索引 &#xff0c;存放前后缀指针) Term Dictionary&#xff08;词项字典&#xff0c;所有词项经过文档与处理后按照字典顺序组成的一个字典&#xff08;相关度&#xff09;&#xff09; Posting List&#xff08;倒排表&#xf…

conda环境下cannot write keep file问题解决

1 问题描述 conda环境下执行如下命令报错&#xff1a; pip install githttps://github.com/wenet-e2e/wenet.git 错误信息如下&#xff1a; (pt) PS D:\code\ptcontainer> pip install githttps://github.com/wenet-e2e/wenet.git Looking in indexes: http://pypi.doub…

锤科HandShaker修改版,支持安卓14、澎湃OS

如今几乎各家手机厂商都在布局生态&#xff0c;但PC端往往是最容易被忽略的一环&#xff0c;哪怕是很强的华为鸿蒙、小米澎湃&#xff0c;想要做到手机和电脑互联&#xff0c;也限制了笔记本机型 虽然我一直致力于解锁非小米电脑安装小米电脑管家&#xff0c;比如前几天刚刚更…

UniApp调试支付宝沙箱(安卓)

先看下这里完整的交互的图&#xff1a;小程序文档 - 支付宝文档中心 一、打包 不管怎样&#xff0c;先打个包先。可以直接使用云端证书、云端打包&#xff0c;只需要指定包名即可。 二、在支付宝开放平台创建应用 这个参考官方的过程就可以了&#xff0c;只要有刚才打的包&…

MySQL安装部署-单机版

MySQL是关系型数据库&#xff0c;本文主要描述在操作系统Linux CentOS 7下安装MySQL Server 8.035单机版本。 https://dev.mysql.com/downloads/mysql/ 如上所示&#xff0c;从MySQL官方网站下载开源社区版本MySQL Server 8.035的最新稳定版本&#xff0c;该版本是对应Linux …

计算机网络-2019期末考试解析

【前言】 从内容上看比较像计算机网络课程了&#xff0c;先做了。 一&#xff0e;填空选择题&#xff08;共 20 分&#xff0c;每空 1 分&#xff09; 1 、双绞线由两根相互绝缘的、绞合成均匀的螺纹状的导线组成&#xff0c;下列关于双绞线的叙述&#xff0c;不正确的是___ __…

Mysql——索引相关的数据结构

索引 引入 我们知道&#xff0c;数据库查询是数据库的最主要功能之一。我们都希望查询数据的速度能尽可能的快&#xff0c;因此数据库系统的设计者会从查询算法的角度进行优化。最基本的查询算法当然是顺序查找&#xff08;linear search&#xff09;&#xff0c;这种复杂度为…

2023年全国职业院校技能大赛软件测试赛题—单元测试卷⑨

单元测试 一、任务要求 题目1&#xff1a;根据下列流程图编写程序实现相应分析处理并显示结果。返回文字“xa*a*b的值&#xff1a;”和x的值&#xff1b;返回文字“xa-b的值&#xff1a;”和x的值&#xff1b;返回文字“xab的值&#xff1a;”和x的值。其中变量a、b均须为整型…