跳转到主要内容

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 (33.8 kB 查看散列值)

上传时间

支持