使用pymysql的fetchall方法时遇到"内存溢出"问题如何解决?

一、问题现象与根源分析

当开发者使用Python的pymysql库执行大数据量查询时,调用fetchall()方法经常遭遇MemoryError异常。典型场景包括:

  • 查询结果包含百万级记录
  • 返回BLOB/TEXT类型大字段
  • 连接未设置游标类型

根本原因在于fetchall()全量加载机制

# 问题代码示例
import pymysql
conn = pymysql.connect(host='localhost', user='root', password='', db='test')
cursor = conn.cursor()
cursor.execute("SELECT * FROM large_table")  # 百万行数据
results = cursor.fetchall()  # 一次性加载所有数据到内存

二、5种解决方案对比

1. 使用SS游标(服务端游标)

通过SSCursor实现流式读取

from pymysql.cursors import SSCursor
cursor = conn.cursor(SSCursor)
cursor.execute("SELECT * FROM large_table")
while True:
    row = cursor.fetchone()  # 每次只加载一行
    if not row: break

2. 分块批处理(Chunking)

结合LIMITOFFSET实现分批加载

chunk_size = 1000
for offset in range(0, total_rows, chunk_size):
    cursor.execute(f"SELECT * FROM large_table LIMIT {offset}, {chunk_size}")
    chunk = cursor.fetchall()

3. 使用DictCursor优化内存

当只需要部分字段时:

from pymysql.cursors import DictCursor
cursor = conn.cursor(DictCursor)
cursor.execute("SELECT id, name FROM large_table")

三、性能基准测试

方法 100万行耗时 内存峰值
fetchall 12s 2.8GB
SSCursor 25s 50MB

四、最佳实践建议

  1. 预估数据量:先执行COUNT(*)查询
  2. 字段精简:避免SELECT *
  3. 连接参数:设置init_command='SET SESSION max_allowed_packet=64MB'

五、进阶方案

对于超大数据集可考虑:

  • 使用LOAD DATA INFILE直接导出
  • 采用Apache Spark等分布式处理框架