You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python从SQLite4000万行表生成嵌套字典后期骤降速原因咨询

为什么处理到3600万条数据时程序突然变慢?

这个场景我太熟悉了——处理超大规模数据集时,前半段跑得飞起,快结束时突然“卡壳”,大概率是内存和数据结构的瓶颈在作祟,咱们来拆解具体原因:

1. 内存耗尽触发虚拟内存,速度直接跳水

你想想,4000万条数据塞进嵌套字典里,每一条都要存两个字符串键+一个值,再加上Python字典本身的哈希表结构开销,到3600万条的时候,这个字典的内存占用绝对是个天文数字——保守估计几个GB起步。

当系统物理内存不够用的时候,就会开始用虚拟内存(硬盘交换空间),硬盘的读写速度比内存慢几百上千倍,程序自然就会突然变慢,看起来像卡住了,但其实还在硬着头皮从硬盘里读写数据。你可以打开任务管理器或者top命令看看内存占用,肯定已经逼近系统内存上限了。

2. 字典哈希冲突加剧,操作耗时指数级上升

Python字典是靠哈希表实现的,当键的数量越来越多,哈希冲突的概率会大幅增加。到3600万条的时候,外层字典的键数量可能已经非常庞大,每次执行if dict_key_1 not in dictionary或者dictionary[dict_key_1].update(...)时,哈希表需要做更多的冲突处理、扩容操作,这些操作的耗时会随着数据量增长呈指数级上升,直接拖慢整个循环。

3. SQLite游标默认行为的隐形开销

你用的是默认的游标迭代方式,SQLite虽然会逐步加载结果,但当数据量极大时,加上内存压力的叠加,数据库的IO效率会急剧下降。另外,你没有设置游标的arraysize,每次从数据库取的数据量太小,频繁的IO交互会把耗时一点点累积起来,到后期彻底爆发。

4. 代码里的小bug雪上加霜

看你的代码,Counter_1计数到100万后,你重置的是counter=0(注意变量名大小写,你定义的是大写的Counter_1,重置的是小写的counter),这会导致之后再也不会打印进度,让你误以为程序完全卡住了,但其实它还在慢腾腾地运行。这个小错误直接让你失去了进度反馈,加剧了“突然变慢”的感知。


给你几个可行的优化方案

  • 别把所有数据塞进内存:如果不是必须要全量嵌套字典,考虑分批次处理,或者用磁盘存储的结构(比如SQLite临时表、或者用pandas分块读取)。
  • 优化字典结构:用collections.defaultdict替代手动判断键是否存在,减少一次in查询的开销:
    from collections import defaultdict
    dictionary = defaultdict(dict)
    # 直接赋值就行,不用再判断键是否存在
    dictionary[dict_key_1][dict_key_2] = value
    
  • 给SQLite游标加arraysize:每次从数据库读取更多数据,减少IO次数:
    cursor = conn.cursor()
    cursor.arraysize = 10000  # 一次读1万条,根据内存情况调整
    cursor.execute(selection_query)
    
  • 修复计数bug:把counter=0改成Counter_1 = 0,这样还能看到进度,知道程序没挂。
  • 换更高效的数据结构:比如用pandas的DataFrame处理,它的内存效率比纯字典高很多,还支持分块读取:
    import pandas as pd
    chunk_size = 100000
    for chunk in pd.read_sql_query(selection_query, conn, chunksize=chunk_size):
        # 处理每个数据块
        pass
    

内容的提问来源于stack exchange,提问作者Öykü Öngün

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:00:34