循环查询数据的性能问题及优化
来源:互联网 发布:iscroll.js 官网下载 编辑:程序博客网 时间:2024/05/20 04:49
写在前言:糟糕的代码,对代码维护、性能、团队协作都会造成负面影响,所以,先设计再实现,谋而后动。
这里的循环查询,指的是在一个for循环中,不断访问数据库来查询数据。在刚接手公司数据报表系统时,发现有很多每日报告跑的出奇的慢,通过一番诊断后,发现主要来自两个方面的因素:一是需要对数据库的某些字段建立和优化索引,二是存在了很多糟糕的代码,这些代码在一个循环中不断的访问数据库,查询数据。本文接下来将摘取其中的三个例子来说明如何避免循环查询带来的性能问题,涉及常用的三种数据存储:MySQL,MongoDB和Redis。
1. 使用IN查询替换for循环
- 优化前代码(简化版,以MySQL为例):
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文件。这种写法,简单明了,只要按照逻辑来写就好了,然而随着交易越来越多,这段代码会跑的越来越慢,即使建立了索引,但是却无法避免每次的数据库访问开销。
- 优化后代码:
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为例):
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。同样的,随着数据量的增加,这段代码会跑的越来越慢。
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通常用来做数据缓存,降低数据库的命中率,从而提供并发性能。然而,如果使用不当,你会发现虽然使用了缓存,但是时间查询效率并没特别大的提升。
- 优化前代码(简化版):
redis_cli = get_redis()for id in user_ids: result = redis_cli.get('user_last_active_time:%d' % id)这个代码本意是要查询一组用户的最近一次活跃时间,这些活跃时间都缓存在Redis中,但是这个代码,如果user_ids的列表很长,就会发现这个缓存查询很慢,因为每次访问redis都需要建立一次IO请求。
-- 优化后代码:
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来一次获取所有的数据,这么做会比上面的快几十倍,在数据量大的情况下。
上面通过三个实例来阐述循环查询对性能的影响和优化的方法,写这篇博客的目的并不仅仅要介绍这些技巧方法,因为技巧方法远不止这些,而是想借此传达一个观点:编程,应该设计先于写代码。虽然都是实现同样的逻辑功能,但是如果没有进行一番设计和思考,必然会写出一些糟糕的代码,其会对代码维护、性能、团队协作都会造成负面影响。
(全文完,本文地址:http://blog.csdn.net/zwgdft/article/details/52822455)
Bruce, 2016/11/13
Bruce, 2016/11/13
1 0
- 循环查询数据的性能问题及优化
- 分享SQL海量数据查询优化性能的总结
- Python循环的性能优化
- oracle11g分页优化,rowid和rownum的性能比较,解决越往后数据查询越慢的问题
- 反射及元数据的性能问题
- Oracle查询性能优化(面试题:数据库查询优化也是常问的问题)
- 数据查询的优化
- SQL查询安全性及性能优化
- SQL查询安全性及性能优化
- mysql查询参数配置及优化性能
- MySQL查询的性能优化
- MySQL查询的性能优化
- 优化报表查询的性能
- MySQL查询的性能优化
- MySQL查询的性能优化
- MySQL查询的性能优化
- MySQL查询的性能优化
- MySQL查询的性能优化
- 日常训练20161014 跟踪
- unity C# xml创建,获取,修改,删除(android同样适用)
- ubuntu14.04配置安装pycharm professional
- 解决UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 108: ordinal not in range(128
- 八大排序--基数排序
- 循环查询数据的性能问题及优化
- 假设一个重男轻女的国家,每个家庭都想生男孩,如果生了一个女孩,就会继续生,直到生下男孩为止,平均每户有多少个女孩?这个国家的男女比例是多少?
- 在linux下实现自己的ls -l
- Java十六进制字符串转byte和字符转字节
- JavaScript事件模型
- 网络通信解决了什么问题
- 大数据面试题
- Hibernate——<many-to-one/>
- How to Collect Bne Log Files for GL Integrators