当前位置: 首页 > 科技观察

为什么PyMySQL拿到一条数据内存就爆炸了

时间:2023-03-12 05:49:05 科技观察

在Python需要读写MySQL数据的时候,我们经常会使用PyMySQL这个第三方库来做。有时候如果一个表的数据非常大,而我们只需要读取一条数据,我们可能会想当然地使用cursor.fetchone()方法,认为我们真的可以只读取一条数据:importpymysqlconnection=pymysql.connect(host='localhost',user='user',password='passwd',db='db',charset='utf8mb4',cursorclass=pymysql.cursors.DictCursor)withconnection.cursor()作为cursor:db='select*fromuserswhereage>10'cursor.execute(db)one_user=cursor.fetchone()但其实上面的代码和下面的代码没什么区别:...withconnection.cursor()ascursor:sql='select*fromuserswhereage>10'cursor.execute(sql)all_users=cursor.fetchall()one_user=all_users[0]这是因为,当我们执行cursor.execute(sql)时,PyMySQL已经把表中所有的数据内部被读入内存。而后面的cursor.fetchall()或cursor.fetchone()只是从内存中返回所有数据或者返回一条数据。让我们看一下PyMySQL[1]的源代码。cursor.execute()方法中的代码如下图所示:第163行调用了self._query方法。让我们再来看看这个方法:看代码的第322行,调用了self._do_get_result()方法。我们再去这个方法看看:注意代码的第342行,此时self._rows列表中已经存入了所有的数据。现在我们来看cursor.fetchone()方法:正如你所见,这只是根据下标从列表中读取一条数据。再看cursor.fetchall()方法:如果之前调用了多次cursor.fetchone(),self.rownumber会不断增加。调用cursor.fetchall()时,跳过之前已经返回的数据,直接返回所有剩余的数据。如果之前没有调用过cursor.fetchone(),则直接返回所有数据。因此,简单地使用cursor.fetchone()并不能节省内存。如果表中的数据非常大,仍然存在内存爆炸的风险。那么真正的解决办法是什么?真正的解决方案是在创建数据库连接时指定游标类型。pymysql.connect有一个参数叫做cursorclass,将它的值设置为pymysql.SSDictCursor就可以解决问题。让我们看看如何正确使用它:importpymysqlconnection=pymysql.connect(host='localhost',user='user',password='passwd',db='db',charset='utf8mb4',cursorclass=pymysql.cursors。SSDictCursor)withconnection.cursor()ascursor:db='select*fromuserswhereage>10'cursor.execute(db)forrowincursor:print('直接迭代游标,每个循环从数据库中读取一次数据,会不先进将所有数据读入内存。')print(row['name'])