0

在下面的代码中,我查询日期并按名称排序(我不索引日期字段可能听起来很奇怪,但我这样做是为了避免在内存中排序,这就是我按名称索引的原因)。如果我正在运行解释,我会得到以下信息:

-> index on name
cursor: BtreeCursor name_1
scanAndOrder: False
nscanned: 1000
nscannedObjects: 1000
n:49
millis:1

然后,如果我使用名称和日期创建复合索引,我会得到以下输出:

-> index on name + date
cursor: BtreeCursor name_1_date_1
scanAndOrder: False
nscanned: 1000
nscannedObjects: 1000
n:49
millis:1

即使我的查询不包含索引或其前缀,在我看来,索引应该能够在第二种情况下直接从索引中读取日期字段,因此 nscannedObject 应该等于 n = 49。确实所有信息都已经在索引中扫描文档的数量应该等于返回结果的数量。看来这里不是这样。我错了还是我做错了什么?

import pymongo
from pymongo import MongoClient

import datetime 
import random

def printCursorExplain(e):
    print 'cursor: ' + e['cursor'] 
    print 'scanAndOrder: ' + str(e['scanAndOrder']) 
    print 'nscanned: ' + str(e['nscanned'])
    print 'nscannedObjects: ' +  str(e['nscannedObjects'])
    print 'n:' + str(e['n'])
    print 'millis:' + str(e['millis'])
    print '---------------------------------------------------------------------------------\n'

client = MongoClient()
db = client.DBQStackOverflow


name_list = ["Sylvain", "Tweety", "Toto", "Titi", "Sylvester"]
YEAR_LIST = [2014]

def generateRandomDate():

    YYYY = YEAR_LIST[random.randint(0,len(YEAR_LIST)-1)]
    MM   = random.randint(1,12)
    DD   = random.randint(1,28)
    date = datetime.datetime(YYYY, MM, DD) 
    return date

def insert():
    for i in range(0, 1000):
        start_date = generateRandomDate()        
        name = name_list[random.randint(0,len(name_list)-1)]
        db.collection.insert( {"date": start_date, "name" :name})


insert()

YYYY = 2014
MM   = 5
DD   = 1
dateCIS = datetime.datetime(YYYY, MM, DD) 


YYYY = 2014
MM   = 5
DD   = 12
dateCIE = datetime.datetime(YYYY, MM, DD) 


queryDict =  {"date" : {"$gte": dateCIS, "$lte": dateCIE}} 
db.collection.create_index([("name", pymongo.ASCENDING)])
db.collection.create_index([("name", pymongo.ASCENDING),("date", pymongo.ASCENDING)], pymongo.ASCENDING)

print "-> index on name"
cursor1 = db.collection.find(queryDict).hint([("name", pymongo.ASCENDING)]).sort([("name", pymongo.ASCENDING)])#.limit(100)
e1 = cursor1.explain()
printCursorExplain(e1)

print "-> index on name + date"
cursor2 = db.collection.find(queryDict).hint([("name", pymongo.ASCENDING),("date", pymongo.ASCENDING)]).sort([("name", pymongo.ASCENDING)])#.limit(100)
e2 = cursor2.explain()
printCursorExplain(e2)
4

2 回答 2

1

出于类似的原因,您的两个索引都会导致对索引键 ( nscanned) 和文档 ( ) 进行全面扫描。nscannedObjects

名称索引

由于您正在搜索date和排序name,因此该索引可用于以正确的排序顺序返回结果..但该date值需要与每个文档进行比较以确定查询是否匹配。

姓名+日期索引

前缀仍然与您的name排序顺序匹配,但{name, date}不能有效地使用复合索引来匹配date值,因为name必须首先检查所有值。这实际上与第一个索引的结果相同。

推荐指数

如果您查询date和排序依据name,最佳索引顺序实际上应该是{date, name}. 这将使索引对于匹配date返回按排序顺序排列的结果都很有用name

注意:通常您不想使用该hint()命令来强制使用特定索引(尽管我假设您在这种情况下这样做是为了测试结果)。如果查询优化器没有选择您期望的索引,那么该索引很可能不是最佳选择。

您应该会发现这篇博文对阅读很有帮助:优化 MongoDB 复合索引

于 2014-07-31T00:59:56.360 回答
0

关键是 MongoDB 不能使用您的任何一个索引来确定哪些文档与查询条件匹配。它可以使用任一索引来帮助进行排序。所以 MongoDB 会扫描整个索引,因为这会以正确的顺序返回文档,但仍然需要拉起每个文档 (nScannedObjects = 1000) 以检查它是否与查询条件匹配。

于 2014-07-23T18:46:10.297 回答