【性能优化】Django性能优化笔记

一、背景

在研发过程中,随着数据量暴增,数据查询性能问题变的越来越突出,需要想办法优化查询响应速度。所以本篇文章,就是记录曾经做过的性能优化过程。

二、优化场景记录

场景一:优化交互逻辑,提高用户体验

问题描述:

客户通过多选下拉框的模块,查询主机。但是加载下拉框需要2分钟左右,主机展示table也很卡。原因是客户的模块有几千个,主机数据有几万条。而且这些数据都不是本地数据,都是需要调用开放性接口,分页去查询。

原因分析:
  1. 前端调用一个函数查询模块数据,实际后端这个函数的逻辑是循环分页调用第三方接口拼接数据。每一次调用接口都有一定的响应时间,这样就会导致这个函数响应很慢。影响用户体验。
  2. 主机数据展示卡顿是因为没有分页,因为这个系统在此前都没有遇到过这样庞大的数据量,并没有考虑分页功能。数据量增大时,前端渲染数据过多,就会导致卡顿。
解决方案:

通过分析,这显然可以通过修改交互逻辑,以较小的代价,达到预期的目标。最终我们的解决方案是:

  1. 前端不用下拉框,改用拓扑树。先查询第一层级的业务(数据不多,只有几百),然后通过节点懒加载模块数据。
  2. 主机数据展示,采用分页形式。不能分页的情况,采用预览设计,只预览前20条数据。

场景二:优化查询语句,提高响应速度

问题描述:

某一个巡检项目,因为赶工,匆匆交付,随着项目运行,数据积累越来越多,有些接口的响应越来越慢,甚至有些接口的响应时间从2s变成12s。有的接口从2分钟直接变成30分钟。

原因分析:
  1. 表关联很多,很多查询接口,都是在循环中做filter查询操作。数据少的时候还好,数据一多,接口响应速度就变得很慢。
  2. 一些需要查询四五个表,并且需要实时大量计算的接口,响应速度也很慢。一方面是查询多个数据量比较大的表,需要一定的时间,而且还需要整合计算,更花费时间。
解决方案:

通过分析,我们发现,以上问题主要集中在查询数据库方面和数据计算方面。所以,我们采取了一下措施,将所有接口的响应时间缩短到3s中以内。

  1. 针对在循环中查询数据库,采取的方案是:先将要循环查询的数据,先查询出来,处理成字典结构。在循环的时候,直接匹配字典数据,无需循环查询数据库,提高效率。
  2. 优化orm查询操作。
    • 避免全表扫描。优先使用exists, count等方法。
    • 避免 N + 1 查询。可使用select_related提前将关联表进行 join,一次性获取相关数据,many-to-many 的外键则使用prefetch_related
    • 其他方法
  3. 针对需要实时查询并且实时计算的接口,我们利用redis缓存,提高响应速度。在数据变化时,实时将数据计算好并且更新到缓存中,这样就不用每次查询时,还需要耗费时间重新查询数据并且计算一遍。

场景三:优化数据库设计,提高查询数据库效率

问题描述:

当数据库的一个表数据达到50w+时,即使是最简单的filter查询操作,都变得很慢。

原因分析:

数据库中,一个表的数据量达到一定的量级之后,查询就会变得很慢,特别是还没有加索引的时候。

解决方案:

通过分析,我们可以发现,是由于数据库设计不合理导致数据查询效率变低。经过和同事分析,我们采取了几个优化数据库表设计的方案。

  1. 有些存储分析结果的表,每天都会新增大量的分析结果数据,但是系统频繁查询的数据都只是需要最新的数据,那历史数据其实可以分离出来,放到历史表中。这就是冷热数据分离。
  2. 数据库表要使用正确的字段类型,避免TextField代替CharFieldIntegerField代替BooleanField等。
  3. 数据库表要正确使用索引。如果某个字段或某组字段被频繁用于过滤或排序查询,建议建立单字段索引或联合索引。但单个表的索引最好不要超过5个。

三、总结

  • 很多时候,在数据量少的时候,我们很少会遇到性能问题。只有当我们业务膨胀数据量膨胀的时候,性能问题才会逐渐突显出来。所以性能优化不是一蹴而就的,是伴随着业务发展,不断优化的过程。
  • 但也不是说一开始做数据库设计时,不需要考虑性能问题,而是在遵守设计规范的同时(设计规范是前辈的经验总结,遵循规范,往往已经可以解决很多性能问题),权衡利弊之后,做出产品。没有什么设计一开始就是完美的,都是伴随着业务的发展,逐步优化而成。
  • 不能为了优化而优化,要取舍,要符合业务需要。

你可能感兴趣的:(python+Django,django,性能优化,orm)