用pandas.read_csv读大文件必炸内存,应加chunksize参数;计数用原生Python按行读;切分CSV需用csvkit或mlr避免引号内换行错误;导入数据库要显式控制事务、防主键冲突;dtype和converters须提前指定以保类型一致。
用 pandas.read_csv 直接读大文件必炸内存
它默认把整个 csv 一次性加载进内存,哪怕只是想统计行数或抽样,也会触发 oom。尤其当文件 >2gb 或列数极多(比如日志宽表),read_csv 的解析开销远超数据本身。
实操建议:
- 永远加
chunksize参数,让它返回TextFileReader迭代器,而不是DataFrame - 避免写
df = pd.read_csv("big.csv")—— 这是绝大多数内存崩掉的起点 - 如果只是计数,用原生 Python 按行读:
sum(1 for line in open("big.csv")),比read_csv快 5–10 倍且内存恒定在几 MB
分片切割不能只靠 split -l 粗暴切行
CSV 里换行符可能出现在引号内(如字段含回车),split -l 会把单行数据硬切成两段,后续导入时直接报 ParserError: Expected x fields in line y。
实操建议:
- 用
csvkit的csvcut或mlr(Miller)这类懂 CSV 语法的工具切分:mlr --csv --implicit-csv-header cat then head -n 1000000 input.csv > part1.csv - 若必须用系统命令,先用
awk跳过引号内换行:awk -v RS='"[^"]*"' '{gsub(/ /, "\n", RT); printf "%s%s", $0, RT}' input.csv | split -l 1000000(仅限简单引号规则) - 切完务必校验首尾行是否完整:用
head -n1 part1.csv和tail -n1 part1.csv看字段数是否一致
增量导入到数据库要防主键冲突和事务膨胀
逐块 to_sql(..., if_exists="append") 看似稳妥,但默认每 chunk 开一个事务,MySQL/PostgreSQL 在高并发写入时容易锁表;SQLite 更是直接卡死。另外,没去重就 append,重复数据会越积越多。
使用 qbo-mileage CLI 及用户凭证,从 Airtable、Outlook 或 Google Calendar 记录生成 QuickBooks Online 里程 CSV 文件。
实操建议:
- 显式控制事务:用
engine.begin()包住整块插入,而非依赖 pandas 自动 commit - 导入前加临时唯一索引或用
ON CONFLICT DO NOTHING(PostgreSQL) /INSERT IGNORE(MySQL)跳过重复 - 别用
if_exists="replace"—— 它会删表重建,元数据和索引全丢,不是“覆盖”而是“重来” - 对 SQLite,把
chunksize设为 5000–10000,太大反而因 WAL 日志暴涨拖慢
dtype 和 converters 不设好,分片后类型不一致
不同 chunk 里某列可能前 10 万行全是数字,后 10 万行突然出现空字符串或 "N/A",read_csv 自动推断类型就会分裂:一部分是 int64,另一部分变成 object,后续 concat 或入库时报 ValueError: invalid literal for int()。
实操建议:
- 强制指定
dtype:比如dtype={"user_id": "Int64", "amount": "float32"}(注意用可空的Int64而非int64) - 对易脏字段,用
converters统一清洗:converters={"status": lambda x: x.strip().upper() if isinstance(x, str) else x} - 首次读一小块(
nrows=10000)跑df.dtypes和df.applymap(type).nunique(),提前发现类型混杂点
最麻烦的其实是字段里藏着不可见字符(u200b、ufeff),分片后有的 chunk 带 BOM 有的不带,解析结果错位——这种得在切割前统一用 iconv -f utf-8 -t utf-8//IGNORE 过一遍。










