Skip to main content
 首页 » 编程设计

python之MongoDb 速度下降

2025年05月04日8落叶无声

我使用 mongodb 来存储压缩的 html 文件。 基本上,一个完整的 mongod 文档是这样的:

{'_id': 1, 'p1': data, 'p2': data2, 'p3': data3} 

其中数据、数据 1、数据 3 是:bson.binary.Binary(zlib_compressed_html)

我有 1200 万个 ID,每个 dataX 的平均大小为 90KB, 所以每个文档的大小至少为 180KB + sizeof(_id) + some_overhead。

总数据大小至少为 2TB。

我想注意到 '_id' 是索引。

我通过以下方式插入到 mongo 中:

def _save(self, mongo_col, my_id, page, html): 
    doc = mongo_col.find_one({'_id': my_id}) 
    key = 'p%d' % page 
    success = False 
    if doc is None: 
        doc = {'_id': my_id, key: html} 
        try: 
            mongo_col.save(doc, safe=True) 
            success = True 
        except: 
            log.exception('Exception saving to mongodb') 
    else: 
        try: 
            mongo_col.update({'_id': my_id}, {'$set': {key: html}}) 
            success = True 
        except: 
            log.exception('Exception updating  mongodb') 
    return success 

如您所见,我首先查找集合以查看是否有文档 my_id 存在。

如果它不存在,那么我创建它并将它保存到 mongo,否则我更新它。

上面的问题是,虽然它非常快,但在某些时候它变得非常慢。

我会给你一些数字:

当速度很快时,我每 4 小时做 1.500.000 次,之后每 4 小时做 300.000 次。

我怀疑这会影响速度:

注意

当执行增加文档大小超过为该文档分配的空间的更新操作时,更新操作会重新定位磁盘上的文档,并可能根据更新类型重新排序文档字段。

从这些驱动程序版本开始,所有写操作都会发出 getLastError 命令来确认写操作的结果: { getLastError: 1 } 有关详细信息,请参阅写入操作文档中有关写入问题的文档。

以上来自:http://docs.mongodb.org/manual/applications/update/

我这么说是因为我们可以有以下内容:

{'_id: 1, 'p1': some_data}, ...., {'_id': 10000000, 'p2': some_data2}, ...{'_id': N, 'p1 ': sd3}

并假设我将上述 _save 方法调用为:

_save(my_collection, 1, 2, bin_compressed_html)

然后它应该用 _id 1 更新文档。但是如果mongo网站是这样的话, 因为我在文档中添加了一个键,它不适合并且应该重新排列文档。

可以将文档移动到集合的末尾,这可能在磁盘上很远。这会减慢速度吗?

或者速度变慢与集合的大小有关?

以任何方式你认为修改我的结构应该更有效:

{'_id': ObjectId, 'mid': 1, 'p': 1, 'd': html}

其中 mid=my_id, p=page, d=compressed html

并修改 _save 方法只做插入?

def _save(self, mongo_col, my_id, page, html): 
    doc = {'mid': my_id, 'p': page, 'd': html} 
    success = False 
    try: 
        mongo_col.save(doc, safe=True) 
        success = True 
    except: 
        log.exception('Exception saving to mongodb') 
    return success 

这样我就避免了更新(因此在磁盘上重新排列)和一次查找(find_one) 但文件将是 3 倍多,我将有 2 个索引(_id 和 mid)。

你有什么建议?

请您参考如下方法:

如果您继续添加 html 页面作为新属性,文档重定位可能会成为一个问题。将页面移动到一个新的集合中真的会成为一个问题吗?您可以在其中简单地为每个页面添加一条记录?另外,我真的不认为 MongoDB 非常适合您的用例。例如。 Redis会更有效率。 您应该注意的另一件事是 have enough ram为您的 _id 索引。使用 db.mongocol.stats() 检查索引大小。