MongoDB之索引小谈

0. 什么是索引

索引就是用来加速查询的。数据库索引与书籍的索引类似:有了索引就不需要翻遍整本书,数据库则可以直接在索引中查找,使得查找速度能提高几个数量级。在索引中找到条目以后,就可以直接跳转到目标文档的位置。

MongoDB的索引几平与传统的关系型数据库索引一模一样,绝大多数优化 MySQL/Oracle/SQLite索引的技巧也同样适用于 MongoDB。

1. 创建索引

1.1 创建简单索引

MongoDB创建索引使用ensureIndex函数,函数实现可使用db.system.ensureIndex查询,实现如下:

function (keys, options) {
    var result = this.createIndex(keys, options);

    if (this.getMongo().writeMode() != "legacy") {
        return result;
    }

    err = this.getDB().getLastErrorObj();
    if (err.err) {
        return err;
    }
    // nothing returned on success
}

函数接受两个参数keys和options,其中keys表示索引内容,options表示索引创建附件条件,options可不传。以下为创建索引的具体示例:

db.UserInfo.ensureIndex({"userAccount": 1}); // 直接根据userAccount创建升序索引
db.UserInfo.ensureIndex({"userAccount": -1}); // 直接根据userAccount创建降序索引

题外话:如何查看MongoDB查询的执行过程?请看以下示例。

  • 不使用索引的查询
db.UserInfo.find().explain(); // 不使用索引的查询,返回如下:

{
	"queryPlanner" : {
		"plannerVersion" : 1,
		"namespace" : "test.UserInfo",
		"indexFilterSet" : false,
		"parsedQuery" : {
			
		},
		"winningPlan" : {
			"stage" : "COLLSCAN",
			"direction" : "forward"
		},
		"rejectedPlans" : [ ]
	},
	"serverInfo" : {
		"host" : "dev-ThinkPad-Edge-E460",
		"port" : 27017,
		"version" : "4.0.6",
		"gitVersion" : "caa42a1f75a56c7643d0b68d3880444375ec42e3"
	},
	"ok" : 1
}
  • 使用索引的查询
db.UserInfo.find({"userAccount":/abc/}).explain();  // 使用索引的查询,返回如下

{
	"queryPlanner" : {
		"plannerVersion" : 1,
		"namespace" : "test.UserInfo",
		"indexFilterSet" : false,
		"parsedQuery" : {
			"userAccount" : {
				"$regex" : "abc"
			}
		},
		"winningPlan" : {
			"stage" : "FETCH",
			"inputStage" : {
				"stage" : "IXSCAN",
				"filter" : {
					"userAccount" : {
						"$regex" : "abc"
					}
				},
				"keyPattern" : {
					"userAccount" : 1
				},
				"indexName" : "userAccount_1",
				"isMultiKey" : false,
				"multiKeyPaths" : {
					"userAccount" : [ ]
				},
				"isUnique" : false,
				"isSparse" : false,
				"isPartial" : false,
				"indexVersion" : 2,
				"direction" : "forward",
				"indexBounds" : {
					"userAccount" : [
						"[\"\", {})",
						"[/abc/, /abc/]"
					]
				}
			}
		},
		"rejectedPlans" : [ ]
	},
	"serverInfo" : {
		"host" : "dev-ThinkPad-Edge-E460",
		"port" : 27017,
		"version" : "4.0.6",
		"gitVersion" : "caa42a1f75a56c7643d0b68d3880444375ec42e3"
	},
	"ok" : 1
}

1.2 创建唯一索引

db.UserInfo.ensureIndex({"userNo":1},{"unique":true}); // 创建一个以UserNo为键的唯一索引
db.UserInfo.ensureIndex({"userNo":1},{"unique":true, "dropDups":true}); // 创建一个以UserNo为键的唯一索引。如果存在重复文档,则保留查询到的第一条,删除其他文档。

注意:其中选项“dropDups”在使用时要特别注意,该操作会删除重复文档,建议操作前备份。

1.3 后台创建索引

db.UserInfo.ensureIndex({"userAccount":1},{"background":true}); // 后台创建索引

注意:非后台建立索引数据库会阻塞建立索引期间的所有请求,建议生产环境或数据量大的环境使用后台创建索引。

1.4 自定义索引名

db.UserInfo.ensureIndex({"userAccount":1},{"name":"idx_ua_asc"}); // 直接创建索引并设置索引名称为idx_ua_asc

注意:索引名最长为127字节,大于127字节的索引名创建索引会失败。

2. 查询索引

MongoDB查询索引使用getIndexes函数,函数实现可使用db.system.getIndexes查询,实现如下:

function (filter) {
    var res = this._getIndexesCommand(filter);
    if (res) {
        return res;
    }
    return this._getIndexesSystemIndexes(filter);
}

函数接受一个filter参数为查询条件,filter可不传。以下为查询索引的具体示例:

db.UserInfo.getIndexes(); // 查询所有所有索引
db.UserInfo.getIndexes({"name":"idx_ua_asc"}); // 查找名称为idx_au_asc的索引

3. 删除索引

MongoDB删除索引使用dropIndexes函数和dropIndex函数删除索引,函数实现可分别使用db.system.dropIndexes和db.system.dropIndex查询,函数实现分别如下:

// dropIndexes
function () {
    if (arguments.length)
        throw Error("dropIndexes doesn't take arguments");

    var res = this._db.runCommand({deleteIndexes: this.getName(), index: "*"});
    assert(res, "no result from dropIndex result");
    if (res.ok)
        return res;

    if (res.errmsg.match(/not found/))
        return res;

    throw _getErrorWithCode(res, "error dropping indexes : " + tojson(res));
}

// dropIndex
function (index) {
    assert(index, "need to specify index to dropIndex");
    var res = this._dbCommand("deleteIndexes", {index: index});
    return res;
}

其中dropIndexes函数删除除过_id键索引以外的全部索引,不接受参数;dropIndex接受一个index参数,删除满足条件的指定索引。具体使用示例如下:

db.UserInfo.dropIndexes(); // 删除所有索引,不包括_id索引
db.UserInfo.dropIndex("idx_ua_asc"); // 删除名称为idx_au_asc的索引

4. 更新索引

更新索引使用方法同创建索引

5. 指定索引查询

db.UserInfo.find({"userAccount":/abc/}).hint({"userAccount":-1}); // 指定索引查询。

注意:慎用!MongoDB查询器会智能选择使用最优的索引查询。

6. 地理位置索引

6.1 创建索引

db.map.ensureIndex({"gps":"2d"}); // 创建地理位置索引,注意值为"2d"

注意:"gps"键的值必须是某种形式的一对值:一个包含两个元素的数组或是包含两个键的内嵌文档。下面这些都是有效的:

{"gps": [0,100]};
{"gps": {"x":-10, "y":100}};
{"gps": {"latitude":-180, "longitude":180}};

键名可以随意,例如{"gps":{"foo":100,"bar":100}}也是可以的。默认情况下,地理空间索引假设值的范围是-180~180,对于经纬度来说非常方便。要是想用其他值,可以通过ensureIndex的选项指定最大值最小值。如下:

db.map.ensureIndex({"light-year":"2d"},{"min":-1000,"max":1000});

6.2 创建复合索引

db.map.ensureIndex({"location":"2d", "desc":1}); // 创建地理空间复合索引

6.3 查询、删除、修改索引

地理位置索引的查询、删除、修改同上,不再赘述。

6.4 关于地理位置查询的题外话

db.map.find({"gps":{"$near":[20,10]}}); // 查找距离点(20,10)最近的点,默认返回100个
db.map.find({"gps":{"$near":[20,10]}}).limit(10); // 查找距离点(20,10)最近的十个位置
db.runCommand({geoNear:"map", near:[20,10], num:10}); // 查找距离点(20,10)最近的十个位置
db.map.find({"gps":{"$within":{"$box":[[0,0],[20,10]]}}}); // 查找在一个矩形范围内的位置,$box的两个参数依次为矩形左下角坐标和右上角坐标
db.map.find({"gps":{"$within":{"$center":[[0,0],5]}}}); // 查找在一个圆形范围内的位置,$center的两个参数依次为圆心坐标和圆半径

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值