为什么PyMySQL获取一条数据会让内存爆炸

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 为什么PyMySQL获取一条数据会让内存爆炸

摄影:产品经理产品经理做的萝卜牛腩

当Python 有读写 MySQL 数据的需求时,我们经常使用PyMySQL这个第三方库来完成。

有时候如果一张表里面的数据非常大,但是我们只需要读取一条数据,此时我们可能会想当然地使用cursor.fetchone()这个方法,以为这样就真的可以只读取一条数据:

import pymysql
connection = pymysql.connect(host='localhost',
                             user='user',
                             password='passwd',
                             db='db',
                             charset='utf8mb4',
                             cursorclass=pymysql.cursors.DictCursor)
with connection.cursor() as cursor:
    db = 'select * from users where age > 10'
    cursor.execute(db)
    one_user = cursor.fetchone()

但实际上,上面这段代码,与下面这段代码没有任何区别:

...
with connection.cursor() as cursor:
    sql = 'select * from users where age > 10'
    cursor.execute(sql)
    all_users = cursor.fetchall()
    one_user = all_users[0]

这是因为,当我们执行到cursor.execute(sql)的时候,PyMySQL就已经把表里面所有的数据读取到内存中了。而后面的cursor.fetchall()或者cursor.fetchone()只不过是从内存中返回全部数据还是返回1条数据而已。

我们来看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即可解决问题。

我们来看一下如何正确使用它:

import pymysql
connection = pymysql.connect(host='localhost',
                             user='user',
                             password='passwd',
                             db='db',
                             charset='utf8mb4',
                             cursorclass=pymysql.cursors.SSDictCursor)
with connection.cursor() as cursor:
    db = 'select * from users where age > 10'
    cursor.execute(db)
    for row in cursor:
        print('对 cursor 直接进行迭代,每循环一次,从数据库读取一条数据。不会提前把所有数据读取到内存中。')
        print(row['name'])
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
2月前
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
|
3天前
|
缓存 NoSQL 算法
14)Redis 在内存用完时会怎么办?如何处理已过期的数据?
14)Redis 在内存用完时会怎么办?如何处理已过期的数据?
9 0
|
4天前
|
存储 弹性计算 网络协议
阿里云ECS内存型实例规格族特点、适用场景、指标数据参考
阿里云ECS提供了多样化的内存型实例规格族,专为需要高性能内存资源的应用场景设计。从最新的r8a系列到经过优化的re6p系列,旨在提供稳定、高效且安全的计算环境。这些实例不仅具备强大的计算性能与内存配比,还通过支持ESSD云盘和高效网络协议,显著提升了存储I/O能力和网络带宽,适用于大数据分析、高性能数据库、内存密集型应用等多种场景,为用户带来卓越的计算体验。本文将详细解析阿里云ECS中的多个内存型实例规格族,包括它们的核心特点、适用场景、实例规格及具体指标数据,为用户在云计算资源选型时提供参考。
|
2月前
|
编译器 C++
virtual类的使用方法问题之C++类中的非静态数据成员是进行内存对齐的如何解决
virtual类的使用方法问题之C++类中的非静态数据成员是进行内存对齐的如何解决
|
2月前
|
存储 算法 大数据
小米教你:2GB内存搞定20亿数据的高效算法
你好,我是小米。本文介绍如何在2GB内存中找出20亿个整数里出现次数最多的数。通过将数据用哈希函数分至16个小文件,每份独立计数后选出频次最高的数,最终比对得出结果。这种方法有效解决大数据下的内存限制问题,并可应用于更广泛的场景。欢迎关注我的公众号“软件求生”,获取更多技术分享!
150 12
|
2月前
内存的数据
va的ava的va的ava的内存需要划分成为5个部分: 1.栈(Stack)存放的都是方法中的局部变量。方法的运行一定要在栈当中运行。 2.堆(Heap)凡是new出来的东西,都是在堆当中 堆内存的东西都有一个地址值:16进制 堆内存的数据,都有默认值。规则: 整数 默认是0 浮点 默认0.0 字符 默认'\u0000'
32 3
|
3月前
|
缓存 NoSQL Redis
Redis性能优化问题之当Redis内存达到maxmemory后,淘汰数据的逻辑是怎样的
Redis性能优化问题之当Redis内存达到maxmemory后,淘汰数据的逻辑是怎样的
|
2月前
|
C++ 容器
curl使用小记(三)——获取远端数据到内存缓冲区
curl使用小记(三)——获取远端数据到内存缓冲区
43 0
|
2月前
|
存储 编译器 C语言
数据在内存中的存储
数据在内存中的存储
|
3月前
|
存储 安全 Java
Java面试题:如何在Java应用中实现有效的内存优化?在多线程环境下,如何确保数据的线程安全?如何设计并实现一个基于ExecutorService的任务处理流程?
Java面试题:如何在Java应用中实现有效的内存优化?在多线程环境下,如何确保数据的线程安全?如何设计并实现一个基于ExecutorService的任务处理流程?
39 0