数据库中的索引
- 说明:索引是一种排序好的便于快速查询的数据结构
- 作用:帮助数据库高效地查询数据
索引优缺点
- 优点
提高数据查询的效率,降低数据库的IO成本;
通过索引对数据进行排序,降低数据排序的成本,降低CPU的消耗。
- 缺点
占用磁盘空间;
大量索引影响SQL语句效率,因为每次插入和修改都需要更新索引。
语法
- 创建索引语法:db.集合名.createIndex(待创建索引的列[,额外选项])
- 参数:
待创建索引的列:{键:1,…,键:-1}
说明:1升序,2降序,例如{age:1}表示创建age索引并按照升序的方式存储。
额外选项:设置索引的名称或者唯一索引等等。
- 删除索引语法:
全部删除:db.集合名.dropIndexes()
删除指定:db.集合名.dropIndex(索引名)
- 查看索引语法:db.集合名.getIndexes()
练习
准备
use test5
for(var i=0;i<100000;i++){
db.c1.insert({'name':"aaa"+i,"age":i});
}
> use test5
switched to db test5
> for(var i=0;i<100000;i++){
... db.c1.insert({'name':"aaa"+i,"age":i});
... }
WriteResult({ "nInserted" : 1 })
> db.c1.count()
100000
创建普通索引
- 给name添加普通索引
> db.c1.createIndex({name:1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
> db.c1.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1 # 默认的_id索引
},
"name" : "_id_",
"ns" : "test5.c1"
},
{
"v" : 2,
"key" : {
"name" : 1 # 新建的name索引
},
"name" : "name_1",
"ns" : "test5.c1"
}
]
- 删除刚创建的name索引
> db.c1.dropIndex('name_1')
{ "nIndexesWas" : 2, "ok" : 1 }
> db.c1.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test5.c1"
}
]
- 给name创建索引并起名为woniu
> db.c1.createIndex({name:1},{name:"woniu"})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
> db.c1.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test5.c1"
},
{
"v" : 2,
"key" : {
"name" : 1
},
"name" : "woniu",
"ns" : "test5.c1"
}
]
创建复合/组合索引
需求:给name和age添加组合索引
说明:就是一次性给两个字段建立索引
语法:db.集合名.createIndex({键1:方式,键2:方式})
> db.c1.createIndex({name:1,age:1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 2,
"numIndexesAfter" : 3,
"ok" : 1
}
> db.c1.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test5.c1"
},
{
"v" : 2,
"key" : {
"name" : 1
},
"name" : "woniu",
"ns" : "test5.c1"
},
{
"v" : 2,
"key" : {
"name" : 1,
"age" : 1
},
"name" : "name_1_age_1",
"ns" : "test5.c1"
}
]
创建唯一索引
语法:db.集合名.createIndex(待添加索引的列,{unique:列名})
- 删除全部索引
> db.c1.dropIndexes()
{
"nIndexesWas" : 3,
"msg" : "non-_id indexes dropped for collection",
"ok" : 1
}
# 系统自带的索引不会删
> db.c1.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test5.c1"
}
- 设置唯一索引
> db.c1.createIndex({name:1},{unique:"name"})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
> db.c1.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test5.c1"
},
{
"v" : 2,
"unique" : true,
"key" : {
"name" : 1
},
"name" : "name_1",
"ns" : "test5.c1"
}
]
- 测试唯一索引的特性
> db.c1.insert({name:"a"})
WriteResult({ "nInserted" : 1 })
> db.c1.find({name:"a"})
{ "_id" : ObjectId("60311d35c7988913752fabc7"), "name" : "a" }
# name="a"的数据已经存在了,并且name列设置了唯一索引,因此再插入相同的数据就会报错
> db.c1.insert({name:"a"})
WriteResult({
"nInserted" : 0,
"writeError" : {
"code" : 11000,
"errmsg" : "E11000 duplicate key error collection: test5.c1 index: name_1 dup key: { : \"a\" }"
}
})
分析索引
- 语法:db.集合名.find().explain(‘executionStats’)
- 说明:
> db.c1.find({id:500}).explain('executionStats')
{
......
},
"executionStats" : { # 执行计划相关统计信息
"executionSuccess" : true,# 执行成功的状态
"nReturned" : 0,# 返回结果集数目
"executionTimeMillis" : 39,# 执行所需时间,毫秒
"totalKeysExamined" : 0,# 索引检查时间
"totalDocsExamined" : 100001,# 检查文档总数
"executionStages" : {
"stage" : "COLLSCAN",# 索引扫描方式
"filter" : { # 过滤条件
"id" : {
"$eq" : 500
}
},
"nReturned" : 0,# 返回结果集数目
"executionTimeMillisEstimate" : 42,# 预估执行时间,毫秒
"works" : 100003,# 工作单元数,一个查询会被派生为一些小的工作单元
"advanced" : 0,# 优先返回的结果数目
"needTime" : 100002,
"needYield" : 0,
"saveState" : 782,
"restoreState" : 782,
"isEOF" : 1,
"invalidates" : 0,
"direction" : "forward",
"docsExamined" : 100001
}
},
"serverInfo" : {
"host" : "LAPTOP-QBLKQL59",
"port" : 27017,
"version" : "3.4.4",
"gitVersion" : "888390515874a9debd1b6c5d36559ca86b44babd"
},
"ok" : 1
}
扫描方式:
COLLSCAN 全表扫描
IXSCAN 索引扫描
FETCH 根据索引去检索指定document
- age未添加索引情况
> db.c1.find({age:1}).explain('executionStats')
{
......
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 1,
"executionTimeMillis" : 41,
"totalKeysExamined" : 0,
"totalDocsExamined" : 100001,
"executionStages" : {
"stage" : "COLLSCAN",
"filter" : {
"age" : {
"$eq" : 1
}
},
"nReturned" : 1,
"executionTimeMillisEstimate" : 32,
"works" : 100003,
"advanced" : 1,
"needTime" : 100001,
"needYield" : 0,
"saveState" : 782,
"restoreState" : 782,
"isEOF" : 1,
"invalidates" : 0,
"direction" : "forward",
"docsExamined" : 100001
}
},
"serverInfo" : {
......
},
"ok" : 1
}
此时扫描方式为全表扫描。
- age添加索引后情况
> db.c1.createIndex({age:1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 2,
"numIndexesAfter" : 3,
"ok" : 1
}
> db.c1.find({age:1}).explain('executionStats')
{
"queryPlanner" : {
......
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 1,
"executionTimeMillis" : 9,
"totalKeysExamined" : 1,
"totalDocsExamined" : 1,
"executionStages" : {
"stage" : "FETCH",
"nReturned" : 1,
"executionTimeMillisEstimate" : 10,
"works" : 2,
"advanced" : 1,
"needTime" : 0,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"invalidates" : 0,
"docsExamined" : 1,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 1,
"executionTimeMillisEstimate" : 10,
"works" : 2,
"advanced" : 1,
"needTime" : 0,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"age" : 1
},
"indexName" : "age_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"age" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"age" : [
"[1.0, 1.0]"
]
},
"keysExamined" : 1,
"seeks" : 1,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0
}
}
},
"serverInfo" : {
......
},
"ok" : 1
}
此时索引方式为索引扫描,根据索引去检索指定文档,工作单元数大大减少,检索效率极大提高。
索引选择规则
- 为常做条件、排序、分组的字段建立索引
- 选择唯一索引
- 选择较小的数据列,为较长的字符串使用前缀索引