一、少读少写少更新
虽然MongoDB相比于MySQL来说,速度快了很多,但是频繁读写MongoDB还是会严重拖慢程序的执行速度。以插入数据为例,对于相同的数据,进行逐条插入和批量插入,速度差异非常显著。
同样是插入10000条数据,逐条插入耗时约3.7s,批量插入耗时约0.2s。这个差距看起来已经很显著了吧。这还只是在本地测试的数据,如果使用远程的MongoDB服务器且数据量足够大,这个时间差甚至可以高达数小时。
建议把要插入到MongoDB中的数据先统一放到一个列表中,等积累到一定量再一次性插入。
对于读数据,在内存允许的情况下,应该一次性把数据读入内存,尽量减少对MongoDB的读取操作。
在某些情况下,更新操作不得不逐条进行,如果使用常规操作,需要一条一条更新,首先把所有数据读入内存,根据_id查找每一条记录后再逐一更新。每一条数据都不一样,似乎没有办法批量更新。
对于这种情况,是否有办法优化呢?答案当然是有,那就是不更新!这句话的意思是说,不要执行“更新”这个动作。把更新这个动作改为插入。这样就可以实现批量更新的效果了。具体来说,就是把数据批量插入到一个新的MongoDB集合中,再把原来的集合删除,最后将新的集合改为原来集合的名字。把更新操作改为插入操作,耗时约为逐条更新的十分之一。
二、能用Redis就不用MongoDB
在什么情况下可以使用Redis来代替MongoDB呢?举一个最常见的例子:判断重复。例如爬取百度贴吧,在帖子列表页可以爬到每个帖子的标题和详情页的网址。如果对某一个帖子有兴趣,就从详情页网址爬进去抓取这个帖子的详细信息。由于需要节省资源,提高抓取速度,因此决定每天只爬新增加的帖子,已经爬过的帖子就不再重复爬取。
解决这个问题,其实要实现的功能很简单。在保存数据的时候,把每个帖子的网址也保存到数据库中。爬虫在爬详情页之前,先去MongoDB中查看这个URL是否已经存在。如果已经存在就不爬详情页;如果不存在,就继续爬这个帖子的详情页。这种办法当然可以实现这个需求,但是由于在前面已经说了,频繁读/写MongoDB是非常浪费时间的,因此这种办法效率并不高。
为了提高效率,就需要引入Redis。由于Redis是基于内存的数据库,因此即使频繁对其读/写,对性能的影响也远远小于频繁读/写MongoDB。在Redis中创建一个集合“crawled_url”,爬虫在爬一个网址之前,先把这个网址sadd到这个集合中。如果返回为1,那么表示这个网址之前没有爬过,爬虫需要去爬取详情页。如果返回0,表示这个网址之前已经爬过了,就不需要再爬了。示例代码片段如下:
…
for url in url_list: #url_list为在贴吧列表页得到的每一个帖子的详情页网址列表
if client.sadd('crawled_url', url) == 1:
crawl(url)
…
--------------------------------------
版权声明:本文为【猿小猴子】博主的文章,同步在【猿小猴子】WeChat平台,转载请附上原文出处链接及本声明。
--------------------------------------