Skip to content

Commit 7d24ce4

Browse files
committed
python mysql oom
1 parent 7b5aaa1 commit 7d24ce4

2 files changed

Lines changed: 113 additions & 1 deletion

File tree

_posts/2016-11-16-speed-up-django-admin-changelist-by-cache.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ tags:
1111
- Django
1212
---
1313

14-
由于之前从来没有遇到过累死的业务需求,所以一直没觉得Django自带的后台管理系统admin有什么问题,
14+
由于之前从来没有遇到过类似的业务需求,所以一直没觉得Django自带的后台管理系统admin有什么问题,
1515
最近在做一个中小学题目管理系统,上来就是百万级别的题目,结果在admin中,打开题目的列表查看第一页
1616
的十来个数据居然需要十几秒,简直是慢的令人发指,用[django-debug-toolbar]{:target="_blank"}
1717
查看了一下,居然是因为求总页数时的`select count(*) from soma_table;`导致的,
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
---
2+
author: shellbye
3+
comments: true
4+
date: 2016-12-16 00:00:08+00:00
5+
layout: post
6+
slug: python_mysql_out_f_memory_killed
7+
title: Python been kill for out of memory when using mysql
8+
categories:
9+
- tech_world
10+
tags:
11+
- python
12+
- mysql
13+
---
14+
15+
# 场景描述
16+
在一个很大的mysql数据表中,将所有的数据全部读出来,
17+
然后一条一条处理,将处理后的数据再一条一条的写入另一个表中,
18+
大概的代码逻辑如下所示:
19+
20+
{% highlight python %}
21+
import MySQLdb
22+
23+
24+
conn = MySQLdb.connect(host=DB_HOST, user=DB_USER, db=DB_NAME,
25+
passwd=DB_PASSWORD, charset="utf8")
26+
cursor = conn.cursor()
27+
cursor.execute("select * from very_big_table;")
28+
rows = cursor.fetchall()
29+
for row in rows:
30+
# do something about the row
31+
cursor.execute("insert into another_table");
32+
conn.commit()
33+
{% endhighlight %}
34+
35+
# 问题出现
36+
整体的逻辑是比较简单的,mysql中的数据量也不是很大,不到百万而已,
37+
但是对每一条数据做处理时,因为要进行一些图像操作,所以可以内存消耗比较大吧,
38+
总之计划是四五天执行完毕的,结果第三天就被操作系统`Killed`了,
39+
在查看系统日志(`/var/log/syslog`)时,发现以下段落,看样子是内存耗尽了:
40+
41+
{% highlight bash %}
42+
ubuntu kernel: [12600693.883297] mysqld invoked oom-killer: gfp_mask=0x24201ca, order=0, oom_score_adj=0
43+
...
44+
...
45+
ubuntu kernel: [12600693.883935] Out of memory: Kill process 89623 (python) score 930 or sacrifice child
46+
{% endhighlight %}
47+
48+
重启了一下程序,发现起初内存占用并不多,但是会随着时间的推移慢慢增长,
49+
看样子,是有内存使用不当的问题了。
50+
51+
# 解决过程
52+
经过一阵Google,发现了这个[SO]和这个[SO1],知道了原来还有`SSCursor`这种神器,
53+
于是火速将代码重构成了这样:
54+
55+
{% highlight python %}
56+
import MySQLdb
57+
import MySQLdb.cursors as cursors
58+
59+
60+
conn = MySQLdb.connect(host=DB_HOST, user=DB_USER, db=DB_NAME,
61+
passwd=DB_PASSWORD, charset="utf8",
62+
cursorclass=cursors.SSCursor)
63+
cursor = conn.cursor()
64+
cursor.execute("select * from very_big_table;")
65+
for row in cursor:
66+
# do something about the row
67+
cursor.execute("insert into another_table");
68+
conn.commit()
69+
{% endhighlight %}
70+
71+
然后执行,Bang!!!,出错了,
72+
73+
{% highlight bash %}
74+
`_mysql_exceptions.ProgrammingError: (2014, "Commands out of sync; you can't run this command now")`
75+
{% endhighlight %}
76+
77+
读文档光是仔细是不行了,要用心,[官方文档]明确的写了,
78+
79+
> You MUST retrieve the entire result set and close() the cursor before additional queries can be peformed on the connection.
80+
81+
原来是这个connection被神器占用了,那么好,单独再开一个connection,
82+
于是代码变成了最终版如下:
83+
84+
{% highlight python %}
85+
import MySQLdb
86+
import MySQLdb.cursors as cursors
87+
88+
89+
conn = MySQLdb.connect(host=DB_HOST, user=DB_USER, db=DB_NAME,
90+
passwd=DB_PASSWORD, charset="utf8",
91+
cursorclass=cursors.SSCursor)
92+
write_conn = MySQLdb.connect(host=DB_HOST, user=DB_USER, db=DB_NAME,
93+
passwd=DB_PASSWORD, charset="utf8")
94+
cursor = conn.cursor()
95+
write_cursor = write_conn.cursor()
96+
cursor.execute("select * from very_big_table;")
97+
for row in cursor:
98+
# do something about the row
99+
write_cursor.execute("insert into another_table");
100+
write_conn.commit()
101+
{% endhighlight %}
102+
103+
世界从此不会内存溢出,赞!
104+
105+
这里有我在我最爱的[stackoverflow.com]上的回答,
106+
可以点击查看哦^_^._
107+
108+
109+
[SO]:http://stackoverflow.com/questions/18193825/python-how-to-use-a-generator-to-avoid-sql-memory-issue
110+
[SO1]:http://stackoverflow.com/questions/1808150/how-to-efficiently-use-mysqldb-sscursor
111+
[官方文档]:http://mysql-python.sourceforge.net/MySQLdb-1.2.2/public/MySQLdb.cursors.CursorUseResultMixIn-class.html
112+
[stackoverflow.com]:http://stackoverflow.com/questions/1811173/why-does-my-python-script-randomly-get-killed/#answer-41176365

0 commit comments

Comments
 (0)