在Google App Engine中使用hash和marshal持久化模块,快速判断数据库条目是否已经存在

本人在用GAE写爬虫的时候,遇到判断重复URL来防止重复抓取的问题,但是由于GAE数据库的限制(db.ListProperty每个记录不能保存超过5000个元素),经测试发现直接筛选数据库中的属性来判断是否有重复链接 会耗费大量CPU时间(甚至有时候会花费0.3秒来判断是否已经抓过某个网页),而利用建立哈希表来解决也没有多少改善.

经分析,根本问题在于hash颗粒度太小,读取数据库时使用GQL难免会比 (url in list)这样的python内置语句慢.经过查阅相关资料,用memcache可以解决.因为memcache是保存在内存中的,所以它的效率很高,且不随请求完毕而消亡.但是memcache有1M大小的限制,而且它并不是永久持久化的.可能因为服务器原因而丢失老数据.

有另一种解决方案就是把hash_list存到一个包装好的raw数据包中,put到数据库,下次使用再展开...于是我找到了marshal模块.它有将python所有内置数据类型和数据结构打包的功能.

注:另外一个python持久化的模块叫pickle 还有其C的底层实现:cPickle,与marshal模块不同的是,它可以记录自定义模块的数据.且展开时要导入原来的模块,若导入失败会引发一个异常.

 

利用python的marshal内置模块构建dump

import marshal from google.appengine.ext import db # 新建模型 class Dump(db.Model): dump_str = db.BlobProperty() #把dump存入list hash_list = [] for url in urls: hash_list.append(hash(url)) dump_str = marshal.dumps(hash_list) Dump( dump_str = dump_str ).put()

以下是判断是否存在url记录的方法:


url = "http://xxx.xxx.xxx/xx" dump = Dump.all().get() exist = False if dump: try: hash_list = marshal.loads(dump.dump_str) except: break # load error if hash(url) in hash_list: exist = True  

此方法用time测试 每个in 判断的操作需要时间小于0.001s

hash的list空间占用还是很小的,以每个数据记录为2M,每个hash数位64位(8字节)整形,打包不浪费空间,这样计算的话,能记录2*1024^3 /8 = 268435456 条hash数据

等空间不够时,还可以用zlib压缩一下,经过测试可以变成大小的1/3到1/4 ,证明marshal打包后 压缩空间还是蛮大的 毕竟全部是hash数组成的list 

再多的数据便要有更多的Dump项来实现了.

但是这个方法也不是很高效,因为每次还要抓一下数据库或者这个包,希望懂GAE的大侠指正或建议.

你可能感兴趣的:(数据库,google,list,python,gae,url)