Open kagxin opened 4 years ago
这篇文章里使用的pymongo,对mongodb的数据和索引进行操作。
生成一个测试用的集合,文档数量为1000000个
from pymongo import MongoClient from mongo import m import random import datetime mongo_uri = 'mongodb://root:root@localhost:27017' connection = MongoClient(mongo_uri) m = connection['demo'] for i in range(1000000): t = { 'i': i, 'username': 'username' + str(i), 'age': random.randint(1, 100), 'created': datetime.datetime.now() } m.users.insert(t) m.users.insert({ 'i': 101, 'username': 'username' + str(101), 'age': 20, 'created': datetime.datetime.now() })
使用explain分析索引性能 参考1 参考2
应该在基数比较高的字段上建立索引,是最高效的。
基数就是集合中某个字段拥有不同值的数量
索引会影响插入修改的性能,不应该在一个集合上创建过多的索引
mongodb 一个查询阶段只会使用一根索引
## 两个索引 age和created m.get_collection('users').create_index([ ('age', pymongo.ASCENDING) ]) m.get_collection('users').create_index([ ('created', pymongo.ASCENDING) ]) ## 范围查询 created和age m.get_collection('users').find( {'created': {'gte': (datetime.datetime.now() - datetime.timedelta(days=1))}, 'age': {'gt': 18}}).explain()
//explain 结果, 并未使用created和age上的两个索引,而是只是用了created上的索引 /* 1 */ { "queryPlanner" : { "plannerVersion" : 1, "namespace" : "demo.users", "indexFilterSet" : false, "parsedQuery" : { "$and" : [ { "age" : { "$eq" : { "gt" : 18.0 } } }, { "created" : { "$eq" : { "gte" : ISODate("2019-11-18T15:51:55.888Z") } } } ] }, "queryHash" : "3E2E5ACC", "planCacheKey" : "D9F6B0E9", "winningPlan" : { "stage" : "FETCH", "filter" : { "age" : { "$eq" : { "gt" : 18.0 } } }, "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "created" : 1 }, "indexName" : "created_1", "isMultiKey" : false, "multiKeyPaths" : { "created" : [] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : { "created" : [ "[{ gte: new Date(1574092315888) }, { gte: new Date(1574092315888) }]" ] } } }, "rejectedPlans" : [ ... ] }, "serverInfo" : { ... }, "ok" : 1.0 }
## 这种情况下最好是创建复合索引,这样mongodb会使用username_age这个复合索引 m.get_collection('users').create_index([ ('username', pymongo.ASCENDING), ('age', pymongo.ASCENDING) ])
m.get_collection('users').find({}).sort([('username', pymongo.ASCENDING), ('age', pymongo.DESCENDING)]).explain() ## 当使用 username和age进行一个正向,一个逆向排序时 ## ('username', pymongo.ASCENDING),('age', pymongo.ASCENDING) 这根索引将不再有效mongodb无法使用它进行排序 m.get_collection('users').create_index([ ('username', pymongo.ASCENDING), ('age', pymongo.ASCENDING) ]) ## 创建这样的索引是正确的选择('username', pymongo.ASCENDING),('age', pymongo.DESCENDING) m.get_collection('users').create_index([ ('username', pymongo.ASCENDING), ('age', pymongo.DESCENDING) ])
复合索引字段的顺序:应该将基数高的字段放在前面
复合索引最左前缀原则
如果有一个{"a":1, "b":1, "c":1, "d":1, ... "z":1}这样的索引,那么我们可以使用{"a":1},{"a":1, "b":1},{"a":1, "b":1, "c":1, "d":1} ...等一系列索引。但{"b":1}, {"a":1, "c":1}这样的查询不会被这根索引优化
一些查询操作符不能使用索引
不会使用所以的操作符,例如:$exist, $nin,$not, $where, 算术运算符等。$OR,会使用索引。
取一个集合中较小的数据集时,索引很高效。结果集在原集合中占比越大,索引的速度就越慢。因为使用索引要进行两次查询(有一个回表操作)。一般来说,结果集占比30%,就应该对比一下索引和全表扫描的效率了。
灵活使用那些很有用的特殊索引:固定集合索引,TTL索引,全文索引,地理空间索引
https://github.com/kagxin/blog/issues/52 https://docs.mongodb.com/manual/reference/operator/meta/explain/index.html https://docs.mongodb.com/manual/indexes/ 《mongodb权威指南》
db.getCollection('device_data').createIndex( { "date_time": 1 }, { expireAfterSeconds: 604800, background:true} )
添加了一个TTL索引,集合文档数量2200万减到950万。(添加之前删除了原有的date_time上的一个普通单列索引)
mongodb 索引注意事项
生成测试数据
mongodb几个注意点
使用explain分析索引性能 参考1 参考2
应该在基数比较高的字段上建立索引,是最高效的。
索引会影响插入修改的性能,不应该在一个集合上创建过多的索引
mongodb 一个查询阶段只会使用一根索引
复合索引字段的顺序:应该将基数高的字段放在前面
复合索引最左前缀原则
一些查询操作符不能使用索引
取一个集合中较小的数据集时,索引很高效。结果集在原集合中占比越大,索引的速度就越慢。因为使用索引要进行两次查询(有一个回表操作)。一般来说,结果集占比30%,就应该对比一下索引和全表扫描的效率了。
灵活使用那些很有用的特殊索引:固定集合索引,TTL索引,全文索引,地理空间索引
ref
https://github.com/kagxin/blog/issues/52 https://docs.mongodb.com/manual/reference/operator/meta/explain/index.html https://docs.mongodb.com/manual/indexes/ 《mongodb权威指南》