记一次django内存异常排查及解决方法


Posted in Python onAugust 07, 2020

起因

Django 作为 Python著名的Web框架,相信很多人都在用,自己工作中也有项目项目在用,而在最近几天的使用中发现,部署Django程序的服务器出现了内存问题,现象就是运行一段时间之后,内存占用非常高,最终会把服务器的内存耗尽,对于Python项目出现内存问题,自己之前处理过一次,所以并没有第一次解决时的慌张,自己之前把解决方法也整理了:https://3water.com/article/151604.htm

但是事情似乎并没有我想的那么简单,自己尝试用之前的的方法tracemalloc库进行问题的排查,但是问题来了实际的项目中有快一百多个接口,怎么排查?难道一个一个接口进行测试排查,但是时间又比较紧急,可能又来不及了。对比上次自己解决是因为上次的项目比较简单,相对来说定位问题比较容易,那么这次怎么处理呢?

处理过程

一般Python项目其实是很少出现内存问题的,一般都是自己代码写的有问题导致的,而对于这次出现的问题,自己的排查思路(对于web 接口类型的项目):

  1. 先排查调用比较频繁的接口
  2. 然后排查数据汇总接口(查询比较复杂)
  3. 如果上述还没有查出来,再排查剩余的接口

在这次的问题排查中,自己大致也是按照这个思路进行的,在对调用频繁的接口进行排查时,并没有发现内存的异常,而出现内存的问题则是在数据汇总的相关接口上。

其实这种接口对于初级开发可能是容易出问题的地方,首先这种接口查询的数据相对其他接口会比较复杂,如果编码基础又不是特别好,可能就会在这些接口上出现bug.

而在这次的排查中,最终确定是在一个汇总数据的接口上,定位到问题处在了Django ORM 使用不当导致的。自己通过一个简单代码实例来说明:

class Student(models.Model):
 name = models.CharField(max_length=20)
 name2 = models.CharField(max_length=20)
 name3 = models.CharField(max_length=20)
 name4 = models.CharField(max_length=20)
 name5 = models.CharField(max_length=20)
 name6 = models.CharField(max_length=20)
 name7 = models.CharField(max_length=20)
 name8 = models.CharField(max_length=20)
 name9 = models.CharField(max_length=20)
 name10 = models.CharField(max_length=20)
 name11 = models.CharField(max_length=20)
 name12 = models.CharField(max_length=20)
 name13 = models.CharField(max_length=20)
 name14 = models.CharField(max_length=20)
 name15 = models.CharField(max_length=20)
 age = models.IntegerField(default=0)

正常情况,我们的表字段会比较多,这里就通过多个name来模拟,出现题的代码就出在关于这个表的接口上:

def index(request):
 studets = Student.objects.filter(age__gt=20)
 if studets:
  pass
 return HttpResponse("test memory")

为了让内存问题容易复现,我通过脚本向Student中插入了20000条数据,当然这里数据越多,问题越明显

通过一个测试脚本并发请求这个接口,观察内存情况,你会发现,内存会出现瞬间上涨的情况,并且如果你的数据越多,请求越多,你的内存可能会在一段时间居高不下,并且逐渐上涨。问题出在哪里了?

其实很简单,问题出在了代码中的if 判断那里,我们通过filter 查询返回的是QuerySet 类型的数据,而我们过滤之后的数据可能会存在非常多的时候,这个时候我们通过if 直接判断,自己的理解这个地方会将整个QuerySet加载到内存中,从而出现内存占用过高的问题,而如果并且这个时候这个接口的响应速度也是非常会变慢,而这个QuerySet 中的数据越多,内存占用越明显。

在Django的文档中其实做了说明

exists()¶
Returns True if the QuerySet contains any results, and False if not. This tries to perform the query in the simplest and fastest way possible, but it does execute nearly the same query as a normal QuerySet query.

exists() is useful for searches relating to both object membership in a QuerySet and to the existence of any objects in a QuerySet, particularly in the context of a large QuerySet.

The most efficient method of finding whether a model with a unique field (e.g. primary_key) is a member of a QuerySet is:

entry = Entry.objects.get(pk=123)
if some_queryset.filter(pk=entry.pk).exists():
 print("Entry contained in queryset")

Which will be faster than the following which requires evaluating and iterating through the entire queryset:

if entry in some_queryset:
 print("Entry contained in QuerySet")

And to find whether a queryset contains any items:

if some_queryset.exists():
 print("There is at least one object in some_queryset")

Which will be faster than:

if some_queryset:
 print("There is at least one object in some_queryset")

… but not by a large degree (hence needing a large queryset for efficiency gains).

Additionally, if a some_queryset has not yet been evaluated, but you know that it will be at some point, then using some_queryset.exists() will do more overall work (one query for the existence check plus an extra one to later retrieve the results) than using bool(some_queryset), which retrieves the results and then checks if any were returned.

所以对于我们的代码我们只需要把if 判断地方改成if not studets.exists() 就可以解决问题。

这是一个很小的知识点,但是如果使用不对,可能就会造成非常严重的内存问题。

总结

除了单元测试,还需要做大数据量测试,这次的问题如果在测试的时候做过一定数据量的测试,可能很早就能及时发现

问题

对于基础的库的使用要更加熟悉

排查问题的思路要明确,不然可能会无从下手

延伸阅读

  • https://docs.djangoproject.com/en/3.0/ref/models/querysets/
  • https://3water.com/article/151604.htm

到此这篇关于django内存异常排查及解决方法的文章就介绍到这了,更多相关django内存异常排查内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python selenium 父子、兄弟、相邻节点定位方式详解
Sep 15 Python
sublime text 3配置使用python操作方法
Jun 11 Python
Python实现树的先序、中序、后序排序算法示例
Jun 23 Python
django 基于中间件实现限制ip频繁访问过程详解
Jul 30 Python
win7下 python3.6 安装opencv 和 opencv-contrib-python解决 cv2.xfeatures2d.SIFT_create() 的问题
Oct 24 Python
python 基于dlib库的人脸检测的实现
Nov 08 Python
Django-rest-framework中过滤器的定制实例
Apr 01 Python
python实现学生管理系统开发
Jul 24 Python
python 检测nginx服务邮件报警的脚本
Dec 31 Python
python xlwt模块的使用解析
Apr 13 Python
浅谈tf.train.Saver()与tf.train.import_meta_graph的要点
May 26 Python
Django+Nginx+uWSGI 定时任务的实现方法
Jan 22 Python
python正则表达式 匹配反斜杠的操作方法
Aug 07 #Python
Pygame框架实现飞机大战
Aug 07 #Python
python爬取网易云音乐热歌榜实例代码
Aug 07 #Python
Python变量格式化输出实现原理解析
Aug 06 #Python
Python实现Canny及Hough算法代码实例解析
Aug 06 #Python
vscode调试django项目的方法
Aug 06 #Python
Python如何使用input函数获取输入
Aug 06 #Python
You might like
phpmyadmin的#1251问题
2006/11/25 PHP
服务器端解压缩zip的脚本
2006/12/22 PHP
PHP中空字符串介绍0、null、empty和false之间的关系
2012/09/25 PHP
php防注入,表单提交值转义的实现详解
2013/06/10 PHP
基于PHP的简单采集数据入库程序
2014/07/30 PHP
php获取从html表单传递数组的方法
2015/03/20 PHP
探究Laravel使用env函数读取环境变量为null的问题
2016/12/06 PHP
浅谈PHP错误类型及屏蔽方法
2017/05/27 PHP
PHP实现微信提现(企业付款到零钱)
2019/08/01 PHP
javascript在一段文字中的光标处插入其他文字
2007/08/26 Javascript
图像替换新技术 状态域方法
2010/01/28 Javascript
js更优雅的兼容
2010/08/12 Javascript
JavaScript实现的图像模糊算法代码分享
2014/04/22 Javascript
jQuery实现给input绑定回车事件的方法
2017/02/09 Javascript
单行 JS 实现移动端金钱格式的输入规则
2017/05/22 Javascript
JQuery 选择器、DOM节点操作练习实例
2017/09/28 jQuery
axios发送post请求springMVC接收不到参数的解决方法
2018/03/05 Javascript
Angularjs中date过滤器失效的问题及解决方法
2018/07/06 Javascript
layui 富文本图片上传接口与普通按钮 文件上传接口的例子
2019/09/23 Javascript
Python 连连看连接算法
2008/11/22 Python
linux系统使用python监控apache服务器进程脚本分享
2014/01/15 Python
python实现文件名批量替换和内容替换
2014/03/20 Python
python+opencv+caffe+摄像头做目标检测的实例代码
2018/08/03 Python
对python调用RPC接口的实例详解
2019/01/03 Python
python增加图像对比度的方法
2019/07/12 Python
Python将主机名转换为IP地址的方法
2019/08/14 Python
解决Python import docx出错DLL load failed的问题
2020/02/13 Python
windows上彻底删除jupyter notebook的实现
2020/04/13 Python
CSS3中设置3D变形的transform-style属性详解
2016/05/23 HTML / CSS
Evisu官方网站:日本牛仔品牌,时尚街头设计风格
2016/12/30 全球购物
荷兰领先的百货商店:De Bijenkorf
2018/10/17 全球购物
办公室人员先进事迹
2014/01/27 职场文书
学生喝酒检讨书
2014/02/06 职场文书
连锁酒店店长职责范本
2014/02/13 职场文书
导游词之金鞭溪风景区
2019/09/12 职场文书
Redis缓存-序列化对象存储乱码问题的解决
2021/06/21 Redis