Monary在MongoDB上执行高性能列查询
项目描述
简介
MongoDB 是一种面向文档的数据库,旨在快速访问数据记录(或行)。在对大型数据集进行数据分析时,通常希望将其组织成列式格式。数据列可以视为数学向量,针对存储在向量形式中的数据,存在大量收集统计信息的技术。
对于 小型到中型 的集合,可以在现代PC的内存中实现多个数据列。例如,一个包含一亿个双精度数字的数组占用八亿字节,或大约0.75 GB。对于更大的问题,仍然可以在内存中实现大部分数据,或者以多个段的方式处理数据。(非常大规模的问题需要更强大的工具,如map/reduce。)
使用Python从MongoDB提取列数据相对简单。在PyMongo中,collection.find() 生成一系列字典对象。在处理数百万条记录时,关键是不要将这些字典保留在内存中,因为它们通常很大。幸运的是,在数据加载时,可以轻松地将数据移动到数组中。
首先,让我们创建350万行测试数据
#!/usr/bin/env python
import random
import pymongo
NUM_BATCHES = 3500
BATCH_SIZE = 1000
# 3500 batches * 1000 per batch = 3.5 million records
c = pymongo.MongoClient()
collection = c.mydb.collection
for i in xrange(NUM_BATCHES):
stuff = [ ]
for j in xrange(BATCH_SIZE):
record = dict(x1=random.uniform(0, 1),
x2=random.uniform(0, 2),
x3=random.uniform(0, 3),
x4=random.uniform(0, 4),
x5=random.uniform(0, 5)
)
stuff.append(record)
collection.insert(stuff)
以下是一个使用numpy数组的示例
#!/usr/bin/env python
import numpy
import pymongo
c = pymongo.MongoClient()
collection = c.mydb.collection
num = collection.count()
arrays = [ numpy.zeros(num) for i in range(5) ]
for i, record in enumerate(collection.find()):
for x in range(5):
arrays[x][i] = record["x%i" % x+1]
for array in arrays: # prove that we did something...
print numpy.mean(array)
对于350万条记录,这个查询在运行Ubuntu 10.10 64位的EC2大型实例上需要85秒,在我的MacBook Pro(2.66 GHz Intel Core 2 Duo,8 GB RAM)上需要88秒。
考虑到每秒加载20万个以上的值,这些时间似乎很有吸引力。然而,更仔细地检查会发现,其中大部分时间是由pymongo花费在读取每个查询结果并将BSON结果转换为Python字典上。(如果你查看CPU使用情况,你会发现Python使用了90%或更多的CPU。)
Monary
如果我们绕过PyMongo驱动程序,可以更快地查询。为了演示这一点,我开发了一个简单的C库和相应的Python包装器 Monary,它利用了MongoDB C驱动程序。该代码旨在接受一个所需的字段列表,并从BSON结果中加载恰好那些字段到提供的数组存储中。
以下是一个使用monary进行相同查询的示例
#!/usr/bin/env python
from monary import Monary
import numpy
with Monary("127.0.0.1") as monary:
arrays = monary.query(
"mydb", # database name
"collection", # collection name
{}, # query spec
["x1", "x2", "x3", "x4", "x5"], # field names (in Mongo record)
["float64"] * 5 # Monary field types (see below)
)
for array in arrays: # prove that we did something...
print numpy.mean(array)
Monary能够在4秒内完成相同的查询,每秒处理大约400万个值(快20倍!)以下是对比PyMongo和Monary查询的简要总结:
PyMongo插入 – EC2: 102秒 – Mac: 76秒
PyMongo查询 – EC2: 85秒 – Mac: 88秒
Monary查询 – EC2: 5.4秒 – Mac: 3.8秒
当然,这个测试创建了一些相当理想的情况:它查询集合中的每条记录,记录只包含查询数据(加上ObjectID),并且数据库是本地运行的。如果使用远程服务器,记录更大,或者只查询记录的子集(可能需要扫描更多记录或使用索引),性能可能会下降。
Monary现在支持以下类型
id(Mongo的12字节ObjectId)
int8
int16
int32
int64
float32
float64
bool
date(存储为int64,自纪元以来的毫秒数)
Monary的源代码可在bitbucket上找到。它包括Mongo C驱动程序的副本,需要编译和安装,可以通过包含的“setup.py”文件完成。安装脚本可以工作,但处于某种粗糙的状态。任何来自distutils专家的帮助都将非常感激!要从Python运行Monary,您需要安装pymongo和numpy包。
Monary已逐渐增加功能(包括最近添加的更多数值类型和日期类型)。以下是一些计划中的未来改进
支持字符串/二进制类型
(我希望开发Monary以支持将大多数BSON类型合理映射到数组存储的一些映射。)
支持获取嵌套字段(例如“x.y”)
(可能)移除对PyMongo和NumPy的依赖
(目前使用Monary必须安装这些。)
项目详情
Monary-0.5.0.tar.gz的散列值
算法 | 散列摘要 | |
---|---|---|
SHA256 | a69a3f9ddd493958432b0a57f37d6efed757bb0493ecc8d6c271685cc6505268 |
|
MD5 | 0baf204f6bea7f0406d47f1bf57b03bd |
|
BLAKE2b-256 | 35b6230a3ec114337e324f372106b83a88efe2043f9adda551292ff57cc1262d |