MongoDB数据库查询性能提高40倍

本文涉及的产品
云原生多模数据库 Lindorm,多引擎 多规格 0-4节点
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云数据库 MongoDB,通用型 2核4GB
简介: MongoDB数据库查询性能提高40倍

MongoDB数据库查询性能提高40倍的经历分享

大家在使用 MongoDB 的时候有没有碰到过性能问题呢?下面这篇文章主要给大家分享了MongoDB数据库查询性能提高40倍的经历,需要的朋友可以参考借鉴,下面来一起看看吧。

前言

数据库性能对软件整体性能有着至关重要的影响,本文给大家分享了一次MongoDB数据库查询性能提高40倍的经历,感兴趣的朋友们可以参考学习。

背景说明

1、数据库:MongoDB

2、数据集:

    • A:字段数不定,这里主要用到的两个UID和Date
    • B:三个字段,UID、Date、Actions。其中Actions字段是包含260元素JSON数组,每个JSON对象有6个字段。共有数据800万条左右。

    3、业务场景:求平均数

      • 通过组合条件从A数据表查询出(UID,Date)列表,最多可能包含数万条记录;
      • 然后用第1步的结果从B中查询出对应的数据
      • 用第2步结果去Actions的某个固定位置的元素的进行计算

      进化过程

      在这里使用Python演示

      最直接想到的方法

      根据上面的业务场景描述,最容易想到的解决方法就是

      from pymongo import MongoClient
      # 连接数据库
      db = MongoClient('mongodb://127.0.0.1:27017')['my_db']
      # 简化的查询数据集A的条件
      filter = {...}
      # 查询Collection A
      a_cursor = db.a.find(_filter)
      a_docs = [x for x in a_cursor]
      # 变量的初始定义
      count = 0
      total = 0
      # 加入需要用到的元素为第21个
      index = 20
      # 查询Collection B,同时做累加
      for a_doc in a _docs:
       b_doc = db.b.find_one({'uid':a_doc['uid'], 'date': a_doc['date']})
       # 只有能查到相应的结果时,才可以
       if b_doc is not None:
       total += b_doc['actions'][20]['number']
       count += 1
       # 求平均数
       if count > 0 :
       avg = total/count

      image.gif

      实现难度当然是最低的,可是整个任务在第一步只有1万条左右的返回时,消耗的时间竟然达到了惊人38秒。当然这是已经加了索引的结果,否则可能都无法得到结果了。

      减少查询次数

      瓶颈显而易见,在循环中查询Collection B,增加了网络开销,自然也就增加时间,如果一次查询出所有结果,自然会大大提高效率。也就是说,我要把第一步的结果作为条件一次性传递,做一个$in操作。可是怎么才能做到呢?如果在uid和date上分别做$in操作,那么返回的结果就会是二者单独做$操作的合集,很显然这和要求是不符的。

      经过上面的分析,似乎进入了死胡同。其实答案也基本显现了,需要有一个字段可以满足上面的要求,那么这个字段就是uid和date的合体,就命名为uid_date。uid_date是一个新字段,在B中并不存在,在使用之前需要将数据库现有的数据做一下处理。

      处理完毕改造程序:

      # 下面的只体现和本次修改相关的内容
      uid_date_list = []
      for a_doc in a_docs:
       uid_date_list.append(a_doc['uid'] + '_' + a_doc['date'])
      # 查询B
      b_cursor = db.b.find({'uid_date':{'$in':uid_date_list}})
      # 下面就是取出结果,求平均数
      ...

      image.gif

      这一番改造颇费时间,主要是前期的数据处理。代码改造完毕,执行下看看吧。

      可是,可是…… 45秒

      我做错了什么?!

      增加返回记录数

      我还是坚信上面的优化思路是对的,现在看看数据库能给一些什么线索吧。

      登录到数据库服务器,找到MongoDB的日志/data/mongodb/logs/mongod.log。仔细查找,发现在查询数据集B时有很多getMore命令。这就奇怪了,我是一次性查询,为什么还有getMore。

      赶紧查下官方的文档,然后发现了下面的内容:

      image.gif编辑

      batcSize参数指定了每次返回的个数,默认的101个。那看来这个应该是问题所在。找下pymongo的文档,也可以设置这个参数,那就设个大的吧10000。

      再次改造程序如下:

      # 增加batch_size
      b_cursor = db.b.find({'uid_date':{'$in': uid_date_list}}, batch_size=10000)

      image.gif

      这次总该可以了。

      嗯,好了一些,降到了20秒左右。可是,这离1秒只能还差距20倍呢。

      返回值减负

      当日不能放弃,继续通过日志查找线索,发现还是有很多getMore。通过各方查找,发现mongodb每次最多返回16M的记录,通过getMore日志的比对,发现的确如此。由于B中每条记录的过去庞大,每次只能几百条记录,因此要一次多返回,那就必须要减少每次返回的记录数。因为在计算时,只用了特定索引位置上的数据,所以只返回该条记录就可以了。

      最后的代码就不再写了,具体可以参考官方文档的实例

      相关实践学习
      MongoDB数据库入门
      MongoDB数据库入门实验。
      快速掌握 MongoDB 数据库
      本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。   相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
      相关文章
      |
      24天前
      |
      关系型数据库 MySQL 数据库
      轻松入门MySQL:精准查询,巧用WHERE与HAVING,数据库查询如虎添翼(7)
      轻松入门MySQL:精准查询,巧用WHERE与HAVING,数据库查询如虎添翼(7)
      |
      3天前
      |
      监控 NoSQL 测试技术
      MongoDB性能最佳实践:如何制定更有效的基准测试?
      感谢你与我们一起走过这段MongoDB性能最佳实践之旅,希望你能从中获取一些有用的信息
      1565 2
      |
      3天前
      |
      SQL Java 数据库连接
      Java从入门到精通:2.3.2数据库编程——了解SQL语言,编写基本查询语句
      Java从入门到精通:2.3.2数据库编程——了解SQL语言,编写基本查询语句
      |
      3天前
      |
      缓存 关系型数据库 MySQL
      MySQL数据库优化技巧:提升性能的关键策略
      索引是提高查询效率的关键。根据查询频率和条件,创建合适的索引能够加快查询速度。但要注意,过多的索引可能会增加写操作的开销,因此需要权衡。
      |
      4天前
      |
      NoSQL MongoDB 数据库
      MongoDB数据恢复—MongoDB数据库文件被破坏的数据恢复案例
      服务器数据恢复环境: 一台Windows Server操作系统服务器,服务器上部署MongoDB数据库。 MongoDB数据库故障&检测: 工作人员在未关闭MongoDB数据库服务的情况下,将数据库文件拷贝到其他分区。拷贝完成后将原MongoDB数据库所在分区进行了格式化操作,然后将数据库文件拷回原分区,重新启动MongoDB服务,服务无法启动。
      |
      8天前
      |
      NoSQL MongoDB Redis
      Python与NoSQL数据库(MongoDB、Redis等)面试问答
      【4月更文挑战第16天】本文探讨了Python与NoSQL数据库(如MongoDB、Redis)在面试中的常见问题,包括连接与操作数据库、错误处理、高级特性和缓存策略。重点介绍了使用`pymongo`和`redis`库进行CRUD操作、异常捕获以及数据一致性管理。通过理解这些问题、易错点及避免策略,并结合代码示例,开发者能在面试中展现其技术实力和实践经验。
      130 8
      Python与NoSQL数据库(MongoDB、Redis等)面试问答
      |
      8天前
      |
      SQL 关系型数据库 MySQL
      mysql 数据库查询 查询字段用逗号隔开 关联另一个表并显示
      mysql 数据库查询 查询字段用逗号隔开 关联另一个表并显示
      18 2
      |
      10天前
      |
      SQL 存储 Oracle
      关系型数据库查询数据的语句
      本文介绍了关系型数据库中的基本SQL查询语句,包括选择所有或特定列、带条件查询、排序、分组、过滤分组、表连接、限制记录数及子查询。SQL还支持窗口函数、存储过程等高级功能,是高效管理数据库的关键。建议深入学习SQL及相应数据库系统文档。
      9 2
      |
      11天前
      |
      SQL 缓存 Java
      Java数据库连接池:优化数据库访问性能
      【4月更文挑战第16天】本文探讨了Java数据库连接池的重要性和优势,它能减少延迟、提高效率并增强系统的可伸缩性和稳定性。通过选择如Apache DBCP、C3P0或HikariCP等连接池技术,并进行正确配置和集成,开发者可以优化数据库访问性能。此外,批处理、缓存、索引优化和SQL调整也是提升性能的有效手段。掌握数据库连接池的使用是优化Java企业级应用的关键。
      |
      19天前
      |
      JSON NoSQL MongoDB
      mongodb导出聚合查询的数据
      mongodb导出聚合查询的数据

      相关产品

    • 云数据库 MongoDB 版