ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

django orm性能优化

2022-07-10 05:01:24  阅读:224  来源: 互联网

标签:优化 sync django collect orm date hostusagemonitor data id


参考:
django 分页查询大表,很慢
面试小知识:MySQL索引相关
MySQL 用 limit 为什么会影响性能?

前言

orm性能优化是一件很重要的事,一般万条以上的数据都需要优化处理了。

这次项目开发,接口测试的时候发现一个接口居然要数秒到一分钟,我都等的不耐烦了,客户用了肯定抓狂。于是开始思考怎么优化,这里记录一下。

优化方法

1、给字段加索引,查询速度从15秒减少到0.06秒。参考这篇文章django模型字段参数db_index添加索引

2、order_by()使用优化,对于数据量比较大的表,直接使用order_by()会很大程度降低性能。

order_by()排序优化

今天主要来看看order_by()优化

在我之前的代码中order_by()的对象是filter多个条件筛选过的少量数据,故表现不明显,在使用索引过后性能就很快。

看看下面这份代码,因为没有日期筛选所以数据量较大,仅仅对collect_date索引已经不能明显提高性能,实测循环一次大概1-2秒,HostUsageMonitor表数据量级是80w条

    for metric_name in data_source_const.METRIC_NEED_STAT:
        # 获取最新采集日期
        monitor_objs = (
            HostUsageMonitor.objects.filter(email=email, metric_name=metric_name).order_by("-collect_date")
        )
        if not monitor_objs.exists():
            resource_usage_data[metric_name] = 0
            continue
        latest_collect_date = monitor_objs[0].collect_date
        # 获取最新日期下的监控数据
        ...

这里讲一下优化的过程

先打断点看看monitor_objs.query或者修改配置让django打印sql(后者效果更好,配置参考django - 打印执行的sql

1、直接用order_by(),2.321秒

HostUsageMonitor.objects.filter(email="110@qq.com", metric_name="mem_util").order_by("-collect_date")
latest_collect_date = monitor_objs[0].collect_date
(2.321) SELECT `data_sync_hostusagemonitor`.`id`, `data_sync_hostusagemonitor`.`server_id`, `data_sync_hostusagemonitor`.`metric_name`, `data_sync_hostusagemonitor`.`collect_date`, `data_sync_hostusagemonitor`.`average`, `data_sync_hostusagemonitor`.`unit`, `data_sync_hostusagemonitor`.`email`, `data_sync_hostusagemonitor`.`region_id` 
FROM `data_sync_hostusagemonitor` 
WHERE (`data_sync_hostusagemonitor`.`email` = '110@qq.com' AND `data_sync_hostusagemonitor`.`metric_name` = 'mem_util') 
ORDER BY `data_sync_hostusagemonitor`.`collect_date` DESC  LIMIT 1; 
args=('110@qq.com', 'mem_util')

2、用only()只加载collect_date,3.783秒

HostUsageMonitor.objects.filter(email="110@qq.com", metric_name="mem_util").only("collect_date").order_by("-collect_date")
latest_collect_date = monitor_objs[0].collect_date
(3.783) SELECT `data_sync_hostusagemonitor`.`id`, `data_sync_hostusagemonitor`.`collect_date` 
FROM `data_sync_hostusagemonitor` 
WHERE (`data_sync_hostusagemonitor`.`email` = '110@qq.com' AND `data_sync_hostusagemonitor`.`metric_name` = 'mem_util') 
ORDER BY `data_sync_hostusagemonitor`.`collect_date` DESC  LIMIT 1; 
args=('110@qq.com', 'mem_util')

3、用only()只加载id,根据查出的id列表做二次查询,0.009秒

monitor_objs_only_id = (
            HostUsageMonitor.objects.filter(email=email, metric_name=metric_name).only("id").order_by("-collect_date")
        )
monitor_objs = HostUsageMonitor.objects.filter(id__in=[monitor.id for monitor in monitor_objs_only_id])
latest_collect_date = monitor_objs[0].collect_date
(0.007) SELECT `data_sync_hostusagemonitor`.`id` 
FROM `data_sync_hostusagemonitor` 
WHERE (`data_sync_hostusagemonitor`.`email` = '110@qq.com' AND `data_sync_hostusagemonitor`.`metric_name` = 'mem_util') 
ORDER BY `data_sync_hostusagemonitor`.`collect_date` DESC; 
args=('110@qq.com', 'mem_util')

(0.002) SELECT `data_sync_hostusagemonitor`.`id`, `data_sync_hostusagemonitor`.`server_id`, `data_sync_hostusagemonitor`.`metric_name`, `data_sync_hostusagemonitor`.`collect_date`, `data_sync_hostusagemonitor`.`average`, `data_sync_hostusagemonitor`.`unit`, `data_sync_hostusagemonitor`.`email`, `data_sync_hostusagemonitor`.`region_id` 
FROM `data_sync_hostusagemonitor` 
WHERE `data_sync_hostusagemonitor`.`id` IN (10, 90, 170, 226, 282, 338, 490, 1002, 1082, 1138, 1194, 1250, 1306, 1362, 1586, 1642, 1842, 1970, 2026, 2082, 2138, 2194, 2250, 2306, 2362, 2418, 2498, 2554, 2610, 2666, 2722, 2778, 2834, 2890, 2970, 3314, 3370, 3426, 3482, 3538, 3594, 3746, 3826, 3882, 4124, 4204, 4284, 4359, 4401, 4492, 4565, 4614, 4734, 4776, 4818, 5129, 5401, 5457, 5535, 5597, 5653, 5709, 5765, 5821, 5877, 5933, 5989, 6045, 6101, 6181, 6237, 6293, 9, 89, 169, 225, 281, 337, 489, 1001, 1081, 1137, 1193, 1249, 1305, 1361, 1585, 1641, 1841, 1969, 2025, 2081, 2137, 2193, 2249, 2305, 2361, 2417, 2497, 2553, 2609, 2665, 2721, 2777, 2833, 2889, 2969, 3313, 3369, 3425, 3481, 3537, 3593, 3745, 3825, 3881, 4123, 4203, 4283, 4358, 4400, 4491, 4564, 4613, 4733, 4775, 4817, 5128, 5400, 5456, 5534, 5596, 5652, 5708, 5764, 5820, 5876, 5932, 5988, 6044, 6100, 6180, 6236, 6292)  
LIMIT 1; 
args=(10, 90, 170, 226, 282, 338, 490, 1002, 1082, 1138, 1194, 1250, 1306, 1362, 1586, 1642, 1842, 1970, 2026, 2082, 2138, 2194, 2250, 2306, 2362, 2418, 2498, 2554, 2610, 2666, 2722, 2778, 2834, 2890, 2970, 3314, 3370, 3426, 3482, 3538, 3594, 3746, 3826, 3882, 4124, 4204, 4284, 4359, 4401, 4492, 4565, 4614, 4734, 4776, 4818, 5129, 5401, 5457, 5535, 5597, 5653, 5709, 5765, 5821, 5877, 5933, 5989, 6045, 6101, 6181, 6237, 6293, 9, 89, 169, 225, 281, 337, 489, 1001, 1081, 1137, 1193, 1249, 1305, 1361, 1585, 1641, 1841, 1969, 2025, 2081, 2137, 2193, 2249, 2305, 2361, 2417, 2497, 2553, 2609, 2665, 2721, 2777, 2833, 2889, 2969, 3313, 3369, 3425, 3481, 3537, 3593, 3745, 3825, 3881, 4123, 4203, 4283, 4358, 4400, 4491, 4564, 4613, 4733, 4775, 4817, 5128, 5400, 5456, 5534, 5596, 5652, 5708, 5764, 5820, 5876, 5932, 5988, 6044, 6100, 6180, 6236, 6292)

观察以上3种orm执行的sql,发现关键在于limit 1,这个limit严重影响了我们的查询速度。

那么为什么limit减少了查询结果,反而加慢了查询效率呢?查阅mysql官方文档可知,在使用order的同时使用limit,会对所有数据进行扫描重排,所以效率很差。

在1和2中orm的order_by()生成的sql都带有limit 1,因为这个sql是在我们monitor_objs[0].collect_date调用查询结果时才组合并查询的。

在3中因为我们拆分成了2次查询。第一条sql的所有查询结果都会被第2条用到,所以不会有limit条件限制;但第2条查询结果我们也会调用monitor_objs[0]也会有个limit 1,这里不会降低查询效率的原因有2个:

  • order by和limit一起出现会对所有数据进行扫描重排,第2条查询没有order by,实测不会有扫描重排(1、2中sql去掉order_by查询效率明显提高)。
  • 假如有重排,扫描重排的时候影响性能的主要原因还是非主键字段匹配(WHERE email = "xxx")(实测用email和id匹配速度天差地别),这里第2条sql主要是主键id的匹配,而根据主键查询是非常快的。

所以在使用order_by()后又只调用结果集的一部分数据时,要特别注意性能问题。

标签:优化,sync,django,collect,orm,date,hostusagemonitor,data,id
来源: https://www.cnblogs.com/libaiyun/p/16462471.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有