话说Django orm性能为什么比原生的mysqldb慢

今天同事反映了一个问题,说后台报表的页面很慢…  我当场就质疑他,肿么可能… 结果试了打开页面看了下,确实很慢.   然后就开始慢慢的排查问题….


关于django orm性能一般,返回结果慢的问题,博文地址是 http://xiaorui.cc/?p=2048


首先确认下mysql索引问题.

进入mysql,查看索引情况 , 命中索引.

mysql> explain SELECT sum(idate_count) FROM `buzz_keyword_history` WHERE (`buzz_keyword_history`.`date` < ‘2015-09-20 00:00:00’ AND `buzz_keyword_history`.`date` >= ‘2015-09-01 00:00:00’ AND `buzz_keyword_history`.`value` = ‘手机’);
+—-+————-+———————-+——+——————————–+———-+———+——-+——+————————————+
| id | select_type | table                | type | possible_keys                  | key      | key_len | ref   | rows | Extra                              |
+—-+————-+———————-+——+——————————–+———-+———+——-+——+————————————+
|  1 | SIMPLE      | buzz_keyword_history | ref  | in_value,in_idate,search_speed | in_value | 182     | const | 1514 | Using index condition; Using where |
+—-+————-+———————-+——+——————————–+———-+———+——-+——+————————————+
1 row in set (0.00 sec)

然后在看了下mysql服务器的负载情况,load特别的底下.

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           2.80    0.00    1.22    0.41    0.00   95.57

Device:         rrqm/s   wrqm/s     r/s     w/s   rsec/s   wsec/s avgrq-sz avgqu-sz   await  svctm  %util
sdc               0.00   808.61   21.65   27.02  5531.93  6685.05   251.00     0.55   11.39   0.71   3.45
sdb               0.00   126.81    3.30    4.95   823.13  1054.05   227.75     0.37   44.44   1.50   1.24
sda               0.06   215.50    2.46    7.87   599.12  1786.94   231.10     0.81   78.76   1.37   1.41
memdiska          0.00     0.00  585.63 2638.44 84515.95 21107.53    32.76     0.15    0.05   0.03   8.54
dm-0              0.00     0.00    0.00    0.00     0.00     0.00     8.00     0.00    4.39   1.13   0.00

接着在mysql server开启了慢查询及sql语句调试模式,发现slow.log没有特别的日志…都是跟本业务无关的慢查询.

整个页面的静态文件也是加载正常,速度也是给力,虽然在nginx里没有配置强制缓存… 但为毛数据加载这么慢 ,为毛 ?

最后不得不重新怀疑mysql查询,我们开始统计整个django orm语句消耗的时间,一看非常的惊人,居然消耗了6秒的时间… 原本以为django 的orm只是帮助我们做了sql语句的映射,说实话我以前还真的就这么想.

run_func.objects.filter(date__gte=d[0], date__lt=d[1],value=value).aggregate(Sum(idate_or_cdate + '_count'))

又看了django orm model的介绍,这django模型不简单呀,他的返回值是querysets类型。 也就是说,他会把orm执行的结果,转换成queryset结构 。 就因为这样被封装,所以我们每次用orm感觉特别友好的原因。 我在github和stackoverflow.com看了一些同样的orm性能问题,回复的答案大多数是临时不用orm,在大查询下,尤其是像我这样的报表业务,orm确实太慢了点…


解决的方法,直接走原生的mysql sql语句,在python下你的选择 mysqldb,也可以用django的connection。推荐用connection,因为大家的db地址配置都是放在settings.config里面的。

下面是django 运行原始sql语句的方法,大家参考下..

from django.db import connection, transaction
import MySQLdb
.......

cursor = connection.cursor(cursorclass = MySQLdb.cursors.DictCursor)
cursor.execute(sql,None)

如果你有多个数据库,可以在connections选择数据库。

from django.db import connections
cursor = connections['xiaorui'].cursor()

transaction.commit_unless_managed(using='xiaorui')

说下优化后最终的结果,使用myqldb执行的sql,返回结果用了不到1秒,而如果用django的orm是6秒左右..   快了好几倍… 

我觉得django orm应该多加个是否要转换成queryset的参数,这样我们可以根据需求来进行配置,不然啥都转成queryset,这性能确实不靠谱.  下面是我用django debug的结果.. 可以看到速度提升了不少.


在搜索django orm性能关键词的时候,我也看到一些文章直接指出django不适合做大型的web系统,虽然我不喜欢用django开发项目,但也不能这么闲扯…    我以前用flask、bottle、tornado这三个web框架,各开发了最少两套平台..  对于造轮子,我痛苦并快乐着….  只是想说,工程项目还是用Django吧,到了咱们这岁数,大家都懒得再去造轮子,然后到群里吹嘘了,赶紧完成需求才是正事,别天天扯些没用的.


最后说一句,django orm性能真是渣渣.


大家觉得文章对你有些作用! 如果想赏钱,可以用微信扫描下面的二维码,感谢!
另外再次标注博客原地址  xiaorui.cc

3 Responses

  1. 踯躅的卧槽马 2016年5月10日 / 上午10:02

    django的orm也可以直接用sql查询,就用raw()方法就可以了。

发表评论

邮箱地址不会被公开。 必填项已用*标注