MongoDB:PyMongo百万级数据去重
场景描述
- 在Python爬虫中经常使用MongoDB数据库来存储爬虫爬取的结果,于是乎就有了一个问题:百万级的MongoDB数据如何去重?
- 常见的思路便是在数据入库的时候检查该数据在数据库中是否已经存在,如果存在则忽略(效率高点)或者覆盖,这样做在数据量比较少的时候是适用的,但是在数据量比较大的时候(百万级及以上)这样做往往是效率非常低的!而且如果是已有的未去重的百万级数据库又该怎么办呢?
- 也可以使用distinct语句进行去重,但是还是那个问题,distinct语句并不适用于百万级数据,甚至在数据量大的时候就会报错:
distinct too big, 16mb cap
。这时候就需要使用 aggregate 聚合框架来去重了!
实现方法
import pymongo
from tqdm import tqdmclass CleanData:def __init__(self):"""连接数据库"""self.client = pymongo.MongoClient(host='host', port=27017)self.db = self.client.crawlab_masterdef clean(self):"""清理gkld_useful_data集合中重复的内容"""# 查询重复数据if results := self.db.gkld_useful_data.aggregate([# 对特定字段分组{'$group': {'_id': {'radar_range': '$radar_range','notice_tags': '$notice_tags','notice_title': '$notice_title','notice_content': '$notice_content',},# 统计出现的次数'count': {'$sum': 1}}},# 过滤分组的字段,选择显示大于一条的数据{'$match': {'count': {'$gt': 1}}}# allowDiskUse=True:避免出现超出内存阈值的异常], allowDiskUse=True):for item in tqdm(list(results), ncols=100, desc='去重进度'):count = item['count'] # count默认为整型(int)radar_range = item['_id']['radar_range']notice_tags = item['_id']['notice_tags']notice_title = item['_id']['notice_title']notice_content = item['_id']['notice_content']for i in range(1, count):# 仅留一条数据,删除重复数据self.db.gkld_useful_data.delete_one({'radar_range': radar_range,'notice_tags': notice_tags,'notice_title': notice_title,'notice_content': notice_content})if __name__ == '__main__':clean = CleanData()clean.clean()
解释说明
$group
:用于根据给定的字段进行分组'$sum': 1
:满足分组条件的数据每出现一次,count的值则加1count
:用来统计出现的次数$match
:{'count': {'$gt': 1}}
选择显示出现次数大于一条的数据(大于一条说明数据是重复数据)allowDiskUse=True
:避免出现超出内存阈值的异常tqdm
:一个专门生成进度条的工具包,参考链接- for item in tqdm(
list(results)
):会显示百分比进度条 - for item in tqdm(
results
):不会显示百分比进度条 - 当数据库中无重复数据时
list(results)
和results
效果相同,都没有百分比进度条
✨新方案✨
简介
- 使用以上方案确实可以解决百万级数据去重的问题,但是如果百万级数据中重复的数量比较大(如:十万+),而且MongoDB的配置也比较低,并且用于检测数据是否重复的字段又比较多,这时候使用以上方案进行去重的话,效率就会比较低。当然你可以通过在以上方案的基础上使用多线程或多进程的方式,用来提高以上方案的去重效率也是可以的。
- 不过,最好的方案还是在入库的时候解决数据重复的问题,当然对于已有的未去重的百万级数据库还是推荐通过以上方案进行去重,那么要如何做到在数据入库的时候快速且可靠的进行去重呢?
- 这里我采取的方式是通过将用来去重的特征字段拼接成一个字符串,然后对拼接后的字符串进行MD5加密,最后用MD5加密后生成的密文去替换MongoDB数据库自动生成的
_id
字段,这样在插入重复的数据时,因为数据库里已有了相同的_id
字段对应的数据,所以这个时候插入数据的程序会报异常,我们可以通过捕获这个异常来进行对重复数据的处理(如:覆盖或忽略)。 - 这样做的好处是避免了在插入数据时对数据库中的数据通过
find
等语法进行数据是否重复的查询,你只需要直接插入数据,不用事先检查数据库中的数据是否已存在重复数据,仅仅只需要捕获插入数据时的异常,并且在异常中采取对应的重复数据去重策略即可!
代码示例
- spider
from hashlib import md5from NewScheme.items import NewschemeItem# 使用MD5加密生成_id字段 encrypt = province_name + city_name + county_name + exam_type_name + info_type_name + notice_title + update_time md = md5(f'{encrypt}'.encode('utf8')) _id = md.hexdigest()[8:-8] # 保存数据 item = NewschemeItem() item['_id'] = _id item['province_name'] = province_name item['city_name'] = city_name item['county_name'] = county_name item['county_show'] = county_show item['exam_type_name'] = exam_type_name item['info_type_name'] = info_type_name item['notice_title'] = notice_title item['update_time'] = update_time item['notice_source'] = notice_source item['job_info'] = job_info item['job_people_num'] = int(job_people_num) item['job_position_num'] = int(job_position_num) item['job_start_time'] = job_start_time item['job_end_time'] = job_end_time item['notice_content'] = notice_content item['attachment_info'] = attachment_info yield item
这里因为我项目的要求,
_id
字段为16位,如果项目没有特别要求的话可以用默认的32位 - pipelines
# 忽略策略 try:self.collection.insert_one(dict(item)) except Exception as e:# 数据重复则忽略print(f'{item.get("_id")}已存在 - ignore') else:print(item.get('notice_title'))# 覆盖策略 try:self.collection.insert_one(dict(item)) except Exception as e:# 数据重复则覆盖_id = item.get("_id")print(f'{_id}已存在 - cover')# 删除旧数据self.collection.delete_one({'_id': _id})# 插入新数据self.collection.insert_one(dict(item)) else:print(item.get('notice_title'))
MongoDB:PyMongo百万级数据去重相关推荐
- mongodb 百万_关于mongodb 百万级数据 去重 查询优化的问题
现有一个企业表company,里面有100万的数据,对应一个员工表user,每个企业ID company_id对应0到多条user数据,员工表中有300万条数据:具体结构如下: db.company. ...
- redis作为MongoDB的缓存在线实时去重
一.背景原理 部分内容参考基于python的MySQL和redis数据同步实现(redis做缓存) 这个说的真的很清楚,但缺少实际的案例 1.MongDB 数据库 MongDB是一种非关系型数据库,主 ...
- Mysql - 百万级数据查询优化笔记 (PHP Script) ②
Mysql - 百万级数据查询优化笔记 (PHP Script) ② 说明:要处理的是在一个脚本中统计的年度账单,和上一篇的处理思路完全 不同,先把全量取出,再按字典形式拼接,10w条数据只需要668 ...
- php mysql百万级数据_php+mysql百万级数据怎么排序_PHP教程
php+mysql百万级数据如何排序? php+mysql百万级数据分页.因涉及多表多条件联合查询.谁能帮忙优化下面的查询语句. if(empty($wheresql)){ $wheresql=&qu ...
- Mysql 百万级数据优化资料
Mysql 百万级数据优化资料 一.我们可以且应该优化什么? 硬件 操作系统/软件库 SQL服务器(设置和查询) 应用编程接口(API) 应用程序 -------------------------- ...
- mysql深度解析_百万级数据下的mysql深度解析
mysql 作为一款非常优秀的免费数据库被广泛的使用,平时我们开发的项目数据过百万的时候不多.最近花了大量的时间来深入的研究mysql百万级数据情况下的优化. 遇到了很多问题并解决了他们,特此分享给大 ...
- phython在file同时写入两个_轻松支撑百万级数据点写入 京东智联云时序数据库HoraeDB架构解密...
本文将通过对时序数据的基本概念.应用场景以及京东智联云时序数据库HoraeDB的介绍,为大家揭秘HoraeDB的核心技术架构和解决方案. 首先我们来了解下时序数据库的基本概念.时序数据库全称时间序列数 ...
- php直接导出csv,PHP百万级数据导出方案(生成器直接输出单个CSV)
概述: 之前写过一篇文章叫PHP百万级数据导出方案(多csv文件压缩),发现很多朋友都很感兴趣,但是当时用的方法比较不方便,可能不太符合很多人的需求.后来想了一下如何优化时,了解到能用生成器来处理内存 ...
- 实战手记:让百万级数据瞬间导入SQL Server
实战手记:让百万级数据瞬间导入SQL Server 想必每个DBA都喜欢挑战数据导入时间,用时越短工作效率越高,也充分的能够证明自己的实力.实际工作中有时候需要把大量数据导入数据库,然后用于各种程序计 ...
最新文章
- imopen和bwmorph_MATLAB图像处理中的应用
- 老年手机英文改中文_这些手游大作你都玩了吗?手机游戏推荐
- POJ 3174 暴力枚举
- vivo 互联网业务就近路由技术实战
- 波形的转换与信号处理
- 毒鸡汤真相,你被骗了多少年?
- 【Python】Python获取命令行參数
- 在线 Sql Server 服务无法启动的解决办法
- IP地址的正则表达式
- 第二十一期:拜托!面试不要再问我Spring Cloud底层原理
- SVN提交时出现locked错误解决办法
- 报错,Error starting ApplicationContext. To display the conditions report re-run your application with
- 这种div高度自适应确定你知道吗?
- 又遇到jqGrid在chrome下宽度不正常有滚动条
- 门店销售系统开发实例
- web前端开发规范,前端架构从入门到微前端百度网盘
- linux抓包UDP流量
- 基于PHP的在线聊天室(网页版)
- 手把手教你写一个微信小程序(前端+Java后端)
- Vue H5 History 部署IIS上404问题