处理100GB级别的CSV文件去重时,传统内存加载方式会直接触发内存溢出,无法完成任务。基于单列哈希值的去重方案是应对超大文件的常用思路,核心是通过哈希函数将目标列的值转换为唯一标识,仅保留哈希值完成重复判断,大幅降低内存占用。

核心思路说明
针对超大CSV文件的去重,核心逻辑分为三步:首先分块读取CSV文件,避免一次性加载全部内容到内存;然后对每一行的目标列计算哈希值,将哈希值存入集合;最后判断新行的哈希值是否已存在于集合中,不存在则保留该行并写入结果文件,存在则直接跳过。这种方式只需要存储哈希值,不需要存储整行数据,内存占用极低。
实现步骤
1. 确定目标去重列
首先需要明确CSV中用于去重的单列,比如用户ID列、订单编号列等,该列的值需要能够唯一标识一条记录。如果目标列的值本身存在重复,那么对应行就会被判定为重复行。
2. 分块读取CSV文件
使用Python的pandas库或者csv模块分块读取文件,每次只加载一部分数据到内存,处理完成后再释放,避免内存溢出。这里推荐使用csv模块,处理超大文件时性能更稳定。
3. 计算哈希值并判断重复
对每一行目标列的值计算哈希值,这里使用Python内置的hash函数即可,也可以根据需求选择md5等哈希算法。将计算得到的哈希值存入一个集合,集合的查找时间复杂度为O(1),判断重复的效率很高。
4. 写入去重后的结果
当判定某行不是重复行时,将该行写入新的CSV结果文件,重复行直接跳过,直到所有分块处理完成。
完整代码示例
以下是基于Python csv模块实现的去重代码,目标去重列为CSV的第一列,可根据实际需求调整列索引:
import csv
def deduplicate_csv_by_hash(input_path, output_path, target_col_index=0):
# 存储已出现的哈希值
seen_hash = set()
# 打开输入文件和输出文件
with open(input_path, 'r', encoding='utf-8') as infile, open(output_path, 'w', encoding='utf-8', newline='') as outfile:
reader = csv.reader(infile)
writer = csv.writer(outfile)
# 先写入表头,如果原文件有表头的话
header = next(reader, None)
if header:
writer.writerow(header)
# 逐行处理数据
for row in reader:
if not row:
continue
# 获取目标列的值
target_value = row[target_col_index]
# 计算哈希值
value_hash = hash(target_value)
# 判断哈希值是否已存在
if value_hash not in seen_hash:
seen_hash.add(value_hash)
writer.writerow(row)
if __name__ == '__main__':
# 输入文件路径,替换为实际的100GB CSV文件路径
input_file = 'large_data.csv'
# 输出去重后的文件路径
output_file = 'deduplicated_data.csv'
deduplicate_csv_by_hash(input_file, output_file, target_col_index=0)
print('去重完成,结果已保存到输出文件')
注意事项
- 哈希函数可能存在哈希碰撞的概率,如果对去重准确性要求极高,可以在哈希值判断的基础上,再额外对比原始值,不过会增加少量内存占用。
- 如果CSV文件的分隔符不是逗号,需要在csv.reader中指定delimiter参数,比如分隔符为制表符时设置delimiter='t'。
- 处理100GB文件时,建议将结果文件输出到和源文件不同的磁盘分区,避免磁盘IO瓶颈影响处理速度。
- 如果目标列存在空值,需要根据业务需求决定空值是否参与去重,可在代码中增加空值判断逻辑。
性能优化建议
如果处理速度仍然达不到预期,可以开启多进程处理,将文件按行数拆分为多个小分块,每个进程处理一个分块,最后再合并结果,不过需要注意哈希值集合的线程安全问题,可以使用进程间共享的数据结构或者先分进程处理再合并去重。