django orm性能优化
参考:
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()后又只调用结果集的一部分数据时,要特别注意性能问题。
django orm性能优化的更多相关文章
- Django ORM性能优化 和 图片验证码
一,ORM性能相关 1. 关联外键, 只拿一次数据 all_users = models.User.objects.all().values('name', 'age', 'role__name') ...
- python django ORM 性能优化 select_related & prefetch_related
q = models.UserInfo.objects.all() select * from userinfo select * from userinfo inner join usertype ...
- Django ORM性能优化之count和len方法的选择(非常详细推荐干货)
接下来我将从源码层面分情况和应用分析我们在计算queryset数据集时是用orm的count函数计算长度还是用len函数计算数据集长度. 首先,我们知道ORM查询queryset数据集是惰性查询的,只 ...
- Django的性能优化
Django的性能优化 一,利用标准数据库优化技术 传统数据库优化技术博大精深,不同的数据库有不同的优化技巧,但重心还是有规则的.在这里算是题外话,挑两点通用的说说: 索引,给关键的字段添加索引, ...
- Django之缓存+序列化+信号+ORM性能优化+验证码
缓存 由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加 明显,最简单解决方式是使用:缓存,缓存将一个某个views的返回值保存至内存或者memcach ...
- Django数据库性能优化之 - 使用Python集合操作
前言 最近有个新需求: 人员基础信息(记作人员A),10w 某种类型的人员信息(记作人员B),1000 要求在后台上(Django Admin)分别展示:已录入A的人员B列表.未录入的人员B列表 团队 ...
- 优化Django ORM中的性能问题(含prefetch_related 和 select_related)
Django是个好工具,使用的很广泛. 在应用比较小的时候,会觉得它很快,但是随着应用复杂和壮大,就显得没那么高效了.当你了解所用的Web框架一些内部机制之后,才能写成比较高效的代码. 怎么查问题 W ...
- Django的model查询操作 与 查询性能优化
Django的model查询操作 与 查询性能优化 1 如何 在做ORM查询时 查看SQl的执行情况 (1) 最底层的 django.db.connection 在 django shell 中使用 ...
- Django之缓存、信号和图片验证码、ORM性能
一. 缓存 1. 介绍 缓存通俗来说:就是把数据先保存在某个地方,下次再读取的时候不用再去原位置读取,让访问速度更快. 缓存机制图解 2.Django中提供了6种缓存方式 1. 开发调试 2. 内存 ...
- C# 性能优化 之 秒表 Stopwatch。 Dapper一个和petapoco差不多的轻量级ORM框架
Sweet小马 小马同学的编程日记. C# 性能优化 之 秒表 Stopwatch. 生词解释:Diagnostics[,daɪəg'nɑstɪks] n.诊断学 using System.Diagn ...
随机推荐
- go-浅学设计模式随记
责任链模式 组成:由多个处理器及处理器处理标志串联组成 作用:常用于处理流水线事务,利用多个处理器对同一个对象进行处理,可以利用各处理器开关 场景:常见逻辑层处理逻辑:获取参数.fetch数据.逻辑处 ...
- Chrome 中的 JavaScript 断点设置和调试技巧--转自hanguokai.com
你是怎么调试 JavaScript 程序的?最原始的方法是用 alert() 在页面上打印内容,稍微改进一点的方法是用 console.log() 在 JavaScript 控制台上输出内容.嗯~,用 ...
- c#遍历一个对象的字段信息
c#遍历对象字段 场景:有一个对象作为导出word段落的数据.每一个字段就代表一个段落,可以对相应段落数据设置样式(字体.颜色.加粗--) 参考文献:(12条消息) C#获取实体类字段信息Proper ...
- Java 04-基础 数据类型转换 自动类型转换+强制类型转换
1.数据类型自动转换 规则1:如果一个操作数为double型,则整个表达式提示至double型 规则2:满足自动类型转换条件, 两种类型要兼容,数值类型(整数和浮点)相互兼容 目标类型取值大于 ...
- Pr视频软件主要知识点
1,选中某一个面板,点击"Tab键上的 '波浪号' 键"即可将这个面板全屏展示 . 2,新建序列项目:自定义,25帧/s,方形像素,无场(逐行扫描). 3,序列面板素材自动缩放适 ...
- 781. 森林中的兔子 (Medium)
问题描述 781. 森林中的兔子 (Medium) 森林中有未知数量的兔子.提问其中若干只兔子 "还有多少只兔子与你(指被提问的兔子)颜色相同?" ,将答案收集到一个整数数组 an ...
- Word10 个人简历office真题
1.新键Microsoft Word 文档,命名为Word,再打开Word文档,选择[布局],打开[页面设置]右下角的箭头,弹出[页面设置]的窗口,根据题目要求调整[页边距]. 2.根据案例题目二 ...
- LaTex【六】表格排版—表格标题位置
LaTex中表格排版--表格描述位置调整 LaTex模板大多默认将表格描述置于表格下方,可通过修改 \caption 的位置调整. 1. 位于表格下方(默认) \begin{table}[h] \be ...
- JS学习-异步JS
异步JS setTimeout() 我们希望传递给setTimeout()中运行的函数的任何参数,都必须作为列表末尾的附加参数传递给它. function sayHi(who) { alert('He ...
- Java数组之冒泡排序【重点】
冒泡排序 冒泡排序是最为出名的排序算法之一,总共有八大排序! 冒泡的代码还是相当简单的,两层循环,外层冒泡轮数,里层依次比较. 我们看到嵌套循环,应该立马就可以得出这个算法的时间复杂度为O(n2). ...