如何提高 pymongo 查询的性能
How to improve performance of pymongo queries
我继承了一个旧的 Mongo 数据库。让我们关注以下两个 collections (为了更好的可读性删除了大部分内容):
Collection 用户
db.user.find_one({"email": "user@host.com"})
{'lastUpdate': datetime.datetime(2016, 9, 2, 11, 40, 13, 160000),
'creationTime': datetime.datetime(2016, 6, 23, 7, 19, 10, 6000),
'_id': ObjectId('576b8d6ee4b0a37270b742c7'),
'email': 'user@host.com' }
Collections 条目(一个用户对多个条目):
db.entry.find_one({"userId": _id})
{'date_entered': datetime.datetime(2015, 2, 7, 0, 0),
'creationTime': datetime.datetime(2015, 2, 8, 14, 41, 50, 701000),
'lastUpdate': datetime.datetime(2015, 2, 9, 3, 28, 2, 115000),
'_id': ObjectId('54d775aee4b035e584287a42'),
'userId': '576b8d6ee4b0a37270b742c7',
'data': 'test'}
可以看到,两者之间没有DBRef
我想做的是计算条目总数,以及在给定日期后更新的条目数。
为此,我使用了 Python 的 pymongo 库。下面的代码得到了我需要的东西,但速度太慢了。
from pymongo import MongoClient
client = MongoClient('mongodb://foobar/')
db = client.userdata
# First I need to fetch all user ids. Otherwise db cursor will time out after some time.
user_ids = [] # build a list of tuples (email, id)
for user in db.user.find():
user_ids.append( (user['email'], str(user['_id'])) )
date = datetime(2016, 1, 1)
for user_id in user_ids:
email, _id = user_id
t0 = time.time()
query = {"userId": _id}
no_of_all_entries = db.entry.find(query).count()
query = {"userId": _id, "lastUpdate": {"$gte": date}}
no_of_entries_this_year = db.entry.find(query).count()
t1 = time.time()
print("delay ", round(t1 - t0, 2))
print(email, no_of_all_entries, no_of_entries_this_year)
在我的笔记本电脑上 运行 两个 db.entry.find
查询需要大约 0.83 秒,在 AWS 服务器(不是 MongoDB 服务器)上需要 0.54 秒。
拥有约 20000 名用户,需要 3 个小时才能获得所有数据。
这是您希望在 Mongo 中看到的那种延迟吗?我可以做些什么来改善这一点?请记住,MongoDB 对我来说是相当新的。
而不是 运行 分别为所有用户提供两个聚合,您可以使用 db.collection.aggregate()
.
为所有用户获取两个聚合
我们将其设为字典,而不是 (email, userId)
元组,因为它更易于使用来获取相应的电子邮件。
user_emails = {str(user['_id']): user['email'] for user in db.user.find()}
date = datetime(2016, 1, 1)
entry_counts = db.entry.aggregate([
{"$group": {
"_id": "$userId",
"count": {"$sum": 1},
"count_this_year": {
"$sum": {
"$cond": [{"$gte": ["$lastUpdate", date]}, 1, 0]
}
}
}}
])
for entry in entry_counts:
print(user_emails.get(entry['_id']),
entry['count'],
entry['count_this_year'])
我很确定可以将用户的电子邮件地址放入结果中,但我也不是 mongo 专家。
我继承了一个旧的 Mongo 数据库。让我们关注以下两个 collections (为了更好的可读性删除了大部分内容):
Collection 用户
db.user.find_one({"email": "user@host.com"})
{'lastUpdate': datetime.datetime(2016, 9, 2, 11, 40, 13, 160000),
'creationTime': datetime.datetime(2016, 6, 23, 7, 19, 10, 6000),
'_id': ObjectId('576b8d6ee4b0a37270b742c7'),
'email': 'user@host.com' }
Collections 条目(一个用户对多个条目):
db.entry.find_one({"userId": _id})
{'date_entered': datetime.datetime(2015, 2, 7, 0, 0),
'creationTime': datetime.datetime(2015, 2, 8, 14, 41, 50, 701000),
'lastUpdate': datetime.datetime(2015, 2, 9, 3, 28, 2, 115000),
'_id': ObjectId('54d775aee4b035e584287a42'),
'userId': '576b8d6ee4b0a37270b742c7',
'data': 'test'}
可以看到,两者之间没有DBRef
我想做的是计算条目总数,以及在给定日期后更新的条目数。
为此,我使用了 Python 的 pymongo 库。下面的代码得到了我需要的东西,但速度太慢了。
from pymongo import MongoClient
client = MongoClient('mongodb://foobar/')
db = client.userdata
# First I need to fetch all user ids. Otherwise db cursor will time out after some time.
user_ids = [] # build a list of tuples (email, id)
for user in db.user.find():
user_ids.append( (user['email'], str(user['_id'])) )
date = datetime(2016, 1, 1)
for user_id in user_ids:
email, _id = user_id
t0 = time.time()
query = {"userId": _id}
no_of_all_entries = db.entry.find(query).count()
query = {"userId": _id, "lastUpdate": {"$gte": date}}
no_of_entries_this_year = db.entry.find(query).count()
t1 = time.time()
print("delay ", round(t1 - t0, 2))
print(email, no_of_all_entries, no_of_entries_this_year)
在我的笔记本电脑上 运行 两个 db.entry.find
查询需要大约 0.83 秒,在 AWS 服务器(不是 MongoDB 服务器)上需要 0.54 秒。
拥有约 20000 名用户,需要 3 个小时才能获得所有数据。 这是您希望在 Mongo 中看到的那种延迟吗?我可以做些什么来改善这一点?请记住,MongoDB 对我来说是相当新的。
而不是 运行 分别为所有用户提供两个聚合,您可以使用 db.collection.aggregate()
.
我们将其设为字典,而不是 (email, userId)
元组,因为它更易于使用来获取相应的电子邮件。
user_emails = {str(user['_id']): user['email'] for user in db.user.find()}
date = datetime(2016, 1, 1)
entry_counts = db.entry.aggregate([
{"$group": {
"_id": "$userId",
"count": {"$sum": 1},
"count_this_year": {
"$sum": {
"$cond": [{"$gte": ["$lastUpdate", date]}, 1, 0]
}
}
}}
])
for entry in entry_counts:
print(user_emails.get(entry['_id']),
entry['count'],
entry['count_this_year'])
我很确定可以将用户的电子邮件地址放入结果中,但我也不是 mongo 专家。