Mongodb索引实战
来源:互联网 发布:sphere软件 编辑:程序博客网 时间:2024/06/03 20:43
转载地址:
https://cnodejs.org/topic/555bf91ee684c4c8088a0c0f
关于mongodb的一个加索引的例子:
最近碰到这样的一个需求,一张酒店政策优惠表,我们要根据用户入住和离开的时间,计算一家酒店的最低价政策前10位,数据库表字段如下:
'hid':88, 酒店id'date':20150530, 入住日期整形(不要纠结unix时间戳)'enable':1, 政策是否启用'price':100, 政策价格'name':'abc', 政策名称'position':'china', 酒店位置'writeTime':datetime.datetime.now(), 写入时间
我们的查询语句也相对固定,都是这样的:
db.getCollection('hotels').find({"hid":88, "date":{"$gte":20150501, "$lte":20150510}, "enable":1}).sort({"price":1}).limit(10)
其中条件分为3个:1、酒店 id :“hid”:882、date在某个区间里3、enable启用为1,表示启用排序条件是一个:1、price正序排序
现在我往数据库插入10万条测试数据,插入脚本如下:
# -*- coding: utf-8 -*-import pymongoimport jsonimport datetime,timeimport sysimport copyimport sys, osfrom multiprocessing import Process, Value, Arrayfrom hashlib import md5from random import choice, randintdef getTimestampFromDatetime(d=None):if d is None:d = datetime.datetime.now()return time.mktime(d.timetuple())def md5Hash(str):m = md5()m.update(str)return m.hexdigest().upper()def task():#10分之一的概率无法使用enableList = [1,1,1,1,1,1,1,1,1,0]dateList = []for i in range(31):dateInt = 20150501dateList.append(dateInt+i)mongoUri = 'mongodb://10.14.40.62:27017/hotel'all_data = {'hid':0,'date':0,'enable':0,'price':0,'name':'abc','position':'china','writeTime':datetime.datetime.now(),}tableName = 'hotels'client = pymongo.MongoClient(mongoUri, max_pool_size=100)db = client.hotellistData = []for i in range(100000):all_data['price'] = randint(100, 10000)all_data['enable'] = choice(enableList)all_data['date'] = choice(dateList)all_data['hid'] = randint(1, 100)listData.append(copy.copy(all_data))db[tableName].insert(listData)if __name__ == '__main__':start = getTimestampFromDatetime()task()end = getTimestampFromDatetime()print('time: {0}s'.format(end-start))
一、不建任何索引查询:我们执行如下语句,查看语句执行情况:
db.getCollection('hotels').find({"hid":88, "date":{"$gte":20150501, "$lte":20150510}, "enable":1}).sort({"price":1}).limit(10).explain()
我们看到结果:
"n" : 10,"nscannedObjects" : 100000,"nscanned" : 100000,..."scanAndOrder" : true,..."millis" : 200,
其中 n 表示最终返回的结果,nscannedObjects表示我们扫描了多少数据,scanAndOrder表示我们进行了扫描并排序的操作,这是非常消耗cpu和内存的。
从结果来看,我们对10万条数据进行了全表扫描,最终得出10条结果出来。显然这个方案我们不能接受,时间我们花费了200毫秒,这个速度如果上线应用,肯定是不行的。
二、对hid加上索引我们很容易就想到,对hid加上索引,这样我们第一个结果hid的搜索就可以快速将酒店的索引返回缩小,于是我们创建酒店 hid 的索引,然后同样执行上述语句。索引如下:
{"hid" : 1}
结果如下:
"n" : 10,"nscannedObjects" : 1024,"nscanned" : 1024,..."scanAndOrder" : true,..."millis" : 58ms,
对比上述的结果,我们把200ms的查询通过hid索引一下优化到了58ms,从扫描全表10万条数据,修改为只扫描了1024条数据,同时我们的响应时间也下降到了58ms,我们是否可以再优化一下呢?
三、建立hid和date的联合索引我们发现查询还有第二个参数,date作为时间范围的,所以我们建立一个联合索引,hid:1, date:1这是否可以更加快一些?索引如下:
{"hid" : 1,"date" : 1}
结果如下:
"n" : 10,"nscannedObjects" : 326,"nscanned" : 326,..."scanAndOrder" : true,..."millis" : 6ms,
经过再次优化,这个查询一下就变成6ms返回,只扫描了326行数据了。但是我们只需要返回10条数据,扫描了300多行数据,是否可以再进行一次优化?
四、建立hid、date、enable的联合索引我们发现查询条件还有第三个参数 enable,由于enable大约有10分之一的数据是我们不要的,就是未启用的政策,所以我们把enable字段也加到索引中,索引如下:
{"hid" : 1,"date" : 1,"enable" : 1}
执行结果如下:
"n" : 10,"nscannedObjects" : 291,"nscanned" : 300,..."scanAndOrder" : true,..."millis" : 5ms,
这里nscanned和nscannedObjects不同,nscanned:300表示从数据库索引条目中搜索了300条数据,nscannedObjects表示在这300条中,出最终的10条记录,扫描了这300条中的291条。
根据上面的结果,我们通过索引又进一步优化了这个查询,但是还不满足,我是否可以再增加sort排序的索引来优化呢?
五、建立hid,date,enable,price联合索引我们把排序的索引也加到联合索引中,看看还能否再进一步优化这个查询了,建立索引如下:
{"hid" : 1,"date" : 1,"enable" : 1,"price" : 1}
同样的执行语句结果如下:
"n" : 10,"nscannedObjects" : 291,"nscanned" : 300,..."scanAndOrder" : true,..."millis" : 5ms,
我们发现,无论是 nscannedObjects 还是 nscanned,以及查询时间都没有任何帮助了,和之前一样了,似乎我们的优化已经完成了。
六、建立逆索引试试因为我们的查询条件有一个date作为区间查询的,而最终我们要得到的是根据price排序的结果,所以我们这样建立索引,看看是否对我们的查询有所帮助:
{"hid" : 1,"price" : 1,"date" : 1,"enable" : 1}
执行结果如下:
"n" : 10,"nscannedObjects" : 10,"nscanned" : 37,..."scanAndOrder" : false,..."millis" : 0ms,
看到结果令人满意,我们把成功的把一个原来200ms的查询优化到0ms了,我们从索引查找到37条记录保存在内存里,同时我们只扫描了其中的10条记录就把结果返回了。同时 scanAndOrder 这个字段也成为了false,表示我们没有做在内存里的扫描和排序操作,将会降低cpu和内存的消耗,我们的优化已经完成了。
不过需要指出一点,如果从写入性能来讲,可以考虑把 “enable” : 1 从索引中拿走,毕竟这个索引并不能很好的帮助我们大量减少筛选的数据。
总结一下:对于这种查询条件有 $in, $gte 等的区间操作的,并且带有sort排序的查询,合理的索引的建立,如果有条件优化到 scanAndOrder 结果为false,将大大的提升我们的数据库性能和响应时间。
- MongoDB索引实战技巧
- MongoDB索引实战技巧
- Mongodb索引实战
- MongoDB数据库索引实战技巧
- MongoDB实战(7)索引与性能
- 【mongoDB实战】mongo对某个键添加索引
- mongodb索引
- MongoDB索引
- MongoDB索引
- MongoDB 索引
- MongoDB 索引
- mongodb 索引
- MongoDB 索引
- mongodb 索引
- mongodb索引
- MongoDB 索引
- MongoDB索引
- MongoDB索引
- 无限互联--EGOTableViewPullRefresh个人理解代码原理和使用
- Shell for循环用法总结
- linuxGPIO驱动示例
- MVC5+Unity4.0注入依赖学习
- Web前端岗位面试题有哪些?
- Mongodb索引实战
- ios自带地图笔记
- Java基础------知识点整理(十)-----String类
- BZOJ 1798 维护序列 (多校连萌,对线段树进行加乘混合操作)
- MYSQL 分组合并函数
- 数据库索引的实现原理二
- 初探MEAN
- Darwin streaming server源码安装方式总结
- Python 中删除列表中所有的空元素