首页

13.2 MongoDB的常见陷阱

关灯 护眼    字体:

上一章 目录 下一章




13.2.1  默认超时时间


1.问题描述

在MongoDB的一个名为test_data的集合中有两百条数据,如图13-5所示。

逐行读取并打印,每打印一行内容就暂停7秒钟。在读取第101行数据时报错,提示找不到游标(Cursor),如图13-6所示。

图13-5  test_data数据集

图13-6  读取第101行数据时报错

2.问题原因

这个问题可以说不是故障,而是功能。由于网络连接非常消耗时间,如果  for  循环每打印一行再连接MongoDB读取下一行,那么网络连接将会消耗太多的时间。所以,PyMongo默认会一次性取101行数据。

对于如下代码:

for  data  in  handler.find():

print(f’这一行数据为:{data}')

在循环的第1轮,PyMongo会连接MongoDB然后获取前101行数据,并把这些数据缓存起来。循环的第2~101轮(第10轮对应的数据为100,因为第1轮对应的数据为0)都不会再发起网络连接,而是直接从缓存里一行一行读取数据。这本来是一个提高查询速度的功能。

在使用PyMongo时,我们不需要显式地关闭游标。这是因为MongoDB会自动监控,如果发现一个游标在10分钟内没有进行任何的数据库操作,就会把这个它关掉。这也是一个方便开发的功能。

提示:

游标(Cursor)可以理解为一个标记,用来指向本次查询的数据位置。每读取一次数据,游标就向下移动一条数据。

PyMongo  的  find()方法返回的就是一个游标对象,当使用  for  循环对游标进行迭代时,  PyMongo  才会读取数据。这就是为什么无论查询条件有多复杂,集合数据量有多大,执行collection.find(查询条件)时都是立刻完成的。因为,此时只得到了一个游标,只有在对游标进行迭代时才会真正去连接MongoDB进行查询。

然而,两个功能在一起就导致了问题。因为每次循环会暂停7秒钟,那么101次循环就会暂停707秒钟,大于10分钟。循环进行到第102轮时,PyMongo会尝试发起下一次请求,但这时MongoDB已经关闭了游标,所以MongoDB不知道应该从哪里开始查询,就会得到一个找不到游标的异常。

3.解决方法

游标对象有一个方法叫作“batch_size”,它的作用是限制PyMongo每一次连接MongoDB批量读取多少条数据。由于程序每一次会暂停7秒钟,假设这个暂停时间是有必要的,没法缩减的,那么可以通过减小批量获取数据的条数来防止游标超时。

由于10分钟为600秒,所以只要批量获取数据的条数小于85,就可以在游标超时之前连接MongoDB获取下一批数据,也就可以解决游标超时的问题。

修改以后的代码如下:

还有一种办法——设置游标永久有效。collection.find()可以设置一个参数:no_cursor_timeout。如果把这个参数设置为True,则游标就不会过期,具体格式如下:

collection.find({'name':  'xxx',  'age':  20},  {'_id':  0},  no_cursor_timeout=True)

例如:

其中,主要代码说明如下。

●  第1行代码:查询所有数据获得游标,并把游标赋值给一个名为cursor的变量。

●  第2~4行代码:迭代游标,查询数据。

●  第5行代码:显式关闭游标。

提示:

第二种做法应谨慎使用。因为一旦设置游标永不超时,那么使用完成以后必须手动关闭游标,否则它将会一直占用MongoDB的资源(即使Python程序已经关闭了,被占用的资源也不会自动释放),直到重启MongoDB。



13.2.2  硬盘空间的使用


随着数据量的增加,MongoDB  占用的硬盘空间也会随之增加。假设某一个集合里有10  000  000条数据,占用硬盘空间4GB。现在删除9  999  999条数据,你会发现这个只剩下1条数据的集合占用的空间仍然是4GB。如果想释放硬盘空间,则需要把整个集合删除。

MongoDB提供了一些命令,可以在不删除集合的情况下释放硬盘空间,但是这些命令并不通用,有一些只能用在单机单节点模式中,有一些只能用在集群中,有一些只能用在WiredTiger模式中,还有一些只能用在MMApv1模式中。并且在释放空间时,集合的读写功能会受到影响。

由于  MongoDB  储存空间优化是数据库工程师的工作,不是本书需要考虑的内容,因此这里介绍一个通用又简单的解决办法:

(1)把新的数据写入新的集合中。

(2)老数据里需要留下的部分也重新插入新的集合。

(3)删除老集合。

(4)重建索引。




上一章 目录 下一章