前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >循环查询数据的性能问题及优化

循环查询数据的性能问题及优化

作者头像
王知无-import_bigdata
发布2020-06-06 16:20:44
3.2K0
发布2020-06-06 16:20:44
举报

糟糕的代码,对代码维护、性能、团队协作都会造成负面影响,所以,先设计再实现,谋而后动。

这里的循环查询,指的是在一个for循环中,不断访问数据库来查询数据。在刚接手公司数据报表系统时,发现有很多每日报告跑的出奇的慢,通过一番诊断后,发现主要来自两个方面的因素:一是需要对数据库的某些字段建立和优化索引,二是存在了很多糟糕的代码,这些代码在一个循环中不断的访问数据库,查询数据。

本文将摘取其中的三个例子来说明如何避免循环查询带来的性能问题,涉及常用的三种数据存储:MySQL,MongoDB和Redis。

1. 使用IN查询替换for循环

  • 优化前代码(简化版,以MySQL为例):
代码语言:javascript
复制
sql = 'SELECT A.real_name, A.phone, A.gender FROM tb_user AS A INNER JOIN tb_trade AS B on A.id=B.user_id WHERE B.id=%s;'for id in trade_ids:    user = db_mysql.find(sql, [id])
    # TODO: do some work

这个代码的本意是要查询每个交易的购买用户的信息,和其他信息拼接起来,然后写入EXCEL文件。这种写法,简单明了,只要按照逻辑来写就好了,然而随着交易越来越多,这段代码会跑的越来越慢,即使建立了索引,但是却无法避免每次的数据库访问开销。

优化后代码:

代码语言:javascript
复制
sql = 'SELECT A.real_name, A.phone, A.gender, B.id FROM tb_user AS A INNER JOIN tb_trade AS B on A.id=B.user_id WHERE B.id IN (%s);'
place_holders = ','.join(map(lambda x: '%s', id_list))  users = db_mysql.findAll(sql % place_holders, [trade_ids])
for id in users:    # TODO: do some work

使用IN替换掉for循环,一次查询拿到所有的数据,然后在for循环中取处理业务逻辑。该方法在MySQL与Mongo中均可以使用,只是语法不同而已。

2. 使用聚合查询替换for循环

优化前代码(简化版,以MongoDB为例):

代码语言:javascript
复制
avaliable_companies = []condition = {  'is_active': True,  'create_time': {'$lt': datatime.now()},  'suspended': False}
for company in companies:    condition['company'] = company['_id']  job = db_mongo.job.find_one(condition)  if job:      avaliable_companies.append(job)
count = len(avaliable_companies)

这段代码的本意是要查询截止到当前时间,生成的job记录是来自哪几家company。同样的,随着数据量的增加,这段代码会跑的越来越慢。

代码语言:javascript
复制
pipeline = [  {'$match': {    'is_active': True,    'create_time': {'$lt': datatime.now()},    'suspended': False,    'company': {'$in': map(lambda x: x['_id'], companies)}  }},  {'$group': {'_id': 'company'}}]
agg_result = db_mongo.job.aggregate(pipeline)
count = len(list(agg_result))

使用聚合可以一次查询出结果,当然,这里也可以通过IN查询来做,同样可以提高性能。

3. 使用pipeline来查询redis

Redis通常用来做数据缓存,降低数据库的命中率,从而提供并发性能。然而,如果使用不当,你会发现虽然使用了缓存,但是时间查询效率并没特别大的提升。

  • 优化前代码(简化版):
代码语言:javascript
复制
redis_cli = get_redis()for id in user_ids:    result = redis_cli.get('user_last_active_time:%d' % id)

这个代码本意是要查询一组用户的最近一次活跃时间,这些活跃时间都缓存在Redis中,但是这个代码,如果user_ids的列表很长,就会发现这个缓存查询很慢,因为每次访问redis都需要建立一次IO请求。

  • 优化后代码:
代码语言:javascript
复制
redis_cli = get_redis()pipeline = redis_cli.pipeline(transaction=False)for d in user_ids:    pipeline.get('user_last_active_time:%d' % id)active_time_list = pipeline.execute()

使用Redis的pipeline来一次获取所有的数据,这么做会比上面的快几十倍,在数据量大的情况下。

上面通过三个实例来阐述循环查询对性能的影响和优化的方法,写这篇博客的目的并不仅仅要介绍这些技巧方法,因为技巧方法远不止这些,而是想借此传达一个观点:编程,应该设计先于写代码。虽然都是实现同样的逻辑功能,但是如果没有进行一番设计和思考,必然会写出一些糟糕的代码,其会对代码维护、性能、团队协作都会造成负面影响。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-06-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 大数据技术与架构 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 使用IN查询替换for循环
  • 2. 使用聚合查询替换for循环
  • 优化前代码(简化版,以MongoDB为例):
  • 3. 使用pipeline来查询redis
相关产品与服务
云数据库 SQL Server
腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档