无需生成CSV文件 如何将SQL大表数据直接读取到pandas中
问题结论
650万行7列的表完全可以一次性读取到pandas中,你的服务器64GB内存远高于该表的实际内存占用需求,内核崩溃是不合理的代码写法和默认参数导致的内存峰值溢出,调整参数即可实现需求。
问题诱因分析
- 冗余执行了两次全表查询:你预先执行了
result = engine.execute(query),已经将全表结果集缓存在了连接内存中,后续read_sql_query又重复执行了一次全表查询,相当于同时在内存中加载了两份数据,直接推高了内存峰值。 - 默认参数导致内存浪费:pandas读取Oracle数据时默认会用高内存占用的数据类型存储(比如默认用int64/float64存储数值、用object类型存储字符串),同时默认的fetch数组大小较小,读取过程中反复申请内存会产生额外的内存开销。
- 多余的连接池配置:你设置了
pool_size=50,单查询场景下不需要这么大的连接池,多余的连接会占用不必要的内存空间。
优化代码方案
首先确认你使用的是64位版本的Python和pandas,32位Python有最大2GB的内存寻址限制,无法满足需求。
优化后的代码如下:
import pandas as pd from sqlalchemy import create_engine cstr = 'oracle://{user}:{password}@{sid}'.format( user=user, password=password, sid=sid ) # 调整连接参数,缩小连接池,设置全局fetch数组大小 engine = create_engine( cstr, convert_unicode=False, pool_recycle=10, pool_size=1, # 单查询场景只需要1个连接即可 echo=True, arraysize=10000 # 调整单次fetch的行数,减少内存申请次数 ) query = 'Select * From Table' # 读取时指定dtype减少内存占用,可根据你的表实际字段类型调整 # 示例:如果有3个数值列、4个字符串列,可按如下方式指定 df = pd.read_sql_query( query, engine, dtype={ "数值列1": "int32", # 如果数值范围小于2^31,用int32比int64省一半内存 "数值列2": "float32", # 精度要求不高的浮点数列用float32 "字符串列1": "string", # 用pandas StringDtype比object省内存 "字符串列2": "string", # 剩余字段同理配置 } )
可选验证操作
你可以先在Oracle中执行如下SQL,确认表的实际磁盘占用大小,正常情况下650万行7列的表磁盘占用不会超过10GB,加载到内存后也不会超过20GB,完全适配64GB内存:
SELECT SUM(BYTES)/1024/1024 AS TABLE_SIZE_MB FROM USER_SEGMENTS WHERE SEGMENT_NAME = '你的表名(注意大写)';
内容的提问来源于stack exchange,提问作者YTE 1008
相关产品推荐
相关产品推荐

